public
Rubygem
Description: Merb Core: All you need. None you don't.
Homepage: http://www.merbivore.com
Clone URL: git://github.com/wycats/merb-core.git
Search Repo:
Add back support for streaming when using Mongrel or evented mongrel
ezmobius (author)
Wed Feb 13 18:06:57 -0800 2008
commit  8abb459d89ccb6061fa9dc59c380823619de1706
tree    e2308b970c53f0c54731eacc31683f0138fce10f
parent  a15b2e9aa1eb31f0a3fa7b2568f3ed25968e8433
...
1
2
3
4
 
 
 
5
6
7
8
...
34
35
36
 
37
38
39
 
 
 
40
41
42
43
...
44
45
46
47
 
 
48
49
50
51
52
53
 
 
54
55
56
...
61
62
63
64
 
 
65
66
67
...
77
78
79
 
80
81
82
83
...
111
112
113
114
115
 
116
117
 
118
119
120
...
122
123
124
125
 
126
127
128
...
140
141
142
 
143
144
145
...
1
2
3
 
4
5
6
7
8
9
10
...
36
37
38
39
40
 
 
41
42
43
44
45
46
47
...
48
49
50
 
51
52
53
54
55
56
 
 
57
58
59
60
61
...
66
67
68
 
69
70
71
72
73
...
83
84
85
86
87
88
89
90
...
118
119
120
 
 
121
122
 
123
124
125
126
...
128
129
130
 
131
132
133
134
...
146
147
148
149
150
151
152
0
@@ -1,7 +1,9 @@
0
 module Merb
0
   # Module that is mixed in to all implemented controllers.
0
   module ControllerMixin
0
-
0
+ def must_support_streaming!
0
+ raise(NotImplemented, "Current Rack adapter does not support streaming") unless request.env['rack.streaming']
0
+ end
0
     # Renders the block given as a parameter using chunked
0
     # encoding.
0
     #
0
0
@@ -34,9 +36,11 @@
0
     # send chunks of data down to the server. The chunking will
0
     # terminate once the block returns.
0
     def render_chunked(&blk)
0
+ must_support_streaming!
0
       headers['Transfer-Encoding'] = 'chunked'
0
- Proc.new {
0
- response.send_status_no_connection_close(0)
0
+ Proc.new { |response|
0
+ @response = response
0
+ response.send_status_no_connection_close('')
0
         response.send_header
0
         blk.call
0
         response.write("0\r\n\r\n")
0
0
@@ -44,13 +48,14 @@
0
     end
0
 
0
     # Writes a chunk from render_chunked to the response that
0
- # is sent back to the client.
0
+ # is sent back to the client. This can only be called within
0
+ # a render_chunked {} block
0
     #
0
     # ==== Parameters
0
     # data<String>:: a chunk of data to return
0
     def send_chunk(data)
0
- response.write('%x' % data.size + "\r\n")
0
- response.write(data + "\r\n")
0
+ @response.write('%x' % data.size + "\r\n")
0
+ @response.write(data + "\r\n")
0
     end
0
     
0
     # Returns a +Proc+ that Mongrel can call later, allowing
0
@@ -61,7 +66,8 @@
0
     # A proc that should get called outside the mutex,
0
     # and which will return the value to render
0
     def render_deferred(&blk)
0
- Proc.new {
0
+ must_support_streaming!
0
+ Proc.new {|response|
0
         result = blk.call
0
         response.send_status(result.length)
0
         response.send_header
0
@@ -77,6 +83,7 @@
0
     # blk<Proc>:: A proc that should get called once the string has
0
     # been returned
0
     def render_then_call(str, &blk)
0
+ must_support_streaming!
0
       Proc.new {
0
         response.send_status(str.length)
0
         response.send_header
0
0
@@ -111,10 +118,9 @@
0
       headers.update(
0
         'Content-Type' => opts[:type].strip, # fixes a problem with extra '\r' with some browsers
0
         'Content-Disposition' => disposition,
0
- 'Content-Transfer-Encoding' => 'binary',
0
- 'X-SENDFILE' => file
0
+ 'Content-Transfer-Encoding' => 'binary'
0
       )
0
- return
0
+ File.open(file)
0
     end
0
     
0
     # Streams a file over HTTP.
0
@@ -122,7 +128,7 @@
0
     # ==== Example
0
     # stream_file( { :filename => file_name,
0
     # :type => content_type,
0
- # :content_length => content_length }) do
0
+ # :content_length => content_length }) do |response|
0
     # AWS::S3::S3Object.stream(user.folder_name + "-" + user_file.unique_id, bucket_name) do |chunk|
0
     # response.write chunk
0
     # end
0
@@ -140,6 +146,7 @@
0
     # :filename<String>:: An acceptable value for the filename= portion
0
     # of headers["Content-Disposition"]
0
     def stream_file(opts={}, &stream)
0
+ must_support_streaming!
0
       opts.update(Merb::Const::DEFAULT_SEND_FILE_OPTIONS.merge(opts))
0
       disposition = opts[:disposition].dup || 'attachment'
0
       disposition << %(; filename="#{opts[:filename]}")
...
1
2
 
3
4
5
...
8
9
10
11
 
12
13
14
...
1
 
2
3
4
5
...
8
9
10
 
11
12
13
14
0
@@ -1,5 +1,5 @@
0
 require 'mongrel'
0
-require 'rack/handler/mongrel'
0
+require 'merb-core/rack/handler/mongrel'
0
 module Merb
0
 
0
   module Rack
0
@@ -8,7 +8,7 @@
0
       # start server on given host and port.
0
       def self.start(opts={})
0
         server = ::Mongrel::HttpServer.new(opts[:host], opts[:port])
0
- server.register('/', ::::Rack::Handler::Mongrel.new(opts[:app]))
0
+ server.register('/', ::Merb::Rack::Handler::Mongrel.new(opts[:app]))
0
         server.run.join
0
       end
0
     end
...
11
12
13
14
 
15
16
17
18
19
20
 
 
 
21
22
23
24
25
 
26
27
28
...
11
12
13
 
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
0
@@ -11,18 +11,22 @@
0
       def call(env)
0
         path = env['PATH_INFO'].chomp('/')
0
         cached_path = (path.empty? ? 'index' : path) + '.html'
0
-
0
+ Merb.logger.info "Request: #{path}"
0
         if file_exist?(path) # Serve the file if it's there
0
           serve_static(env)
0
         elsif file_exist?(cached_path) # Serve the page cache if it's there
0
           env['PATH_INFO'] = cached_path
0
           serve_static(env)
0
         else # No static file, let Merb handle it
0
+ if path =~ /favicon\.ico/
0
+ return [404, {"Content-Type"=>"text/html"}, "404 Not Founds."]
0
+ end
0
           begin
0
             controller = ::Merb::Dispatcher.handle(env)
0
           rescue Object => e
0
             return [500, {"Content-Type"=>"text/html"}, e.message + "<br/>" + e.backtrace.join("<br/>")]
0
           end
0
+ Merb.logger.info "\n\n"
0
           Merb.logger.flush
0
           [controller.status, controller.headers, controller.body]
0
         end
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
0
@@ -1 +1,77 @@
0
+require 'mongrel'
0
+require 'stringio'
0
+
0
+class Mongrel::HttpResponse
0
+ NO_CLOSE_STATUS_FORMAT = "HTTP/1.1 %d %s\r\n".freeze
0
+ def send_status_no_connection_close(content_length=@body.length)
0
+ unless @status_sent
0
+ write(NO_CLOSE_STATUS_FORMAT % [@status, Mongrel::HTTP_STATUS_CODES[@status]])
0
+ @status_sent = true
0
+ end
0
+ end
0
+end
0
+
0
+module Merb
0
+ module Rack
0
+ module Handler
0
+ class Mongrel < ::Mongrel::HttpHandler
0
+ def self.run(app, options={})
0
+ server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0',
0
+ options[:Port] || 8080)
0
+ server.register('/', ::Merb::Rack::Handler::Mongrel.new(app))
0
+ yield server if block_given?
0
+ server.run.join
0
+ end
0
+
0
+ def initialize(app)
0
+ @app = app
0
+ end
0
+
0
+ def process(request, response)
0
+ env = {}.replace(request.params)
0
+ env.delete "HTTP_CONTENT_TYPE"
0
+ env.delete "HTTP_CONTENT_LENGTH"
0
+
0
+ env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
0
+
0
+ env.update({"rack.version" => [0,1],
0
+ "rack.input" => request.body || StringIO.new(""),
0
+ "rack.errors" => STDERR,
0
+
0
+ "rack.multithread" => true,
0
+ "rack.multiprocess" => false, # ???
0
+ "rack.run_once" => false,
0
+
0
+ "rack.url_scheme" => "http",
0
+ "rack.streaming" => true
0
+ })
0
+ env["QUERY_STRING"] ||= ""
0
+ env.delete "PATH_INFO" if env["PATH_INFO"] == ""
0
+
0
+ status, headers, body = @app.call(env)
0
+
0
+ begin
0
+ response.status = status.to_i
0
+ headers.each { |k, vs|
0
+ vs.each { |v|
0
+ response.header[k] = v
0
+ }
0
+ }
0
+
0
+ if body.respond_to?(:call)
0
+ body.call(response)
0
+ else
0
+ body.each { |part|
0
+ response.body << part
0
+ }
0
+ end
0
+ response.finished
0
+ ensure
0
+ body.close if body.respond_to? :close
0
+ end
0
+ end
0
+ end
0
+ end
0
+ end
0
+end

Comments

    No one has commented yet.