From 7fe9d585056ea5002cd789d70c3ea4bd5000759c Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 29 Nov 2016 03:30:02 +0000 Subject: http_response: support rack.hijack on HTTP/0.9 responses We still need to iterate through all response headers to support response-only Rack hijacking. Previously, we only supported full hijacking on so-called "HTTP/0.9" clients. n.b. This diff will be easier to read with the -b/--ignore-space-change option of git-diff(1) or GNU diff(1) --- lib/yahns/http_response.rb | 114 ++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 58 deletions(-) (limited to 'lib') diff --git a/lib/yahns/http_response.rb b/lib/yahns/http_response.rb index e2fc940..a31ab70 100644 --- a/lib/yahns/http_response.rb +++ b/lib/yahns/http_response.rb @@ -131,68 +131,66 @@ module Yahns::HttpResponse # :nodoc: term = false hdr_only, chunk_ok = opt - if @hs.headers? - code = status.to_i - hdr_only ||= Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(code) - msg = Rack::Utils::HTTP_STATUS_CODES[code] - buf = "#{response_start}#{msg ? %Q(#{code} #{msg}) : status}\r\n" \ - "Date: #{httpdate}\r\n".dup - headers.each do |key, value| - case key - when %r{\ADate\z}i - next - when %r{\AContent-Range\z}i - if %r{\Abytes (\d+)-(\d+)/\d+\z} =~ value - offset = $1.to_i - count = $2.to_i - offset + 1 - end - kv_str(buf, key, value) - when %r{\AConnection\z}i - # allow Rack apps to tell us they want to drop the client - alive = false if value =~ /\bclose\b/i - when %r{\AContent-Length\z}i - term = true - clen = value.to_i - flags |= MSG_MORE if clen > 0 && !hdr_only - kv_str(buf, key, value) - when %r{\ATransfer-Encoding\z}i - term = true if value =~ /\bchunked\b/i - kv_str(buf, key, value) - when "rack.hijack" - hijack = value - else - kv_str(buf, key, value) + code = status.to_i + hdr_only ||= Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(code) + msg = Rack::Utils::HTTP_STATUS_CODES[code] + buf = "#{response_start}#{msg ? %Q(#{code} #{msg}) : status}\r\n" \ + "Date: #{httpdate}\r\n".dup + headers.each do |key, value| + case key + when %r{\ADate\z}i + next + when %r{\AContent-Range\z}i + if %r{\Abytes (\d+)-(\d+)/\d+\z} =~ value + offset = $1.to_i + count = $2.to_i - offset + 1 end - end - count ||= clen - - if !term && chunk_ok + kv_str(buf, key, value) + when %r{\AConnection\z}i + # allow Rack apps to tell us they want to drop the client + alive = false if value =~ /\bclose\b/i + when %r{\AContent-Length\z}i term = true - body = Yahns::ChunkBody.new(body, opt) - buf << "Transfer-Encoding: chunked\r\n".freeze + clen = value.to_i + flags |= MSG_MORE if clen > 0 && !hdr_only + kv_str(buf, key, value) + when %r{\ATransfer-Encoding\z}i + term = true if value =~ /\bchunked\b/i + kv_str(buf, key, value) + when "rack.hijack" + hijack = value + else + kv_str(buf, key, value) end - alive &&= term - buf << (alive ? "Connection: keep-alive\r\n\r\n".freeze - : "Connection: close\r\n\r\n".freeze) - case rv = kgio_syssend(buf, flags) - when nil # all done, likely - buf.clear - buf = nil # recycle any memory we used ASAP - break - when String - flags = MSG_DONTWAIT - buf = rv # unlikely, hope the skb grows - when :wait_writable, :wait_readable # unlikely - if self.class.output_buffering - alive = hijack ? hijack : alive - rv = response_header_blocked(buf, body, alive, offset, count) - body = nil # ensure we do not close body in ensure - return rv - else - response_wait_write(rv) or return :close - end - end while true end + count ||= clen + + if !term && chunk_ok + term = true + body = Yahns::ChunkBody.new(body, opt) + buf << "Transfer-Encoding: chunked\r\n".freeze + end + alive &&= term + buf << (alive ? "Connection: keep-alive\r\n\r\n".freeze + : "Connection: close\r\n\r\n".freeze) + case rv = kgio_syssend(buf, flags) + when nil # all done, likely + buf.clear + buf = nil # recycle any memory we used ASAP + break + when String + flags = MSG_DONTWAIT + buf = rv # unlikely, hope the skb grows + when :wait_writable, :wait_readable # unlikely + if self.class.output_buffering + alive = hijack ? hijack : alive + rv = response_header_blocked(buf, body, alive, offset, count) + body = nil # ensure we do not close body in ensure + return rv + else + response_wait_write(rv) or return :close + end + end while @hs.headers? return response_hijacked(hijack) if hijack return http_response_done(alive) if hdr_only -- cgit v1.2.3-24-ge0c7