Class: Kcar::Response
- Inherits:
-
Object
- Object
- Kcar::Response
- Defined in:
- lib/kcar/response.rb
Overview
This may be used to generate a Rack response
Constant Summary collapse
- LAST_CHUNK =
:stopdoc:
"0\r\n"
- CRLF =
"\r\n"
- Parser =
Kcar::Parser
- READ_SIZE =
By default we readpartial at most 16K off a socket at once
0x4000
Instance Attribute Summary collapse
-
#buf ⇒ Object
Returns the value of attribute buf.
-
#hdr ⇒ Object
Returns the value of attribute hdr.
-
#parser ⇒ Object
Returns the value of attribute parser.
-
#sock ⇒ Object
Returns the value of attribute sock.
-
#unchunk ⇒ Object
Returns the value of attribute unchunk.
Instance Method Summary collapse
-
#close ⇒ Object
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. -
#each ⇒ Object
this method allows our Kcar::Response object to be used as a Rack response body.
- #each_identity ⇒ Object
- #each_rechunk {|LAST_CHUNK| ... } ⇒ Object
- #each_unchunk ⇒ Object
- #each_until_eof {|@buf| ... } ⇒ Object
-
#initialize(sock, hdr = {}, unchunk = true) ⇒ Response
constructor
initializes a socket,
sock
must respond to the “readpartial” method. -
#rack ⇒ Object
returns a 3-element array suitable for use as a Rack response: [ status, headers, body ].
-
#read ⇒ Object
returns a 3-element array that resembles a Rack response, but is more useful for additional processing by other code.
-
#reset ⇒ Object
:stopdoc:.
Constructor Details
#initialize(sock, hdr = {}, unchunk = true) ⇒ Response
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
21 22 23 |
# File 'lib/kcar/response.rb', line 21 def initialize(sock, hdr = {}, unchunk = true) @sock, @hdr, @unchunk, @buf, @parser = sock, hdr, unchunk, "", Parser.new end |
Instance Attribute Details
#buf ⇒ Object
Returns the value of attribute buf.
7 8 9 |
# File 'lib/kcar/response.rb', line 7 def buf @buf end |
#hdr ⇒ Object
Returns the value of attribute hdr.
7 8 9 |
# File 'lib/kcar/response.rb', line 7 def hdr @hdr end |
#parser ⇒ Object
Returns the value of attribute parser.
7 8 9 |
# File 'lib/kcar/response.rb', line 7 def parser @parser end |
#sock ⇒ Object
Returns the value of attribute sock.
7 8 9 |
# File 'lib/kcar/response.rb', line 7 def sock @sock end |
#unchunk ⇒ Object
Returns the value of attribute unchunk.
7 8 9 |
# File 'lib/kcar/response.rb', line 7 def unchunk @unchunk end |
Instance Method Details
#close ⇒ Object
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
58 59 60 |
# File 'lib/kcar/response.rb', line 58 def close @parser.keepalive? ? reset : @sock.close end |
#each ⇒ Object
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.
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/kcar/response.rb', line 65 def each if @parser.body_eof? return end if @unchunk @parser.chunked? ? each_unchunk { |x| yield x } : each_identity { |x| yield x } else if @parser.keepalive? @parser.chunked? ? each_rechunk { |x| yield x } : each_identity { |x| yield x } else each_until_eof { |x| yield x } # fastest path end end rescue EOFError end |
#each_identity ⇒ Object
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/kcar/response.rb', line 128 def each_identity len = @parser.body_bytes_left if len == nil each_until_eof { |x| yield x } else 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) end len -= dst.size yield dst end if len > 0 begin len -= @sock.readpartial(len > READ_SIZE ? READ_SIZE : len, dst).size yield dst end while len > 0 dst.respond_to?(:clear) ? dst.clear : @buf = "" end end end |
#each_rechunk {|LAST_CHUNK| ... } ⇒ Object
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/kcar/response.rb', line 89 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 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) yield LAST_CHUNK 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) yield(trailers.map! { |k,v| "#{k}: #{v}\r\n" }.join << CRLF) end |
#each_unchunk ⇒ Object
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/kcar/response.rb', line 158 def each_unchunk dst = "" begin @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) # 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... until @parser.headers(@hdr, @buf) @buf << @sock.readpartial(READ_SIZE, dst) end end |
#each_until_eof {|@buf| ... } ⇒ Object
119 120 121 122 123 124 125 126 |
# File 'lib/kcar/response.rb', line 119 def each_until_eof yield @buf unless @buf.empty? # easy, just read and write everything until EOFError dst = @sock.readpartial(READ_SIZE) begin yield dst end while @sock.readpartial(READ_SIZE, dst) end |
#rack ⇒ Object
returns a 3-element array suitable for use as a Rack response:
[ status, headers, body ]
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
50 51 52 53 |
# File 'lib/kcar/response.rb', line 50 def rack @unchunk = false read end |
#read ⇒ Object
returns a 3-element array that resembles a Rack response, but is more useful for additional processing by other code.
[ status, headers, body ]
Use Kcar::Response#rack if you want to guaranteee a proper Rack response.
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
36 37 38 39 40 41 42 |
# File 'lib/kcar/response.rb', line 36 def read @buf << @sock.readpartial(READ_SIZE) if @buf.empty? until response = @parser.headers(@hdr, @buf) @buf << @sock.readpartial(READ_SIZE) end response << self end |
#reset ⇒ Object
:stopdoc:
84 85 86 87 |
# File 'lib/kcar/response.rb', line 84 def reset @parser.reset @hdr.clear end |