Skip to content

Commit

Permalink
Add support for the rack hijack protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
evanphx committed Feb 6, 2013
1 parent 804a6e2 commit daa76a1
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 7 deletions.
10 changes: 9 additions & 1 deletion lib/puma/client.rb
Expand Up @@ -36,14 +36,22 @@ def initialize(io, env)
@timeout_at = nil

@requests_served = 0
@hijacked = false
end

attr_reader :env, :to_io, :body, :io, :timeout_at, :ready
attr_reader :env, :to_io, :body, :io, :timeout_at, :ready, :hijacked

def inspect
"#<Puma::Client:0x#{object_id.to_s(16)} @ready=#{@ready.inspect}>"
end

# For the hijack protocol (allows us to just put the Client object
# into the env)
def call
@hijacked = true
env[HIJACK_IO] ||= @io
end

def set_timeout(val)
@timeout_at = Time.now + val
end
Expand Down
4 changes: 4 additions & 0 deletions lib/puma/const.rb
Expand Up @@ -147,5 +147,9 @@ module Const
COLON = ": ".freeze

NEWLINE = "\n".freeze

HIJACK_P = "rack.hijack?".freeze
HIJACK = "rack.hijack".freeze
HIJACK_IO = "rack.hijack_io".freeze
end
end
30 changes: 24 additions & 6 deletions lib/puma/server.rb
Expand Up @@ -332,6 +332,9 @@ def handle_request(req, lines)

env[PUMA_SOCKET] = client

env[HIJACK_P] = true
env[HIJACK] = req

body = req.body

env[RACK_INPUT] = body
Expand All @@ -345,6 +348,9 @@ def handle_request(req, lines)
begin
begin
status, headers, res_body = @app.call(env)

return :async if req.hijacked

status = status.to_i

if status == -1
Expand Down Expand Up @@ -406,6 +412,8 @@ def handle_request(req, lines)
end
end

response_hijack = nil

headers.each do |k, vs|
case k
when CONTENT_LENGTH2
Expand All @@ -416,6 +424,9 @@ def handle_request(req, lines)
content_length = nil
when CONTENT_TYPE
next if no_body
when HIJACK
response_hijack = vs
next
end

vs.split(NEWLINE).each do |v|
Expand All @@ -435,18 +446,25 @@ def handle_request(req, lines)
lines << CONNECTION_CLOSE
end

if content_length
lines.append CONTENT_LENGTH_S, content_length.to_s, line_ending
chunked = false
elsif allow_chunked
lines << TRANSFER_ENCODING_CHUNKED
chunked = true
unless response_hijack
if content_length
lines.append CONTENT_LENGTH_S, content_length.to_s, line_ending
chunked = false
elsif allow_chunked
lines << TRANSFER_ENCODING_CHUNKED
chunked = true
end
end

lines << line_ending

fast_write client, lines.to_s

if response_hijack
response_hijack.call client
return :async
end

res_body.each do |part|
if chunked
client.syswrite part.bytesize.to_s(16)
Expand Down
3 changes: 3 additions & 0 deletions test/hello-delay.ru
@@ -0,0 +1,3 @@
sleep 5

run lambda { |env| [200, {"Content-Type" => "text/plain"}, ["Hello World"]] }
6 changes: 6 additions & 0 deletions test/hijack.ru
@@ -0,0 +1,6 @@

run lambda { |env|
io = env['rack.hijack'].call
io.puts "HTTP/1.1 200\r\n\r\nBLAH"
[-1, {}, []]
}
5 changes: 5 additions & 0 deletions test/hijack2.ru
@@ -0,0 +1,5 @@
run lambda { |env|
body = lambda { |io| io.puts "BLAH\n"; io.close }

[200, { 'rack.hijack' => body }, []]
}

0 comments on commit daa76a1

Please sign in to comment.