packet 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,214 +1,208 @@
1
- # This module rewrites pieces of the very good Mongrel web server in
2
- # order to change it from a threaded application to an event based
3
- # application running inside an Packet event loop. It should
4
- # be compatible with the existing Mongrel handlers for Rails,
5
- # Camping, Nitro, etc....
6
-
7
- require 'packet'
1
+ require "packet"
8
2
  require 'mongrel'
9
3
 
10
4
  module Mongrel
11
- class MongrelProtocol < Packet::Connection
12
- def post_init
13
- @parser = HttpParser.new
14
- @params = HttpParams.new
15
- @nparsed = 0
16
- @request = nil
17
- @request_len = nil
18
- @linebuffer = ''
19
- end
20
-
21
- def receive_data data
22
- @linebuffer << data
23
- @nparsed = @parser.execute(@params, @linebuffer, @nparsed) unless @parser.finished?
24
- if @parser.finished?
25
- if @request_len.nil?
26
- @request_len = @params[::Mongrel::Const::CONTENT_LENGTH].to_i
27
- script_name, path_info, handlers = ::Mongrel::HttpServer::Instance.classifier.resolve(@params[::Mongrel::Const::REQUEST_PATH])
28
- if handlers
29
- @params[::Mongrel::Const::PATH_INFO] = path_info
30
- @params[::Mongrel::Const::SCRIPT_NAME] = script_name
31
- @params[::Mongrel::Const::REMOTE_ADDR] = @params[::Mongrel::Const::HTTP_X_FORWARDED_FOR] #|| ::Socket.unpack_sockaddr_in(get_peername)[1]
32
- @notifiers = handlers.select { |h| h.request_notify }
33
- end
34
- if @request_len > ::Mongrel::Const::MAX_BODY
35
- new_buffer = Tempfile.new(::Mongrel::Const::MONGREL_TMP_BASE)
36
- new_buffer.binmode
37
- new_buffer << @linebuffer[@nparsed..-1]
38
- @linebuffer = new_buffer
39
- else
40
- @linebuffer = StringIO.new(@linebuffer[@nparsed..-1])
41
- @linebuffer.pos = @linebuffer.length
42
- end
43
- end
44
- if @linebuffer.length >= @request_len
45
- @linebuffer.rewind
46
- ::Mongrel::HttpServer::Instance.process_http_request(@params,@linebuffer,self)
47
- @linebuffer.delete if Tempfile === @linebuffer
48
- end
49
- elsif @linebuffer.length > ::Mongrel::Const::MAX_HEADER
50
- close_connection
51
- raise ::Mongrel::HttpParserError.new("HEADER is longer than allowed, aborting client early.")
52
- end
53
- rescue ::Mongrel::HttpParserError
54
- if $mongrel_debug_client
55
- STDERR.puts "#{Time.now}: BAD CLIENT (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #$!"
56
- STDERR.puts "#{Time.now}: REQUEST DATA: #{data.inspect}\n---\nPARAMS: #{params.inspect}\n---\n"
57
- end
58
- close_connection
59
- rescue Exception => e
60
- close_connection
61
- raise e
62
- end
63
-
64
- def write data
65
- send_data data
66
- end
67
-
68
- def closed?
69
- false
70
- end
71
-
72
- end
73
-
74
- class HttpServer
75
- def initialize(host, port, num_processors=950, x=0, y=nil) # Deal with Mongrel 1.0.1 or earlier, as well as later.
76
- @socket = nil
77
- @classifier = URIClassifier.new
78
- @host = host
79
- @port = port
80
- @workers = ThreadGroup.new
81
- if y
82
- @throttle = x
83
- @timeout = y || 60
84
- else
85
- @timeout = x
86
- end
87
- @num_processors = num_processors #num_processors is pointless for evented....
88
- @death_time = 60
89
- self.class.const_set(:Instance,self)
90
- end
91
-
92
- def run
93
- trap('INT') { raise StopServer }
94
- trap('TERM') { raise StopServer }
95
- @acceptor = Thread.new do
96
- Packet::Reactor.run do |t_reactor|
97
- begin
5
+ class MongrelProtocol
6
+ def post_init
7
+ @parser = HttpParser.new
8
+ @params = HttpParams.new
9
+ @nparsed = 0
10
+ @request = nil
11
+ @request_len = nil
12
+ @linebuffer = ''
13
+ end
14
+
15
+ def receive_data data
16
+ @linebuffer << data
17
+ @nparsed = @parser.execute(@params, @linebuffer, @nparsed) unless @parser.finished?
18
+ if @parser.finished?
19
+ if @request_len.nil?
20
+ @request_len = @params[::Mongrel::Const::CONTENT_LENGTH].to_i
21
+ script_name, path_info, handlers = ::Mongrel::HttpServer::Instance.classifier.resolve(@params[::Mongrel::Const::REQUEST_PATH])
22
+ if handlers
23
+ @params[::Mongrel::Const::PATH_INFO] = path_info
24
+ @params[::Mongrel::Const::SCRIPT_NAME] = script_name
25
+ @params[::Mongrel::Const::REMOTE_ADDR] = @params[::Mongrel::Const::HTTP_X_FORWARDED_FOR] #|| ::Socket.unpack_sockaddr_in(get_peername)[1]
26
+ @notifiers = handlers.select { |h| h.request_notify }
27
+ end
28
+ if @request_len > ::Mongrel::Const::MAX_BODY
29
+ new_buffer = Tempfile.new(::Mongrel::Const::MONGREL_TMP_BASE)
30
+ new_buffer.binmode
31
+ new_buffer << @linebuffer[@nparsed..-1]
32
+ @linebuffer = new_buffer
33
+ else
34
+ @linebuffer = StringIO.new(@linebuffer[@nparsed..-1])
35
+ @linebuffer.pos = @linebuffer.length
36
+ end
37
+ end
38
+ if @linebuffer.length >= @request_len
39
+ @linebuffer.rewind
40
+ ::Mongrel::HttpServer::Instance.process_http_request(@params,@linebuffer,self)
41
+ @linebuffer.delete if Tempfile === @linebuffer
42
+ end
43
+ elsif @linebuffer.length > ::Mongrel::Const::MAX_HEADER
44
+ close_connection
45
+ raise ::Mongrel::HttpParserError.new("HEADER is longer than allowed, aborting client early.")
46
+ end
47
+ rescue ::Mongrel::HttpParserError
48
+ if $mongrel_debug_client
49
+ STDERR.puts "#{Time.now}: BAD CLIENT (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #$!"
50
+ STDERR.puts "#{Time.now}: REQUEST DATA: #{data.inspect}\n---\nPARAMS: #{params.inspect}\n---\n"
51
+ end
52
+ close_connection
53
+ rescue Exception => e
54
+ close_connection
55
+ raise e
56
+ end
57
+
58
+ def write data
59
+ send_data data
60
+ end
61
+
62
+ def closed?
63
+ false
64
+ end
65
+
66
+ end
67
+
68
+ class HttpServer
69
+ def initialize(host, port, num_processors=950, x=0, y=nil) # Deal with Mongrel 1.0.1 or earlier, as well as later.
70
+ @socket = nil
71
+ @classifier = URIClassifier.new
72
+ @host = host
73
+ @port = port
74
+ @workers = ThreadGroup.new
75
+ if y
76
+ @throttle = x
77
+ @timeout = y || 60
78
+ else
79
+ @timeout = x
80
+ end
81
+ @num_processors = num_processors #num_processors is pointless for evented....
82
+ @death_time = 60
83
+ self.class.const_set(:Instance,self)
84
+ end
85
+
86
+ def run
87
+ trap('INT') { raise StopServer }
88
+ trap('TERM') { raise StopServer }
89
+ @acceptor = Thread.new do
90
+ Packet::Reactor.run do |t_reactor|
91
+ begin
98
92
  t_reactor.start_server(@host,@port,MongrelProtocol)
99
93
  rescue StopServer
100
94
  t_reactor.start_server
101
- end
95
+ end
102
96
  end
103
- end
104
- end
105
-
106
- def process_http_request(params,linebuffer,client)
107
- if not params[Const::REQUEST_PATH]
108
- uri = URI.parse(params[Const::REQUEST_URI])
109
- params[Const::REQUEST_PATH] = uri.request_uri
110
- end
111
-
112
- raise "No REQUEST PATH" if not params[Const::REQUEST_PATH]
113
-
114
- script_name, path_info, handlers = @classifier.resolve(params[Const::REQUEST_PATH])
115
-
116
- if handlers
117
- notifiers = handlers.select { |h| h.request_notify }
118
- request = HttpRequest.new(params, linebuffer, notifiers)
119
-
120
- # request is good so far, continue processing the response
121
- response = HttpResponse.new(client)
122
-
123
- # Process each handler in registered order until we run out or one finalizes the response.
124
- dispatch_to_handlers(handlers,request,response)
125
-
126
- # And finally, if nobody closed the response off, we finalize it.
127
- unless response.done
128
- response.finished
129
- else
130
- response.close_connection
131
- end
132
- else
133
- # Didn't find it, return a stock 404 response.
134
- client.send_data(Const::ERROR_404_RESPONSE)
135
- client.close_connection
136
- end
137
- end
138
-
139
- def dispatch_to_handlers(handlers,request,response)
140
- handlers.each do |handler|
141
- handler.process(request, response)
142
- break if response.done
143
- end
144
- end
145
- end
146
-
147
- class HttpRequest
148
- def initialize(params, linebuffer, dispatchers)
149
- @params = params
150
- @dispatchers = dispatchers
151
- @body = linebuffer
152
- end
153
- end
154
-
155
- class HttpResponse
156
- def send_file(path, small_file = false)
157
- File.open(path, "rb") do |f|
158
- while chunk = f.read(Const::CHUNK_SIZE) and chunk.length > 0
159
- begin
160
- write(chunk)
161
- rescue Object => exc
162
- break
163
- end
164
- end
165
- end
166
- @body_sent = true
167
- end
168
-
169
- def write(data)
170
- @socket.send_data data
171
- end
172
-
173
- def close_connection_after_writing
174
- @socket.close_connection
175
- end
176
-
177
- def socket_error(details)
178
- @socket.close_connection
179
- done = true
180
- raise details
181
- end
182
-
183
- def finished
184
- send_status
185
- send_header
186
- send_body
187
- @socket.close_connection
188
- end
189
- end
190
-
191
- class Configurator
192
- # This version fixes a bug in the regular Mongrel version by adding
193
- # initialization of groups.
194
- def change_privilege(user, group)
195
- if user and group
196
- log "Initializing groups for {#user}:{#group}."
197
- Process.initgroups(user,Etc.getgrnam(group).gid)
198
- end
199
-
200
- if group
201
- log "Changing group to #{group}."
202
- Process::GID.change_privilege(Etc.getgrnam(group).gid)
203
- end
204
-
205
- if user
206
- log "Changing user to #{user}."
207
- Process::UID.change_privilege(Etc.getpwnam(user).uid)
208
- end
209
- rescue Errno::EPERM
210
- log "FAILED to change user:group #{user}:#{group}: #$!"
211
- exit 1
212
- end
213
- end
97
+ end
98
+ end
99
+
100
+ def process_http_request(params,linebuffer,client)
101
+ if not params[Const::REQUEST_PATH]
102
+ uri = URI.parse(params[Const::REQUEST_URI])
103
+ params[Const::REQUEST_PATH] = uri.request_uri
104
+ end
105
+
106
+ raise "No REQUEST PATH" if not params[Const::REQUEST_PATH]
107
+
108
+ script_name, path_info, handlers = @classifier.resolve(params[Const::REQUEST_PATH])
109
+
110
+ if handlers
111
+ notifiers = handlers.select { |h| h.request_notify }
112
+ request = HttpRequest.new(params, linebuffer, notifiers)
113
+
114
+ # request is good so far, continue processing the response
115
+ response = HttpResponse.new(client)
116
+
117
+ # Process each handler in registered order until we run out or one finalizes the response.
118
+ dispatch_to_handlers(handlers,request,response)
119
+
120
+ # And finally, if nobody closed the response off, we finalize it.
121
+ unless response.done
122
+ response.finished
123
+ end
124
+ else
125
+ # Didn't find it, return a stock 404 response.
126
+ client.send_data(Const::ERROR_404_RESPONSE)
127
+ client.close_connection
128
+ end
129
+ end
130
+
131
+ def dispatch_to_handlers(handlers,request,response)
132
+ handlers.each do |handler|
133
+ handler.process(request, response)
134
+ break if response.done
135
+ end
136
+ end
137
+ end
138
+
139
+ class HttpRequest
140
+ def initialize(params, linebuffer, dispatchers)
141
+ @params = params
142
+ @dispatchers = dispatchers
143
+ @body = linebuffer
144
+ end
145
+ end
146
+
147
+ class HttpResponse
148
+ def send_file(path, small_file = false)
149
+ File.open(path, "rb") do |f|
150
+ while chunk = f.read(Const::CHUNK_SIZE) and chunk.length > 0
151
+ begin
152
+ write(chunk)
153
+ rescue Object => exc
154
+ break
155
+ end
156
+ end
157
+ end
158
+ @body_sent = true
159
+ end
160
+
161
+ def write(data)
162
+ @socket.send_data data
163
+ end
164
+
165
+ def close_connection_after_writing
166
+ @socket.close_connection
167
+ end
168
+
169
+ def socket_error(details)
170
+ @socket.close_connection
171
+ done = true
172
+ raise details
173
+ end
174
+
175
+ def finished
176
+ send_status
177
+ send_header
178
+ send_body
179
+ @socket.close_connection
180
+ end
181
+ end
182
+
183
+ class Configurator
184
+ # This version fixes a bug in the regular Mongrel version by adding
185
+ # initialization of groups.
186
+ def change_privilege(user, group)
187
+ if user and group
188
+ log "Initializing groups for {#user}:{#group}."
189
+ Process.initgroups(user,Etc.getgrnam(group).gid)
190
+ end
191
+
192
+ if group
193
+ log "Changing group to #{group}."
194
+ Process::GID.change_privilege(Etc.getgrnam(group).gid)
195
+ end
196
+
197
+ if user
198
+ log "Changing user to #{user}."
199
+ Process::UID.change_privilege(Etc.getpwnam(user).uid)
200
+ end
201
+ rescue Errno::EPERM
202
+ log "FAILED to change user:group #{user}:#{group}: #$!"
203
+ exit 1
204
+ end
205
+ end
214
206
  end
207
+
208
+
@@ -0,0 +1,10 @@
1
+ PACKET_APP = File.expand_path(File.join(File.dirname(__FILE__) + "/.."))
2
+ ["lib"].each { |x| $LOAD_PATH.unshift(EVAL_APP_ROOT + "/#{x}")}
3
+ require "packet"
4
+ require "rubygems"
5
+ require "test/spec"
6
+ require "mocha"
7
+
8
+
9
+
10
+
@@ -0,0 +1,14 @@
1
+ require File.join(File.dirname(__FILE__) + "/spec_helper")
2
+ context "Double Keyed Hash in general" do
3
+ xspecify "should allow muliple keys while storing the value in hash" do
4
+ end
5
+
6
+ xspecify "should return correct value when either of the keys is used" do
7
+ end
8
+
9
+ xspecify "should return nil if nither of keys match" do
10
+ end
11
+
12
+ xspecify "should allow deletion of value from hash based on either of keys" do
13
+ end
14
+ end
@@ -0,0 +1,38 @@
1
+ require File.join(File.dirname(__FILE__) + "/spec_helper")
2
+ context "Packet Core in general when mixed inside a class" do
3
+ xspecify "allow the class to act as a reactor" do
4
+ end
5
+
6
+ xspecify "should start a server on specified port" do
7
+ end
8
+
9
+ xspecify "should let clients connect to the server" do
10
+ end
11
+
12
+ xspecify "should be able to connect to external servers" do
13
+ end
14
+
15
+ xspecify "should be able to read data from clients when socket is ready" do
16
+ end
17
+
18
+ xspecify "should be able to write data to clients when socket is ready for write" do
19
+ end
20
+
21
+ xspecify "should invoke receive_data method data is receieved from clients" do
22
+ end
23
+
24
+ xspecify "should invoke post_init when client connects" do
25
+ end
26
+
27
+ xspecify "should invoke unbind when a client disconnects" do
28
+ end
29
+
30
+ xspecify "should invoke connection_completed when connection to external server is connected." do
31
+ end
32
+
33
+ xspecify "should check for ready timers on each iteration" do
34
+ end
35
+
36
+ xspecify "should run proper timer on each iteration." do
37
+ end
38
+ end