Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added example for Rack hijack response SPEC

  • Loading branch information...
commit e04855459cb42fd98a0a483075f8337cafe6d949 1 parent 4acc072
James Tucker authored
Showing with 104 additions and 0 deletions.
  1. +89 −0 example/hijack_response.ru
  2. +15 −0 lib/thin/connection.rb
89 example/hijack_response.ru
View
@@ -0,0 +1,89 @@
+require 'thread'
+
+class HijackChat
+ def initialize
+ @chatters = []
+ @buffers = Hash.new { |h, io| h[io] = "" }
+ @mutex = Mutex.new
+ end
+
+ def call(env)
+ headers = {}
+ headers['rack.hijack'] = proc do |io|
+ io.write "Welcome to HijackChat!\n"
+ io.flush
+ puts "Hijacked: #{io.inspect}"
+ chatters { |c| c << io }
+ end
+ [200, headers, []]
+ end
+
+ def start
+ @thread = Thread.new do
+ Thread.current.abort_on_exception = true
+
+ active = false
+ while true
+ each do |c|
+ if line = buffer(c)
+ puts "Got line: #{line.inspect}"
+ active = true
+ distribute line, c
+ end
+ end
+ # Very poor mans CPU backoff...
+ sleep 0.2 unless active
+ end
+ end
+ end
+
+ def buffer c
+ buf = @buffers[c]
+ buf << c.read_nonblock(65536)
+ buf.slice!(/.+(?:\r\n|[\r\n])/)
+ rescue IO::WaitReadable
+ # do nothing
+ rescue IOError
+ # whatever...
+ kill c
+ nil
+ end
+
+ def distribute line, source
+ each do |c|
+ next if c == source
+ safe_write line, c
+ end
+ end
+
+ def safe_write line, c
+ # Sync write, because it's a demo right??
+ c.write line
+ c.flush
+ rescue IOError
+ # whatever...
+ kill c
+ nil
+ end
+
+ def kill c
+ c.close
+ @buffers.delete c
+ @chatters.delete c
+ end
+
+ def each &block
+ chatters { |cs| cs.dup }.each &block
+ end
+
+ def chatters
+ @mutex.synchronize do
+ yield @chatters
+ end
+ end
+end
+
+app = HijackChat.new
+app.start
+
+run app
15 lib/thin/connection.rb
View
@@ -109,8 +109,23 @@ def post_process(result)
# Status code -1 indicates that we're going to respond later (async).
return if result.first == AsyncResponse.first
+ # XXX delete is not actually part of the Rack SPEC...
+ hijacking_callback = result[1].delete 'rack.hijack'
+
@response.status, @response.headers, @response.body = *result
+ if hijacking_callback
+ top = @response.head
+ trace { top }
+ send_data top
+ if EM.reactor_running?
+ EM.next_tick { hijacking_callback.call hijack }
+ else
+ hijacking_callback.call hijack
+ end
+ return
+ end
+
log "!! Rack application returned nil body. Probably you wanted it to be an empty string?" if @response.body.nil?
# Make the response persistent if requested by the client
Please sign in to comment.
Something went wrong with that request. Please try again.