spider-gazelle 2.0.4 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c58fe4be1489a50a6ccaa2ddc2f49593a8141060
4
- data.tar.gz: fa45ab4c055d22fa7feb9ab4307c9cd8c27b56d3
3
+ metadata.gz: 778791bf3b7c52fdfa82112ce9e1bf4b53df9223
4
+ data.tar.gz: 57b34f904238ae88fa5a2bb45c7110f366eaad1e
5
5
  SHA512:
6
- metadata.gz: afa593ea2c53a7417ef75868e8d72b0b2a5786f715049f0dffc9260fe9abdb4d813f29746dcd35af104774139d0dbcee25ca41cf0bd8f75a1c4728f4a1f297d2
7
- data.tar.gz: 48e07a08af8002346cda819620e0046d8c1c6669341d61a862b6490a32797f74dc29771ea4ea9852f11b4d040f07fe3d190ecdea7e6484b602ff7f109599a3a2
6
+ metadata.gz: 79445b4d863e03f59656d48f112ce565764cca4ebfb59ca915ad56de43342c54392b7fb71afe77894af4acc31952f12d4b3dcb1fe5505d9caac7f192507f1574
7
+ data.tar.gz: a20df55c3754354438a217350b2693067e23988722c0bbc0c3401d9bf23974fe702534c563a005743cb2ce34d60d956bf38c5ef085afaebfc7a1ca26686fe528
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thread'
2
4
  require 'singleton'
3
5
 
@@ -10,22 +12,22 @@ require 'spider-gazelle/signaller'
10
12
 
11
13
 
12
14
  module SpiderGazelle
13
- INTERNAL_PIPE_BACKLOG = 128
15
+ INTERNAL_PIPE_BACKLOG = 4096
14
16
 
15
17
  # Signaller is used to communicate:
16
18
  # * command line requests
17
19
  # * Startup and shutdown requests
18
20
  # * Live updates (bindings passed by this pipe)
19
- SIGNAL_SERVER = '/tmp/sg-signaller.pipe'.freeze
21
+ SIGNAL_SERVER = '/tmp/sg-signaller.pipe'
20
22
 
21
23
  # Spider server is used to
22
24
  # * Track gazelles
23
25
  # * Signal shutdown as required
24
26
  # * Pass sockets
25
- SPIDER_SERVER = '/tmp/sg-spider.pipe.'.freeze
27
+ SPIDER_SERVER = '/tmp/sg-spider.pipe.'
28
+
26
29
 
27
30
  MODES = [:process, :thread, :no_ipc].freeze
28
- APP_MODE = [:thread_pool, :fiber_pool, :libuv, :eventmachine, :celluloid]
29
31
 
30
32
 
31
33
  class LaunchControl
@@ -144,24 +146,24 @@ module SpiderGazelle
144
146
 
145
147
  if running
146
148
  if master[:spider]
147
- logger.verbose "Starting Spider".freeze
149
+ logger.verbose "Starting Spider"
148
150
  start_spider(signaller, logger, options)
149
151
  elsif master[:gazelle]
150
- logger.verbose "Starting Gazelle".freeze
152
+ logger.verbose "Starting Gazelle"
151
153
  start_gazelle(signaller, logger, options)
152
154
  else
153
- logger.verbose "Sending signal to SG Master".freeze
155
+ logger.verbose "Sending signal to SG Master"
154
156
  signal_master(reactor, signaller, logger, options)
155
157
  end
156
158
 
157
159
  elsif master[:debug]
158
- logger.verbose "SG is now running in debug mode".freeze
160
+ logger.verbose "SG is now running in debug mode"
159
161
  else
160
- logger.verbose "SG was not running, launching Spider".freeze
162
+ logger.verbose "SG was not running, launching Spider"
161
163
  launch_spider(@args)
162
164
  end
163
165
  rescue => e
164
- logger.verbose "Error performing requested operation".freeze
166
+ logger.verbose "Error performing requested operation"
165
167
  logger.print_error(e)
166
168
  shutdown
167
169
  end
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rack" # Ruby webserver abstraction
2
- require "rack/lock_patch" # Serialize execution in development mode
3
4
  require 'spider-gazelle/gazelle/app_store'
4
5
  require 'spider-gazelle/gazelle/http1'
5
6
 
@@ -10,7 +11,7 @@ require "spider-gazelle/upgrades/websocket"
10
11
 
11
12
  module SpiderGazelle
12
13
  class Gazelle
13
- SPACE = ' '.freeze
14
+ SPACE = ' '
14
15
 
15
16
  def initialize(thread, type)
16
17
  raise ArgumentError, "type must be one of #{MODES}" unless MODES.include?(type)
@@ -52,23 +53,13 @@ module SpiderGazelle
52
53
  socket = @pipe.check_pending
53
54
  return if socket.nil?
54
55
  process_connection(socket, data.to_i)
56
+ rescue => e
57
+ @logger.print_error(e)
55
58
  end
56
59
 
57
- def shutdown(finished = nil)
58
- # Wait for the requests to finish (give them 15 seconds)
59
- # TODO::
60
-
60
+ def shutdown
61
+ # Wait for the requests to finish
61
62
  @logger.verbose { "Gazelle: #{@type} Pid: #{Process.pid} shutting down" }
62
-
63
- # Then stop the current thread if we are in threaded mode
64
- if @type == :thread
65
- # In threaded mode the gazelle has the power
66
- @thread.stop
67
- else
68
- # Both no_ipc and process need to know when the requests
69
- # have completed to shutdown
70
- finished.resolve(true)
71
- end
72
63
  end
73
64
 
74
65
 
@@ -84,8 +75,8 @@ module SpiderGazelle
84
75
  authenticate
85
76
  end
86
77
 
87
- @pipe.catch do |error|
88
- @logger.print_error(error)
78
+ @pipe.catch do |error, backtrace|
79
+ @logger.print_error(error, String.new, backtrace)
89
80
  end
90
81
 
91
82
  @pipe.finally do
@@ -93,7 +84,7 @@ module SpiderGazelle
93
84
  Reactor.instance.shutdown
94
85
  else
95
86
  # Threaded mode
96
- shutdown
87
+ connect_to_spider
97
88
  end
98
89
  end
99
90
  end
@@ -141,7 +132,7 @@ module SpiderGazelle
141
132
  end
142
133
 
143
134
  def set_protocol(socket, version)
144
- app, app_mode, port, tls = socket.storage
135
+ app, port, tls = socket.storage
145
136
 
146
137
  parser = if version == :h2
147
138
  @http2_cache.pop || new_http2_parser
@@ -149,7 +140,7 @@ module SpiderGazelle
149
140
  @http1_cache.pop || new_http1_parser
150
141
  end
151
142
 
152
- parser.load(socket, port, app, app_mode, tls)
143
+ parser.load(socket, port, app, tls)
153
144
  socket.progress @on_progress
154
145
  socket.storage = parser
155
146
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thread'
2
4
 
3
5
  module SpiderGazelle
@@ -15,10 +17,11 @@ module SpiderGazelle
15
17
  return if @loaded[rackup]
16
18
 
17
19
  app, opts = ::Rack::Builder.parse_file(rackup)
20
+ app = Rack::Lint.new(app) if options[:lint]
18
21
  tls = configure_tls(options)
19
22
  port = tls ? 443 : 80
20
23
 
21
- val = [app, options[:app_mode], port, tls]
24
+ val = [app, port.to_s, tls]
22
25
  @apps << val
23
26
  @loaded[rackup] = val
24
27
  }
@@ -40,7 +43,7 @@ module SpiderGazelle
40
43
  tls = configure_tls(options)
41
44
  port = tls ? 443 : 80
42
45
 
43
- val = [app, options[:app_mode], port, tls]
46
+ val = [app, port.to_s, tls]
44
47
  @apps << val
45
48
  @loaded[obj_id] = val
46
49
 
@@ -58,8 +61,8 @@ module SpiderGazelle
58
61
  @apps[id.to_i]
59
62
  end
60
63
 
61
- PROTOCOLS = ['h2'.freeze, 'http/1.1'.freeze].freeze
62
- FALLBACK = 'http/1.1'.freeze
64
+ PROTOCOLS = ['h2', 'http/1.1'].freeze
65
+ FALLBACK = 'http/1.1'
63
66
  def self.configure_tls(opts)
64
67
  return false unless opts[:tls]
65
68
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  require 'http-parser' # C based, fast, http parser
3
4
  require 'spider-gazelle/gazelle/request'
@@ -30,10 +31,10 @@ module SpiderGazelle
30
31
  req.header.frozen? ? req.header = header : req.header << header
31
32
  end
32
33
 
33
- DASH = '-'.freeze
34
- UNDERSCORE = '_'.freeze
35
- HTTP_META = 'HTTP_'.freeze
36
- COMMA = ', '.freeze
34
+ DASH = '-'
35
+ UNDERSCORE = '_'
36
+ HTTP_META = 'HTTP_'
37
+ COMMA = ', '
37
38
 
38
39
  def on_header_value(parser, value)
39
40
  req = @connection.parsing
@@ -49,7 +50,7 @@ module SpiderGazelle
49
50
  req.env[header] << COMMA
50
51
  req.env[header] << value
51
52
  else
52
- req.env[header] = value
53
+ req.env[header] = String.new(value)
53
54
  end
54
55
  end
55
56
  end
@@ -78,18 +79,8 @@ module SpiderGazelle
78
79
  @thread = thread
79
80
  @logger = logger
80
81
 
81
- @work_complete = proc { |request, result|
82
- if request.is_async && !request.hijacked
83
- if result.is_a?(Fixnum) && !request.defer.resolved?
84
- # TODO:: setup timeout for async response
85
- end
86
- else
87
- # Complete the current request
88
- request.defer.resolve(result)
89
- end
90
- }
91
-
92
- @queue_response = method(:queue_response)
82
+ @queue_response = method :queue_response
83
+ @write_chunk = method :write_chunk
93
84
 
94
85
  # The parser state for this instance
95
86
  @state = ::HttpParser::Parser.new_instance do |inst|
@@ -108,28 +99,13 @@ module SpiderGazelle
108
99
  def self.on_progress(data, socket); end
109
100
  DUMMY_PROGRESS = self.method :on_progress
110
101
 
111
- HTTP = 'http'.freeze
112
- HTTPS = 'https'.freeze
102
+ HTTP = 'http'
103
+ HTTPS = 'https'
113
104
 
114
- def load(socket, port, app, app_mode, tls)
105
+ def load(socket, port, app, tls)
115
106
  @socket = socket
116
107
  @port = port
117
108
  @app = app
118
- @mode = app_mode
119
-
120
- case @mode
121
- when :thread_pool
122
- @exec = method :exec_on_thread_pool
123
- when :fiber_pool
124
- # TODO:: Implement these modes
125
- @exec = method :critical_error
126
- when :libuv
127
- @exec = method :critical_error
128
- when :eventmachine
129
- @exec = method :critical_error
130
- when :celluloid
131
- @exec = method :critical_error
132
- end
133
109
 
134
110
  @remote_ip = socket.peername[0]
135
111
  @scheme = tls ? HTTPS : HTTP
@@ -182,15 +158,15 @@ module SpiderGazelle
182
158
  # Parser Callbacks
183
159
  # ----------------
184
160
  def start_parsing
185
- @parsing = Request.new @thread, @app, @port, @remote_ip, @scheme
161
+ @parsing = Request.new @thread, @app, @port, @remote_ip, @scheme, @socket
186
162
  end
187
163
 
188
- REQUEST_METHOD = 'REQUEST_METHOD'.freeze
164
+ REQUEST_METHOD = 'REQUEST_METHOD'
189
165
  def headers_complete
190
166
  @parsing.env[REQUEST_METHOD] = @state.http_method.to_s
191
167
  end
192
168
 
193
- ASYNC = "async.callback".freeze
169
+ ASYNC = "async.callback"
194
170
  def finished_parsing
195
171
  request = @parsing
196
172
  @parsing = nil
@@ -205,52 +181,54 @@ module SpiderGazelle
205
181
  # See: http://polycrystal.org/2012/04/15/asynchronous_responses_in_rack.html
206
182
  # Process a response that was marked as async.
207
183
  request.env[ASYNC] = proc { |data|
208
- @thread.schedule { request.defer.resolve(data) }
184
+ @thread.schedule { request.defer.resolve([request, data]) }
209
185
  }
210
186
  request.upgrade = @state.upgrade?
211
187
  @requests << request
212
- process_next unless @processing
188
+
189
+ unless @processing
190
+ ::Fiber.new { process_next }.resume
191
+ end
213
192
  end
214
193
 
215
194
  # ------------------
216
195
  # Request Processing
217
196
  # ------------------
197
+ EMPTY_RESPONSE = [''].freeze
218
198
  def process_next
219
199
  @processing = @requests.shift
220
200
  if @processing
221
- @exec.call
222
- @processing.then @queue_response
223
- end
224
- end
225
-
226
- WORKER_ERROR = proc { |error|
227
- Logger.instance.print_error error, 'critical error'
228
- Reactor.instance.shutdown
229
- }
230
- def exec_on_thread_pool
231
- request = @processing
232
- promise = @thread.work { work(request) }
233
- promise.then @work_complete
234
- promise.catch WORKER_ERROR
235
- end
201
+ request = @processing
202
+ begin
203
+ result = begin
204
+ request.execute!
205
+ rescue StandardError => e
206
+ @logger.print_error e, 'framework error'
207
+ @processing.keep_alive = false
208
+ [500, {}, EMPTY_RESPONSE]
209
+ end
236
210
 
237
- EMPTY_RESPONSE = [''.freeze].freeze
238
- def work(request)
239
- begin
240
- [request, request.execute!]
241
- rescue StandardError => e
242
- @logger.print_error e, 'framework error'
243
- @processing.keep_alive = false
244
- [request, [500, {}, EMPTY_RESPONSE]]
211
+ if request.is_async && !request.hijacked
212
+ if result.nil? && !request.defer.resolved?
213
+ # TODO:: setup timeout for async response
214
+ end
215
+ else
216
+ # Complete the current request
217
+ request.defer.resolve([request, result])
218
+ end
219
+ request.then @queue_response
220
+ rescue Exception => error
221
+ Logger.instance.print_error error, 'critical error'
222
+ Reactor.instance.shutdown
223
+ end
245
224
  end
246
225
  end
247
226
 
248
-
249
227
  # ----------------
250
228
  # Response Sending
251
229
  # ----------------
252
- def queue_response(result)
253
- @responses << [@processing, result]
230
+ def queue_response(response)
231
+ @responses << response
254
232
  send_next_response unless @transmitting
255
233
 
256
234
  # Processing will be set to nil if the array is empty
@@ -258,14 +236,14 @@ module SpiderGazelle
258
236
  end
259
237
 
260
238
 
261
- HEAD = 'HEAD'.freeze
262
- ETAG = 'ETag'.freeze
263
- HTTP_ETAG = 'HTTP_ETAG'.freeze
264
- CONTENT_LENGTH2 = 'Content-Length'.freeze
265
- TRANSFER_ENCODING = 'Transfer-Encoding'.freeze
266
- CHUNKED = 'chunked'.freeze
267
- ZERO = '0'.freeze
268
- NOT_MODIFIED_304 = "HTTP/1.1 304 Not Modified\r\n".freeze
239
+ HEAD = 'HEAD'
240
+ ETAG = 'ETag'
241
+ HTTP_ETAG = 'HTTP_ETAG'
242
+ CONTENT_LENGTH2 = 'Content-Length'
243
+ TRANSFER_ENCODING = 'Transfer-Encoding'
244
+ CHUNKED = 'chunked'
245
+ ZERO = '0'
246
+ NOT_MODIFIED_304 = "HTTP/1.1 304 Not Modified\r\n"
269
247
 
270
248
  def send_next_response
271
249
  request, result = @responses.shift
@@ -361,7 +339,7 @@ module SpiderGazelle
361
339
  # Optimize the response
362
340
  begin
363
341
  if body.size < 2
364
- headers[CONTENT_LENGTH2] = body.size == 1 ? body[0].bytesize : ZERO
342
+ headers[CONTENT_LENGTH2] = body.size == 1 ? body[0].bytesize.to_s : ZERO
365
343
  end
366
344
  rescue # just in case
367
345
  end
@@ -381,7 +359,7 @@ module SpiderGazelle
381
359
  end
382
360
  end
383
361
 
384
- CLOSE_CHUNKED = "0\r\n\r\n".freeze
362
+ CLOSE_CHUNKED = "0\r\n\r\n"
385
363
  def write_response(request, status, headers, body)
386
364
  keep_alive = request.keep_alive
387
365
 
@@ -397,7 +375,6 @@ module SpiderGazelle
397
375
  write_headers keep_alive, status, headers
398
376
 
399
377
  # Stream the response
400
- @write_chunk ||= method :write_chunk
401
378
  body.each &@write_chunk
402
379
 
403
380
  @socket.write CLOSE_CHUNKED
@@ -407,8 +384,8 @@ module SpiderGazelle
407
384
  body.close if body.respond_to?(:close)
408
385
  end
409
386
 
410
- COLON_SPACE = ': '.freeze
411
- LINE_END = "\r\n".freeze
387
+ COLON_SPACE = ': '
388
+ LINE_END = "\r\n"
412
389
  def add_header(header, key, value)
413
390
  header << key
414
391
  header << COLON_SPACE
@@ -416,14 +393,14 @@ module SpiderGazelle
416
393
  header << LINE_END
417
394
  end
418
395
 
419
- CONNECTION = "Connection".freeze
420
- NEWLINE = "\n".freeze
421
- CLOSE = "close".freeze
422
- RACK = "rack".freeze
396
+ CONNECTION = "Connection"
397
+ NEWLINE = "\n"
398
+ CLOSE = "close"
399
+ RACK = "rack"
423
400
  def write_headers(keep_alive, status, headers)
424
401
  headers[CONNECTION] = CLOSE if keep_alive == false
425
402
 
426
- header = "HTTP/1.1 #{status} #{fetch_code(status)}\r\n"
403
+ header = String.new("HTTP/1.1 #{status} #{fetch_code(status)}\r\n")
427
404
  headers.each do |key, value|
428
405
  next if key.start_with? RACK
429
406
  value.to_s.split(NEWLINE).each {|val| add_header(header, key, val)}
@@ -439,7 +416,7 @@ module SpiderGazelle
439
416
  end
440
417
 
441
418
  HTTP_STATUS_CODES = Rack::Utils::HTTP_STATUS_CODES
442
- HTTP_STATUS_DEFAULT = proc { 'CUSTOM'.freeze }
419
+ HTTP_STATUS_DEFAULT = proc { 'CUSTOM' }
443
420
  def fetch_code(status)
444
421
  HTTP_STATUS_CODES.fetch(status, &HTTP_STATUS_DEFAULT)
445
422
  end
@@ -469,7 +446,7 @@ module SpiderGazelle
469
446
  end
470
447
  end
471
448
 
472
- ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-Length: 0\r\n\r\n".freeze
449
+ ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-Length: 0\r\n\r\n"
473
450
  def send_parsing_error
474
451
  @logger.info "Parsing error!"
475
452
  @socket.stop_read
@@ -477,7 +454,7 @@ module SpiderGazelle
477
454
  @socket.shutdown
478
455
  end
479
456
 
480
- ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\nContent-Length: 0\r\n\r\n".freeze
457
+ ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\nContent-Length: 0\r\n\r\n"
481
458
  def send_internal_error
482
459
  @logger.info "Internal error"
483
460
  @socket.stop_read