packet 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +1 -1
- data/TODO +2 -0
- data/examples/netbeans.jpg +0 -0
- data/examples/write_bulk.rb +41 -0
- data/lib/packet.rb +19 -26
- data/lib/{disconnect_error.rb → packet/disconnect_error.rb} +3 -2
- data/lib/{double_keyed_hash.rb → packet/double_keyed_hash.rb} +0 -0
- data/lib/{callback.rb → packet/packet_callback.rb} +0 -0
- data/lib/{connection.rb → packet/packet_connection.rb} +10 -3
- data/lib/{core.rb → packet/packet_core.rb} +84 -40
- data/lib/{event.rb → packet/packet_event.rb} +1 -1
- data/lib/{packet_guid.rb → packet/packet_guid.rb} +0 -0
- data/lib/{class_helpers.rb → packet/packet_helper.rb} +0 -0
- data/lib/{packet_master.rb → packet/packet_master.rb} +18 -10
- data/lib/{meta_pimp.rb → packet/packet_meta_pimp.rb} +1 -1
- data/lib/packet/packet_mongrel.rb +218 -0
- data/lib/{nbio.rb → packet/packet_nbio.rb} +37 -37
- data/lib/{bin_parser.rb → packet/packet_parser.rb} +0 -0
- data/lib/{periodic_event.rb → packet/packet_periodic_event.rb} +0 -0
- data/lib/{pimp.rb → packet/packet_pimp.rb} +3 -1
- data/lib/{worker.rb → packet/packet_worker.rb} +1 -4
- data/lib/{timer_store.rb → packet/timer_store.rb} +26 -18
- data/lib/packet_mongrel.rb +200 -206
- data/spec/spec_helper.rb +10 -0
- data/spec/test_double_keyed_hash.rb +14 -0
- data/spec/test_packet_core.rb +38 -0
- metadata +25 -20
- data/lib/thread_pool.rb +0 -54
- data/lib/worker_pool.rb +0 -10
@@ -0,0 +1,218 @@
|
|
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
|
+
PACKET_APP = File.expand_path(File.join(File.dirname(__FILE__) + "/.."))
|
7
|
+
["bin","config","parser","worker","framework","lib","pimp"].each { |x| $LOAD_PATH.unshift(PACKET_APP + "/#{x}")}
|
8
|
+
require "rubygems"
|
9
|
+
require "packet"
|
10
|
+
require 'mongrel'
|
11
|
+
|
12
|
+
module Mongrel
|
13
|
+
class MongrelProtocol
|
14
|
+
def post_init
|
15
|
+
@parser = HttpParser.new
|
16
|
+
@params = HttpParams.new
|
17
|
+
@nparsed = 0
|
18
|
+
@request = nil
|
19
|
+
@request_len = nil
|
20
|
+
@linebuffer = ''
|
21
|
+
end
|
22
|
+
|
23
|
+
def receive_data data
|
24
|
+
@linebuffer << data
|
25
|
+
@nparsed = @parser.execute(@params, @linebuffer, @nparsed) unless @parser.finished?
|
26
|
+
if @parser.finished?
|
27
|
+
if @request_len.nil?
|
28
|
+
@request_len = @params[::Mongrel::Const::CONTENT_LENGTH].to_i
|
29
|
+
script_name, path_info, handlers = ::Mongrel::HttpServer::Instance.classifier.resolve(@params[::Mongrel::Const::REQUEST_PATH])
|
30
|
+
if handlers
|
31
|
+
@params[::Mongrel::Const::PATH_INFO] = path_info
|
32
|
+
@params[::Mongrel::Const::SCRIPT_NAME] = script_name
|
33
|
+
@params[::Mongrel::Const::REMOTE_ADDR] = @params[::Mongrel::Const::HTTP_X_FORWARDED_FOR] #|| ::Socket.unpack_sockaddr_in(get_peername)[1]
|
34
|
+
@notifiers = handlers.select { |h| h.request_notify }
|
35
|
+
end
|
36
|
+
if @request_len > ::Mongrel::Const::MAX_BODY
|
37
|
+
new_buffer = Tempfile.new(::Mongrel::Const::MONGREL_TMP_BASE)
|
38
|
+
new_buffer.binmode
|
39
|
+
new_buffer << @linebuffer[@nparsed..-1]
|
40
|
+
@linebuffer = new_buffer
|
41
|
+
else
|
42
|
+
@linebuffer = StringIO.new(@linebuffer[@nparsed..-1])
|
43
|
+
@linebuffer.pos = @linebuffer.length
|
44
|
+
end
|
45
|
+
end
|
46
|
+
if @linebuffer.length >= @request_len
|
47
|
+
@linebuffer.rewind
|
48
|
+
::Mongrel::HttpServer::Instance.process_http_request(@params,@linebuffer,self)
|
49
|
+
@linebuffer.delete if Tempfile === @linebuffer
|
50
|
+
end
|
51
|
+
elsif @linebuffer.length > ::Mongrel::Const::MAX_HEADER
|
52
|
+
close_connection
|
53
|
+
raise ::Mongrel::HttpParserError.new("HEADER is longer than allowed, aborting client early.")
|
54
|
+
end
|
55
|
+
rescue ::Mongrel::HttpParserError
|
56
|
+
if $mongrel_debug_client
|
57
|
+
STDERR.puts "#{Time.now}: BAD CLIENT (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #$!"
|
58
|
+
STDERR.puts "#{Time.now}: REQUEST DATA: #{data.inspect}\n---\nPARAMS: #{params.inspect}\n---\n"
|
59
|
+
end
|
60
|
+
close_connection
|
61
|
+
rescue Exception => e
|
62
|
+
close_connection
|
63
|
+
raise e
|
64
|
+
end
|
65
|
+
|
66
|
+
def write data
|
67
|
+
send_data data
|
68
|
+
end
|
69
|
+
|
70
|
+
def closed?
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
class HttpServer
|
77
|
+
def initialize(host, port, num_processors=950, x=0, y=nil) # Deal with Mongrel 1.0.1 or earlier, as well as later.
|
78
|
+
@socket = nil
|
79
|
+
@classifier = URIClassifier.new
|
80
|
+
@host = host
|
81
|
+
@port = port
|
82
|
+
@workers = ThreadGroup.new
|
83
|
+
if y
|
84
|
+
@throttle = x
|
85
|
+
@timeout = y || 60
|
86
|
+
else
|
87
|
+
@timeout = x
|
88
|
+
end
|
89
|
+
@num_processors = num_processors #num_processors is pointless for evented....
|
90
|
+
@death_time = 60
|
91
|
+
self.class.const_set(:Instance,self)
|
92
|
+
end
|
93
|
+
|
94
|
+
def run
|
95
|
+
trap('INT') { raise StopServer }
|
96
|
+
trap('TERM') { raise StopServer }
|
97
|
+
@acceptor = Thread.new do
|
98
|
+
Packet::Reactor.run do |t_reactor|
|
99
|
+
begin
|
100
|
+
t_reactor.start_server(@host,@port,MongrelProtocol)
|
101
|
+
rescue StopServer
|
102
|
+
t_reactor.start_server
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def process_http_request(params,linebuffer,client)
|
109
|
+
if not params[Const::REQUEST_PATH]
|
110
|
+
uri = URI.parse(params[Const::REQUEST_URI])
|
111
|
+
params[Const::REQUEST_PATH] = uri.request_uri
|
112
|
+
end
|
113
|
+
|
114
|
+
raise "No REQUEST PATH" if not params[Const::REQUEST_PATH]
|
115
|
+
|
116
|
+
script_name, path_info, handlers = @classifier.resolve(params[Const::REQUEST_PATH])
|
117
|
+
|
118
|
+
if handlers
|
119
|
+
notifiers = handlers.select { |h| h.request_notify }
|
120
|
+
request = HttpRequest.new(params, linebuffer, notifiers)
|
121
|
+
|
122
|
+
# request is good so far, continue processing the response
|
123
|
+
response = HttpResponse.new(client)
|
124
|
+
|
125
|
+
# Process each handler in registered order until we run out or one finalizes the response.
|
126
|
+
dispatch_to_handlers(handlers,request,response)
|
127
|
+
|
128
|
+
# And finally, if nobody closed the response off, we finalize it.
|
129
|
+
unless response.done
|
130
|
+
response.finished
|
131
|
+
else
|
132
|
+
response.close_connection
|
133
|
+
end
|
134
|
+
else
|
135
|
+
# Didn't find it, return a stock 404 response.
|
136
|
+
client.send_data(Const::ERROR_404_RESPONSE)
|
137
|
+
client.close_connection
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def dispatch_to_handlers(handlers,request,response)
|
142
|
+
handlers.each do |handler|
|
143
|
+
handler.process(request, response)
|
144
|
+
break if response.done
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class HttpRequest
|
150
|
+
def initialize(params, linebuffer, dispatchers)
|
151
|
+
@params = params
|
152
|
+
@dispatchers = dispatchers
|
153
|
+
@body = linebuffer
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
class HttpResponse
|
158
|
+
def send_file(path, small_file = false)
|
159
|
+
File.open(path, "rb") do |f|
|
160
|
+
while chunk = f.read(Const::CHUNK_SIZE) and chunk.length > 0
|
161
|
+
begin
|
162
|
+
write(chunk)
|
163
|
+
rescue Object => exc
|
164
|
+
break
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
@body_sent = true
|
169
|
+
end
|
170
|
+
|
171
|
+
def write(data)
|
172
|
+
@socket.send_data data
|
173
|
+
end
|
174
|
+
|
175
|
+
def close_connection_after_writing
|
176
|
+
@socket.close_connection
|
177
|
+
end
|
178
|
+
|
179
|
+
def socket_error(details)
|
180
|
+
@socket.close_connection
|
181
|
+
done = true
|
182
|
+
raise details
|
183
|
+
end
|
184
|
+
|
185
|
+
def finished
|
186
|
+
send_status
|
187
|
+
send_header
|
188
|
+
send_body
|
189
|
+
@socket.close_connection
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
class Configurator
|
194
|
+
# This version fixes a bug in the regular Mongrel version by adding
|
195
|
+
# initialization of groups.
|
196
|
+
def change_privilege(user, group)
|
197
|
+
if user and group
|
198
|
+
log "Initializing groups for {#user}:{#group}."
|
199
|
+
Process.initgroups(user,Etc.getgrnam(group).gid)
|
200
|
+
end
|
201
|
+
|
202
|
+
if group
|
203
|
+
log "Changing group to #{group}."
|
204
|
+
Process::GID.change_privilege(Etc.getgrnam(group).gid)
|
205
|
+
end
|
206
|
+
|
207
|
+
if user
|
208
|
+
log "Changing user to #{user}."
|
209
|
+
Process::UID.change_privilege(Etc.getpwnam(user).uid)
|
210
|
+
end
|
211
|
+
rescue Errno::EPERM
|
212
|
+
log "FAILED to change user:group #{user}:#{group}: #$!"
|
213
|
+
exit 1
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
|
@@ -11,48 +11,54 @@ module Packet
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def read_data(t_sock)
|
14
|
-
sock_data =
|
14
|
+
sock_data = []
|
15
15
|
begin
|
16
|
-
while(t_data = t_sock.recv_nonblock(
|
17
|
-
raise DisconnectError.new(t_sock) if t_data.empty?
|
16
|
+
while(t_data = t_sock.recv_nonblock((16*1024)-1))
|
17
|
+
raise DisconnectError.new(t_sock,sock_data.join) if t_data.empty?
|
18
18
|
sock_data << t_data
|
19
19
|
end
|
20
20
|
rescue Errno::EAGAIN
|
21
|
-
return sock_data
|
21
|
+
return sock_data.join
|
22
22
|
rescue Errno::EWOULDBLOCK
|
23
|
-
return sock_data
|
23
|
+
return sock_data.join
|
24
24
|
rescue
|
25
|
-
raise DisconnectError.new(t_sock)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def write_data(p_data,p_sock)
|
30
|
-
return unless p_data
|
31
|
-
if p_data.is_a? Fixnum
|
32
|
-
t_data = p_data.to_s
|
33
|
-
else
|
34
|
-
t_data = p_data.dup.to_s
|
35
|
-
end
|
36
|
-
t_length = t_data.length
|
37
|
-
begin
|
38
|
-
p_sock.write_nonblock(t_data)
|
39
|
-
rescue Errno::EAGAIN
|
40
|
-
return
|
41
|
-
rescue Errno::EPIPE
|
42
|
-
raise DisconnectError.new(p_sock)
|
25
|
+
raise DisconnectError.new(t_sock,sock_data.join)
|
43
26
|
end
|
44
27
|
end
|
45
28
|
|
46
29
|
# method writes data to socket in a non blocking manner, but doesn't care if there is a error writing data
|
47
30
|
def write_once(p_data,p_sock)
|
48
|
-
t_data = p_data.
|
31
|
+
t_data = p_data.to_s
|
32
|
+
written_length = 0
|
33
|
+
data_length = t_data.length
|
49
34
|
begin
|
50
|
-
p_sock.write_nonblock(t_data)
|
35
|
+
written_length = p_sock.write_nonblock(t_data)
|
36
|
+
return "" if written_length == data_length
|
37
|
+
return t_data[written_length..-1]
|
51
38
|
rescue Errno::EAGAIN
|
52
|
-
return
|
39
|
+
return t_data[written_length..-1]
|
53
40
|
rescue Errno::EPIPE
|
54
41
|
raise DisconnectError.new(p_sock)
|
42
|
+
rescue Errno::ECONNRESET
|
43
|
+
raise DisconnectError.new(p_sock)
|
44
|
+
rescue
|
45
|
+
raise DisconnectError.new(p_sock)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# write the data in socket buffer and schedule the thing
|
50
|
+
def write_and_schedule sock
|
51
|
+
outbound_data.each_with_index do |t_data,index|
|
52
|
+
leftover = write_once(t_data,sock)
|
53
|
+
if leftover.empty?
|
54
|
+
outbound_data.delete_at(index)
|
55
|
+
else
|
56
|
+
outbound_data[index] = leftover
|
57
|
+
reactor.schedule_write(sock)
|
58
|
+
break
|
59
|
+
end
|
55
60
|
end
|
61
|
+
reactor.cancel_write(sock) if outbound_data.empty?
|
56
62
|
end
|
57
63
|
|
58
64
|
# method dumps the object in a protocol format which can be easily picked by a recursive descent parser
|
@@ -61,17 +67,11 @@ module Packet
|
|
61
67
|
dump_length = object_dump.length.to_s
|
62
68
|
length_str = dump_length.rjust(9,'0')
|
63
69
|
final_data = length_str + object_dump
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
rescue Errno::EINTR
|
70
|
-
puts "Interrupt error"
|
71
|
-
return
|
72
|
-
rescue Errno::EPIPE
|
73
|
-
puts "Pipe error"
|
74
|
-
raise DisconnectError.new(p_sock)
|
70
|
+
outbound_data << final_data
|
71
|
+
begin
|
72
|
+
write_and_schedule(p_sock)
|
73
|
+
rescue DisconnectError => sock
|
74
|
+
close_connection(sock)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
File without changes
|
File without changes
|
@@ -6,13 +6,15 @@ module Packet
|
|
6
6
|
iattr_accessor :pimp_name
|
7
7
|
attr_accessor :lifeline, :pid, :signature
|
8
8
|
attr_accessor :fd_write_end
|
9
|
-
attr_accessor :workers, :reactor
|
9
|
+
attr_accessor :workers, :reactor,:outbound_data
|
10
|
+
|
10
11
|
|
11
12
|
def initialize(lifeline_socket,worker_pid,p_reactor)
|
12
13
|
@lifeline = lifeline_socket
|
13
14
|
@pid = worker_pid
|
14
15
|
@reactor = p_reactor
|
15
16
|
@signature = Guid.hexdigest
|
17
|
+
@outbound_data = []
|
16
18
|
pimp_init if self.respond_to?(:pimp_init)
|
17
19
|
end
|
18
20
|
|
@@ -14,17 +14,16 @@ module Packet
|
|
14
14
|
# @fd_reader = args.shift if args.length > 2
|
15
15
|
@msg_writer = messengers[:write_end]
|
16
16
|
@msg_reader = messengers[:read_end]
|
17
|
-
@fd_reader = messengers[:read_fd]
|
18
17
|
t_instance = new
|
19
18
|
t_instance.worker_options = messengers[:options]
|
20
19
|
t_instance.worker_init if t_instance.respond_to?(:worker_init)
|
21
20
|
t_instance.start_reactor
|
21
|
+
t_instance
|
22
22
|
end
|
23
23
|
|
24
24
|
def initialize
|
25
25
|
super
|
26
26
|
@read_ios << msg_reader
|
27
|
-
@read_ios << fd_reader
|
28
27
|
@tokenizer = BinParser.new
|
29
28
|
end
|
30
29
|
|
@@ -61,7 +60,6 @@ module Packet
|
|
61
60
|
class << handler_instance
|
62
61
|
extend Forwardable
|
63
62
|
attr_accessor :worker, :connection, :reactor, :initialized, :signature
|
64
|
-
attr_accessor :thread_pool
|
65
63
|
include NbioHelper
|
66
64
|
include Connection
|
67
65
|
def_delegators :@reactor, :start_server, :connect, :add_periodic_timer, :add_timer, :cancel_timer,:reconnect
|
@@ -69,7 +67,6 @@ module Packet
|
|
69
67
|
handler_instance.connection = connection
|
70
68
|
handler_instance.worker = self
|
71
69
|
handler_instance.reactor = self
|
72
|
-
handler_instance.thread_pool = @thread_pool
|
73
70
|
end
|
74
71
|
|
75
72
|
def log log_data
|
@@ -3,6 +3,9 @@
|
|
3
3
|
Nothing more, nothing less.
|
4
4
|
=end
|
5
5
|
|
6
|
+
require "event"
|
7
|
+
require "packet_guid"
|
8
|
+
|
6
9
|
module Packet
|
7
10
|
class TimerStore
|
8
11
|
attr_accessor :order
|
@@ -16,15 +19,21 @@ module Packet
|
|
16
19
|
@container[int_time] ||= []
|
17
20
|
@container[int_time] << timer
|
18
21
|
|
19
|
-
if @container.empty?
|
22
|
+
if @container.empty? or @order.empty?
|
20
23
|
@order << int_time
|
21
24
|
return
|
22
25
|
end
|
23
|
-
|
26
|
+
|
27
|
+
|
28
|
+
if @order.last <= int_time
|
24
29
|
@order << int_time
|
25
30
|
else
|
26
|
-
index = bin_search_for_key(
|
27
|
-
@order
|
31
|
+
index = bin_search_for_key(0,@order.length - 1,int_time)
|
32
|
+
if(int_time < @order[index-1] && index != 0)
|
33
|
+
@order.insert(index-1,int_time)
|
34
|
+
else
|
35
|
+
@order.insert(index,int_time)
|
36
|
+
end
|
28
37
|
end
|
29
38
|
end
|
30
39
|
|
@@ -39,25 +48,24 @@ module Packet
|
|
39
48
|
bin_search_for_key(lower_index,pivot,key)
|
40
49
|
end
|
41
50
|
end
|
42
|
-
end
|
43
51
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
@
|
52
|
+
def each
|
53
|
+
@order.each_with_index do |x,i|
|
54
|
+
@container[x].each do |timer|
|
55
|
+
yield timer
|
56
|
+
end
|
57
|
+
# @container.delete(x) if @container[x].empty?
|
58
|
+
# @order.delete_at(i)
|
50
59
|
end
|
51
60
|
end
|
52
|
-
end
|
53
61
|
|
54
|
-
|
55
|
-
|
56
|
-
|
62
|
+
def delete(timer)
|
63
|
+
int_time = timer.scheduled_time.to_i
|
64
|
+
@container[int_time] && @container[int_time].delete(timer)
|
57
65
|
|
58
|
-
|
59
|
-
|
66
|
+
if(!@container[int_time] || @container[int_time].empty?)
|
67
|
+
@order.delete(timer)
|
68
|
+
end
|
60
69
|
end
|
61
70
|
end
|
62
71
|
end
|
63
|
-
|