From d88a3f93dce4b75068c3137d8dd92009195d0576 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 6 Feb 2011 10:25:22 +0000 Subject: kcar/response: micro-optimizations This minimizes allocations made at runtime and uses ivars for faster access under 1.9 --- lib/kcar/response.rb | 87 ++++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/lib/kcar/response.rb b/lib/kcar/response.rb index 67e0286..4764d8b 100644 --- a/lib/kcar/response.rb +++ b/lib/kcar/response.rb @@ -1,14 +1,15 @@ # -*- 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 @@ -18,7 +19,7 @@ class Response < Struct.new(:sock, :hdr, :unchunk, :buf, :parser) # 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 @@ -33,9 +34,9 @@ class Response < Struct.new(:sock, :hdr, :unchunk, :buf, :parser) # +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 @@ -47,7 +48,7 @@ class Response < Struct.new(:sock, :hdr, :unchunk, :buf, :parser) # 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 @@ -55,23 +56,25 @@ class Response < Struct.new(:sock, :hdr, :unchunk, :buf, :parser) # 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 @@ -79,11 +82,11 @@ class Response < Struct.new(:sock, :hdr, :unchunk, :buf, :parser) # :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 @@ -91,30 +94,30 @@ class Response < Struct.new(:sock, :hdr, :unchunk, :buf, :parser) # 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) begin @@ -122,12 +125,12 @@ class Response < Struct.new(:sock, :hdr, :unchunk, :buf, :parser) 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 @@ -135,7 +138,7 @@ class Response < Struct.new(:sock, :hdr, :unchunk, :buf, :parser) if dst.size >= len tmp = dst[len, dst.size] dst = dst[0, len] - buf.replace(tmp) + @buf.replace(tmp) end len -= dst.size @@ -144,30 +147,28 @@ class Response < Struct.new(:sock, :hdr, :unchunk, :buf, :parser) 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 -- cgit v1.2.3-24-ge0c7