William James
12/28/2006 8:10:00 PM
Rob Muhlestein wrote:
> Looking for a combination of readpartial() and readline() in order to
> safely read a line of maxlen. IO.readline() appears to suffer from lacking
> this maximum allowing DOS against servers reading HTTP headers, for
> example, just by using sock.readline() alone, which is what
> Net::HTTPResponse and others do:
>
> 1974 class << HTTPResponse
> 1975 def read_new(sock) #:nodoc: internal use only 1976 httpv,
> code, msg = read_status_line(sock) 1977 res =
> response_class(code).new(httpv, code, msg) 1978
> each_response_header(sock) do |k,v| 1979 res.add_field k, v
> 1980 end
> 1981 res
> 1982 end
> 1983
> 1984 private
> 1985
> 1986 def read_status_line(sock) 1987 str = sock.readline
> 1988 m =
> /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/in.match(str) o r
> 1989 raise HTTPBadResponse, "wrong status line: #{str.dump}"
> 1990 m.captures
> 1991 end
>
> I know this is fundamentally a problem with the popular readline C libs
> out there. Wish they had an nreadline like the sprintf and snprintf
> additional function. Sure I could readchar() or readbytes() watching each
> read for a newline, but that is just unfun. Have I overlooked something
> obvious in my search? Hoping to not have to write my own safe/buffered IO
> layer like I've had to do with other langs.
>
> If there is enough interest, maybe I'll hack a readmaxline() method into
> an IO patch to submit. Actually, on second thought, how about adding a
> second parameter:
>
> ios.readline(sep_string=$/,maxlen=nil)
>
> The tough question would then be whether to raise an LineTooLong exception
> or just return what could be read of the line up to that point.
>
> Thanks,
>
> Rob
class File
def safe_readline(sep_string=$/,maxlen=nil)
buf_size = 1024
line = ""
while !self.eof?
s = read( [ buf_size, maxlen - line.size ].min )
line << s
if i = line.index( sep_string )
line = line[0,i+sep_string.size]
return [ line, true ]
end
return [ line[0,maxlen], false ] if maxlen &&
line.size >= maxlen
end
[ line, true ]
end
end
open('junk'){|h| p h.safe_readline("\n",9) }