lib/kcar/response.rb in kcar-0.1.2 vs lib/kcar/response.rb in kcar-0.2.0
- old
+ new
@@ -1,26 +1,27 @@
# -*- encoding: binary -*-
-module Kcar
# This may be used to generate a Rack response
#
-class Response < Struct.new(:sock, :hdr, :unchunk, :buf, :parser)
+class Kcar::Response
+ attr_accessor :sock, :hdr, :unchunk, :buf, :parser
# :stopdoc:
LAST_CHUNK = "0\r\n"
CRLF = "\r\n"
+ Parser = Kcar::Parser
# :startdoc:
# By default we readpartial at most 16K off a socket at once
READ_SIZE = 0x4000
# initializes a socket, +sock+ must respond to the "readpartial"
# method. +unchunk+ may be set to disable transparent unchunking
# +hdr+ may be a Hash, Array, or Rack::Utils::HeaderHash
def initialize(sock, hdr = {}, unchunk = true)
- super(sock, hdr, unchunk, "", Parser.new)
+ @sock, @hdr, @unchunk, @buf, @parser = sock, hdr, unchunk, "", Parser.new
end
# returns a 3-element array that resembles a Rack response, but is
# more useful for additional processing by other code.
#
@@ -31,13 +32,13 @@
# this method will not return until the response headers are fully parsed,
# but the body returned will be this Kcar::Response handler itself.
# +unchunk+ must be true to guarantee trailers will be stored in the
# returned +header+ object
def read
- buf << sock.readpartial(READ_SIZE) if buf.empty?
- while (response = parser.headers(hdr, buf)).nil?
- buf << sock.readpartial(READ_SIZE)
+ @buf << @sock.readpartial(READ_SIZE) if @buf.empty?
+ until response = @parser.headers(@hdr, @buf)
+ @buf << @sock.readpartial(READ_SIZE)
end
response << self
end
# returns a 3-element array suitable for use as a Rack response:
@@ -45,129 +46,129 @@
#
# this method will not return until the response headers are fully parsed,
# but the body returned will be this Kcar::Response handler itself.
# It is not guaranteed that trailers will be stored in the returned +header+
def rack
- self.unchunk = false
+ @unchunk = false
read
end
# this is expected to be called by our Rack server, it will close
# our given +sock+ object if keepalive is not used otherwise it
# will just reset the parser and clear the header object
def close
- parser.keepalive? ? reset : sock.close
+ @parser.keepalive? ? reset : @sock.close
end
# this method allows our Kcar::Response object to be used as a Rack response
# body. It may only be called once (usually by a Rack server) as it streams
# the response body off the our socket object.
- def each(&block)
- if parser.body_eof?
+ def each
+ if @parser.body_eof?
return
end
- if unchunk
- parser.chunked? ? each_unchunk(&block) : each_identity(&block)
+ if @unchunk
+ @parser.chunked? ? each_unchunk { |x| yield x } :
+ each_identity { |x| yield x }
else
- if parser.keepalive?
- parser.chunked? ? each_rechunk(&block) : each_identity(&block)
+ if @parser.keepalive?
+ @parser.chunked? ? each_rechunk { |x| yield x } :
+ each_identity { |x| yield x }
else
- each_until_eof(&block) # fastest path
+ each_until_eof { |x| yield x } # fastest path
end
end
rescue EOFError
end
# :stopdoc:
def reset
- parser.reset
- hdr.clear
+ @parser.reset
+ @hdr.clear
end
- def each_rechunk(&block)
+ def each_rechunk
# We have to filter_body to keep track of parser state
# (which sucks). Also, as a benefit to clients we'll rechunk
# to increase the likelyhood of network transfers being on
# chunk boundaries so we're less likely to trigger bugs in
# other people's code :)
dst = ""
begin
- parser.filter_body(dst, buf) and break
+ @parser.filter_body(dst, @buf) and break
size = dst.size
if size > 0
yield("#{size.to_s(16)}\r\n")
yield(dst << CRLF)
end
- break if parser.body_eof?
- end while buf << sock.readpartial(READ_SIZE, dst)
+ break if @parser.body_eof?
+ end while @buf << @sock.readpartial(READ_SIZE, dst)
yield LAST_CHUNK
- while parser.trailers(hdr, buf).nil?
- buf << sock.readpartial(READ_SIZE, dst)
+ until @parser.trailers(@hdr, @buf)
+ @buf << @sock.readpartial(READ_SIZE, dst)
end
# since Rack does not provide a way to explicitly send trailers
# in the response, we'll just yield a stringified version to our
# server and pretend it's part of the body.
- trailers = parser.extract_trailers(hdr)
+ trailers = @parser.extract_trailers(@hdr)
yield(trailers.map! { |k,v| "#{k}: #{v}\r\n" }.join("") << CRLF)
end
- def each_until_eof(&block)
- yield buf unless buf.empty?
+ def each_until_eof
+ yield @buf unless @buf.empty?
# easy, just read and write everything until EOFError
- dst = sock.readpartial(READ_SIZE)
+ dst = @sock.readpartial(READ_SIZE)
begin
yield dst
- end while sock.readpartial(READ_SIZE, dst)
+ end while @sock.readpartial(READ_SIZE, dst)
end
- def each_identity(&block)
- len = parser.body_bytes_left
- if len.nil?
- each_until_eof(&block)
+ def each_identity
+ len = @parser.body_bytes_left
+ if len == nil
+ each_until_eof { |x| yield x }
else
- dst = buf
+ dst = @buf
if dst.size > 0
# in case of keepalive we need to read the second response,
# so modify buf so that the second response is at the front
# of the buffer
if dst.size >= len
tmp = dst[len, dst.size]
dst = dst[0, len]
- buf.replace(tmp)
+ @buf.replace(tmp)
end
len -= dst.size
yield dst
end
if len > 0
begin
- len -= sock.readpartial(len > READ_SIZE ? READ_SIZE : len, dst).size
+ len -= @sock.readpartial(len > READ_SIZE ? READ_SIZE : len, dst).size
yield dst
end while len > 0
- dst.respond_to?(:clear) ? dst.clear : self.buf = ''
+ dst.respond_to?(:clear) ? dst.clear : @buf = ""
end
end
end
- def each_unchunk(&block)
+ def each_unchunk
dst = ""
begin
- parser.filter_body(dst, buf) and break
+ @parser.filter_body(dst, @buf) and break
yield dst if dst.size > 0
- parser.body_eof? and break
- end while buf << sock.readpartial(READ_SIZE, dst)
+ @parser.body_eof? and break
+ end while @buf << @sock.readpartial(READ_SIZE, dst)
# we can't pass trailers to the client since we unchunk
# the response, so just read them off the socket and
# stash them in hdr just in case...
- while parser.headers(hdr, buf).nil?
- buf << sock.readpartial(READ_SIZE, dst)
+ until @parser.headers(@hdr, @buf)
+ @buf << @sock.readpartial(READ_SIZE, dst)
end
end
# :startdoc:
-
-end # class Response
-end # module Kcar
+end