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.
@@ -4,7 +4,7 @@
4
4
  class Packet::MetaPimp < Packet::Pimp
5
5
  # initializer of pimp
6
6
  attr_accessor :callback_hash
7
- attr_accessor :worker_status, :worker_key
7
+ attr_accessor :worker_status, :worker_key,:worker_name
8
8
  def pimp_init
9
9
  @callback_hash ||= {}
10
10
  @worker_status = nil
@@ -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(1023))
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.dup.to_s
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
- begin
65
- p_sock.write_nonblock(final_data)
66
- rescue Errno::EAGAIN
67
- puts "EAGAIN Error while writing socket"
68
- return
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
@@ -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
- if @order.last <= key
26
+
27
+
28
+ if @order.last <= int_time
24
29
  @order << int_time
25
30
  else
26
- index = bin_search_for_key(o,@order.length - 1,int_time)
27
- @order.insert(index,int_time)
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
- def each
45
- @order.each_with_index do |x,i|
46
- if x <= Time.now.to_i
47
- @container[x].each { |timer| yield x }
48
- @container.delete(x)
49
- @order.delete_at(i)
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
- def delete(timer)
55
- int_time = timer.scheduled_time
56
- @container[int_time] && @container[int_time].delete(timer)
62
+ def delete(timer)
63
+ int_time = timer.scheduled_time.to_i
64
+ @container[int_time] && @container[int_time].delete(timer)
57
65
 
58
- if(!@container[int_time] || @container[int_time].empty?)
59
- @order.delete(timer)
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
-