Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

add partial rack hijack for WEBrick #533

Merged
merged 2 commits into from

4 participants

@dahakawang

No description provided.

lib/rack/handler/webrick.rb
@@ -69,9 +76,19 @@ def service(req, res)
res[k] = vs.split("\n").join(", ")
end
}
- body.each { |part|
- res.body << part
- }
+
+ io_lambda = headers["rack.hijack"]
+ if io_lambda
+ rd, wr = IO.pipe
+ res.body = rd
+ res.chunked = true
+ res.body
@rkh Owner
rkh added a note

What's this line for?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/rack/handler/webrick.rb
@@ -46,7 +47,11 @@ def service(req, res)
"rack.multiprocess" => false,
"rack.run_once" => false,
- "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
+ "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http",
+
+ "rack.hijack?" => true,
+ "rack.hijack" => lambda { raise RuntimeError, "only partial hijack is supported."},
@rkh Owner
rkh added a note

Should this be a NotImplementedError maybe?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@rkh
Owner

OK, this looks pretty sweet.

@raggi wdyt?

Also, we might wanna add tests.

@dahakawang

@rkh im going to add some test cases later.. :smiley:

@rkh
Owner

Good work! WEBrick is threaded internally for the part that's streaming out the body, is it? Can this handle more than one connection at a time or will one hijacked dangling connection block everything?

@dahakawang

@rkh im not familiar with WEBrick‘s internal implementation, but iv done some experiments which show multiple connections can be established simultaneously with a hijacked controller(within rails).

@rkh rkh merged commit 232ed1e into rack:master

1 check passed

Details default The Travis build passed
@pixeltrix

@spastorino @tenderlove this is making Rails static file serving in development very slow (see rails/rails#18828) - this doesn't seem right to me, wdyt?

TBH I don't understand why we're sending static assets through the whole Rack pipeline in development mode. If we know that static assets are always going to sit in a certain location, why don't we add a WEBRick File handler to point at that directory? Not only could it serve up the files faster, but it wouldn't go through Rack::Lock in dev mode (which means static assets could be served in parallel).

@tenderlove because the asset files are coming from Sprockets not from files in /public/assets typically (unless you've run rake assets:precompile but that's a whole other set of issues).

Even if we did somehow manage to make assets work via a WEBRick file handler, do we want to send rendered output five bytes at a time? :smile:

because the asset files are coming from Sprockets not from files in /public/assets typically

So they're not actually static? If they are actually static, I don't think there's anything preventing us from pointing a file handler in the app/assets directory.

Even if we did somehow manage to make assets work via a WEBRick file handler, do we want to send rendered output five bytes at a time?

We can separate the byte sizes between static and dynamic requests.

So they're not actually static? If they are actually static, I don't think there's anything preventing us from pointing a file handler in the app/assets directory

The app/assets directory structure isn't the url structure - for example app/assets/images/logo.png => /assets/logo.png.

We can separate the byte sizes between static and dynamic requests

But surely five bytes is too small even for dynamic requests?

Ya, I think we can increase the byte size. I just really want to get asset serving out of Rack::Lock. We have many files, and serving them serially us super painful.

The app/assets directory structure isn't the url structure - for example app/assets/images/logo.png => /assets/logo.png.

Do you know if that's the only transformation? We can mount a webrick file handler at any URL and have the base be somewhere else. I just don't know the rules.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 28, 2013
  1. @dahakawang
Commits on Mar 29, 2013
  1. @dahakawang
This page is out of date. Refresh to see the latest.
Showing with 43 additions and 4 deletions.
  1. +20 −4 lib/rack/handler/webrick.rb
  2. +23 −0 test/spec_webrick.rb
View
24 lib/rack/handler/webrick.rb
@@ -8,6 +8,7 @@ class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
def self.run(app, options={})
options[:BindAddress] = options.delete(:Host) if options[:Host]
options[:Port] ||= 8080
+ options[:OutputBufferSize] = 5
@server = ::WEBrick::HTTPServer.new(options)
@server.mount "/", Rack::Handler::WEBrick, app
yield @server if block_given?
@@ -46,7 +47,11 @@ def service(req, res)
"rack.multiprocess" => false,
"rack.run_once" => false,
- "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
+ "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http",
+
+ "rack.hijack?" => true,
+ "rack.hijack" => lambda { raise NotImplementedError, "only partial hijack is supported."},
+ "rack.hijack_io" => nil,
})
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
@@ -61,6 +66,8 @@ def service(req, res)
begin
res.status = status.to_i
headers.each { |k, vs|
+ next if k.downcase == "rack.hijack"
+
if k.downcase == "set-cookie"
res.cookies.concat vs.split("\n")
else
@@ -69,9 +76,18 @@ def service(req, res)
res[k] = vs.split("\n").join(", ")
end
}
- body.each { |part|
- res.body << part
- }
+
+ io_lambda = headers["rack.hijack"]
+ if io_lambda
+ rd, wr = IO.pipe
+ res.body = rd
+ res.chunked = true
+ io_lambda.call wr
+ else
+ body.each { |part|
+ res.body << part
+ }
+ end
ensure
body.close if body.respond_to? :close
end
View
23 test/spec_webrick.rb
@@ -139,5 +139,28 @@
}
end
+ should "support Rack partial hijack" do
+ io_lambda = lambda{ |io|
+ 5.times do
+ io.write "David\r\n"
+ end
+ io.close
+ }
+
+ @server.mount "/partial", Rack::Handler::WEBrick,
+ Rack::Lint.new(lambda{ |req|
+ [
+ 200,
+ {"rack.hijack" => io_lambda},
+ [""]
+ ]
+ })
+
+ Net::HTTP.start(@host, @port){ |http|
+ res = http.get("/partial")
+ res.body.should.equal "David\r\nDavid\r\nDavid\r\nDavid\r\nDavid\r\n"
+ }
+ end
+
@server.shutdown
end
Something went wrong with that request. Please try again.