packet 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
-