spider-gazelle 1.2.0 → 2.0.0
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.
- checksums.yaml +4 -4
- data/bin/sg +1 -64
- data/lib/rack/handler/spider-gazelle.rb +17 -26
- data/lib/rack/lock_patch.rb +27 -27
- data/lib/spider-gazelle.rb +165 -16
- data/lib/spider-gazelle/gazelle.rb +151 -134
- data/lib/spider-gazelle/gazelle/app_store.rb +86 -0
- data/lib/spider-gazelle/gazelle/http1.rb +496 -0
- data/lib/spider-gazelle/gazelle/request.rb +155 -0
- data/lib/spider-gazelle/logger.rb +122 -0
- data/lib/spider-gazelle/options.rb +213 -0
- data/lib/spider-gazelle/reactor.rb +69 -0
- data/lib/spider-gazelle/signaller.rb +214 -0
- data/lib/spider-gazelle/signaller/signal_parser.rb +66 -0
- data/lib/spider-gazelle/spider.rb +305 -343
- data/lib/spider-gazelle/spider/binding.rb +80 -0
- data/lib/spider-gazelle/upgrades/websocket.rb +92 -88
- data/spec/http1_spec.rb +173 -0
- data/spec/rack_lock_spec.rb +97 -97
- data/spider-gazelle.gemspec +6 -6
- metadata +24 -17
- data/lib/spider-gazelle/app_store.rb +0 -64
- data/lib/spider-gazelle/binding.rb +0 -53
- data/lib/spider-gazelle/connection.rb +0 -371
- data/lib/spider-gazelle/const.rb +0 -206
- data/lib/spider-gazelle/request.rb +0 -103
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 883af96ffb1054dc17b7f623ca7a6a1b6afe48cf
|
4
|
+
data.tar.gz: 9dda42f38e4cb802d4c6a11632438c16c2868cde
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 848ba774b73a281f074505d8a0c92e8742e78cc6608ed441e283497f26fdfda6b3f992547b2df4eae4960cb3649e93ca135ffdf0173e7b97e22c67f193e3f8ab
|
7
|
+
data.tar.gz: 170c737ed7502cbff903f1ef8d04f8050d0632471d50077384125f284608b5362fe3585e8c823faa30973d21f0fb55901154c579c3c861ebd724d32190f0b2d6
|
data/bin/sg
CHANGED
@@ -1,67 +1,4 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
3
|
require 'spider-gazelle'
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
options = {
|
9
|
-
Host: "0.0.0.0",
|
10
|
-
Port: 3000,
|
11
|
-
environment: ENV['RACK_ENV'] || 'development',
|
12
|
-
rackup: "#{Dir.pwd}/config.ru",
|
13
|
-
Verbose: false
|
14
|
-
}
|
15
|
-
|
16
|
-
parser = OptionParser.new do |opts|
|
17
|
-
opts.on "-p", "--port PORT", Integer, "Define what port TCP port to bind to (default: 3000)" do |arg|
|
18
|
-
options[:Port] = arg
|
19
|
-
end
|
20
|
-
|
21
|
-
opts.on "-a", "--address HOST", "bind to HOST address (default: 0.0.0.0)" do |arg|
|
22
|
-
options[:Host] = arg
|
23
|
-
end
|
24
|
-
|
25
|
-
opts.on "-v", "--verbose", "loud output" do
|
26
|
-
options[:Verbose] = true
|
27
|
-
end
|
28
|
-
|
29
|
-
opts.on "-e", "--environment ENVIRONMENT", "The environment to run the Rack app on (default: development)" do |arg|
|
30
|
-
options[:environment] = arg
|
31
|
-
end
|
32
|
-
|
33
|
-
opts.on "-r", "--rackup FILE", "Load Rack config from this file (default: config.ru)" do |arg|
|
34
|
-
options[:rackup] = arg
|
35
|
-
end
|
36
|
-
|
37
|
-
opts.on "-l", "--log FILE", "Location of the servers log file (default: logs/server.log)" do |arg|
|
38
|
-
ENV['SG_LOG'] = arg
|
39
|
-
end
|
40
|
-
|
41
|
-
opts.on "-m", "--mode MODE", "Either thread, process or no_ipc (default: thread)" do |arg|
|
42
|
-
ENV['SG_MODE'] = arg
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
parser.banner = "sg <options> <rackup file>"
|
47
|
-
parser.on_tail "-h", "--help", "Show help" do
|
48
|
-
puts parser
|
49
|
-
exit 1
|
50
|
-
end
|
51
|
-
|
52
|
-
parser.parse!(ARGV)
|
53
|
-
|
54
|
-
if ARGV.last =~ /\.ru$/
|
55
|
-
options[:rackup] = ARGV.shift
|
56
|
-
end
|
57
|
-
|
58
|
-
unless File.exists?(options[:rackup])
|
59
|
-
abort "No rackup found at #{options[:rackup]}"
|
60
|
-
end
|
61
|
-
|
62
|
-
# Force process mode on Windows (sockets over pipes not working at the moment)
|
63
|
-
ENV['SG_MODE'] = 'no_ipc' if ::FFI::Platform.windows?
|
64
|
-
|
65
|
-
::SpiderGazelle::Spider.run options[:rackup], options
|
66
|
-
|
67
|
-
puts "\nSpider-Gazelle leaps through the veldt"
|
4
|
+
SpiderGazelle::LaunchControl.instance.exec(ARGV)
|
@@ -1,37 +1,28 @@
|
|
1
1
|
require "rack/handler"
|
2
2
|
require "spider-gazelle"
|
3
|
-
require "spider-gazelle/const"
|
4
3
|
|
5
4
|
module Rack
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
module Handler
|
6
|
+
module SpiderGazelle
|
7
|
+
def self.run(app, options = {})
|
8
|
+
|
9
|
+
# Replace the rackup with app
|
10
|
+
options = ::SpiderGazelle::Options::DEFAULTS.merge(options)
|
11
|
+
options.delete(:rackup)
|
12
|
+
options[:app] = app
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
# Can't pass an object over a pipe
|
15
|
+
options[:isolate] = true
|
16
|
+
options[:mode] = :thread if options[:mode] == :process
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
# Ensure the environment is set
|
19
|
+
options[:environment] ||= ENV['RACK_ENV'] || 'development'
|
20
|
+
ENV['RACK_ENV'] = options[:environment]
|
20
21
|
|
21
|
-
|
22
|
-
|
22
|
+
::SpiderGazelle::LaunchControl.instance.launch([options])
|
23
|
+
end
|
23
24
|
end
|
24
25
|
|
25
|
-
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.valid_options
|
29
|
-
{ "Host=HOST" => "Hostname to listen on (default: 0.0.0.0)",
|
30
|
-
"Port=PORT" => "Port to listen on (default: 8080)",
|
31
|
-
"Quiet" => "Don't report each request" }
|
32
|
-
end
|
26
|
+
register :"spider-gazelle", SpiderGazelle
|
33
27
|
end
|
34
|
-
|
35
|
-
register :"spider-gazelle", SpiderGazelle
|
36
|
-
end
|
37
28
|
end
|
data/lib/rack/lock_patch.rb
CHANGED
@@ -4,36 +4,36 @@ require 'rack/body_proxy'
|
|
4
4
|
require 'rack/lock' # ensure this loads first
|
5
5
|
|
6
6
|
module Rack
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
# Rack::Lock locks every request inside a mutex, so that every request
|
8
|
+
# will effectively be executed synchronously.
|
9
|
+
class Lock
|
10
|
+
# FLAG = 'rack.multithread'.freeze # defined in rack/lock
|
11
|
+
RACK_MULTITHREAD ||= FLAG
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
def initialize(app, mutex = Mutex.new)
|
14
|
+
@app, @mutex = app, mutex
|
15
|
+
@sig = ConditionVariable.new
|
16
|
+
@count = 0
|
17
|
+
end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
19
|
+
def call(env)
|
20
|
+
@mutex.lock
|
21
|
+
@count += 1
|
22
|
+
@sig.wait(@mutex) if @count > 1
|
23
|
+
response = @app.call(env.merge(RACK_MULTITHREAD => false))
|
24
|
+
returned = response << BodyProxy.new(response.pop) {
|
25
|
+
@mutex.synchronize { unlock }
|
26
|
+
}
|
27
|
+
ensure
|
28
|
+
unlock unless returned
|
29
|
+
@mutex.unlock
|
30
|
+
end
|
31
31
|
|
32
|
-
|
32
|
+
private
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
def unlock
|
35
|
+
@count -= 1
|
36
|
+
@sig.signal
|
37
|
+
end
|
37
38
|
end
|
38
|
-
end
|
39
39
|
end
|
data/lib/spider-gazelle.rb
CHANGED
@@ -1,22 +1,171 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require "rack" # Ruby webserver abstraction
|
4
|
-
require "rack/lock_patch" # Serialize execution in development mode
|
1
|
+
require 'thread'
|
2
|
+
require 'singleton'
|
5
3
|
|
6
|
-
require "spider-gazelle/request" # Holds request information and handles request processing
|
7
|
-
require "spider-gazelle/connection" # Holds connection information and handles request pipelining
|
8
|
-
require "spider-gazelle/gazelle" # Processes data received from connections
|
9
4
|
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
5
|
+
require 'spider-gazelle/options'
|
6
|
+
require 'spider-gazelle/logger'
|
7
|
+
require 'spider-gazelle/reactor'
|
8
|
+
require 'spider-gazelle/signaller'
|
13
9
|
|
14
|
-
# Reactor aware websocket implementation
|
15
|
-
require "spider-gazelle/upgrades/websocket"
|
16
10
|
|
17
11
|
module SpiderGazelle
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
12
|
+
VERSION = '2.0.0'.freeze
|
13
|
+
EXEC_NAME = 'sg'.freeze
|
14
|
+
INTERNAL_PIPE_BACKLOG = 128
|
15
|
+
|
16
|
+
# Signaller is used to communicate:
|
17
|
+
# * command line requests
|
18
|
+
# * Startup and shutdown requests
|
19
|
+
# * Live updates (bindings passed by this pipe)
|
20
|
+
SIGNAL_SERVER = '/tmp/sg-signaller.pipe'.freeze
|
21
|
+
|
22
|
+
# Spider server is used to
|
23
|
+
# * Track gazelles
|
24
|
+
# * Signal shutdown as required
|
25
|
+
# * Pass sockets
|
26
|
+
SPIDER_SERVER = '/tmp/sg-spider.pipe.'.freeze
|
27
|
+
|
28
|
+
MODES = [:process, :thread, :no_ipc].freeze
|
29
|
+
APP_MODE = [:thread_pool, :fiber_pool, :libuv, :eventmachine, :celluloid]
|
30
|
+
|
31
|
+
|
32
|
+
class LaunchControl
|
33
|
+
include Singleton
|
34
|
+
|
35
|
+
|
36
|
+
attr_reader :password, :args
|
37
|
+
|
38
|
+
|
39
|
+
def exec(args)
|
40
|
+
options = SpiderGazelle::Options.sanitize(args)
|
41
|
+
@args = args
|
42
|
+
launch(options)
|
43
|
+
end
|
44
|
+
|
45
|
+
def launch(options)
|
46
|
+
# Enable verbose messages if requested
|
47
|
+
Logger.instance.verbose! if options[0][:verbose]
|
48
|
+
|
49
|
+
# Start the Libuv Event Loop
|
50
|
+
reactor = ::SpiderGazelle::Reactor.instance
|
51
|
+
reactor.run do
|
52
|
+
|
53
|
+
# Check if SG is already running
|
54
|
+
signaller = ::SpiderGazelle::Signaller.instance
|
55
|
+
|
56
|
+
if options[0][:isolate]
|
57
|
+
# This ensures this process will load the spider code
|
58
|
+
options[0][:spider] = true
|
59
|
+
boot(true, signaller, options)
|
60
|
+
else
|
61
|
+
signaller.check.then do |running|
|
62
|
+
boot(running, signaller, options)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def shutdown
|
69
|
+
reactor = Reactor.instance
|
70
|
+
reactor.thread.schedule do
|
71
|
+
reactor.shutdown
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
# ---------------------------------------
|
77
|
+
# SPIDER LAUNCH CONTROL
|
78
|
+
# ---------------------------------------
|
79
|
+
def launch_spider(args)
|
80
|
+
require 'securerandom'
|
81
|
+
require 'thread'
|
82
|
+
|
83
|
+
@password ||= SecureRandom.hex
|
84
|
+
#cmd = "#{EXEC_NAME} -s #{@password} #{Shellwords.join(args)}"
|
85
|
+
cmd = [EXEC_NAME, '-s', @password] + args
|
86
|
+
|
87
|
+
Thread.new do
|
88
|
+
result = system(*cmd)
|
89
|
+
|
90
|
+
# TODO:: We need to detect a failed load
|
91
|
+
# This is a little more tricky as spiders
|
92
|
+
# may come and go without this process exiting
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# This is called when a spider process starts
|
97
|
+
def start_spider(signaller, logger, options)
|
98
|
+
logger.set_client signaller.pipe unless options[0][:isolate]
|
99
|
+
|
100
|
+
require 'spider-gazelle/spider'
|
101
|
+
Spider.instance.run!(options)
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
# ---------------------------------------
|
107
|
+
# GAZELLE LAUNCH CONTROL
|
108
|
+
# ---------------------------------------
|
109
|
+
def start_gazelle(signaller, logger, options)
|
110
|
+
logger.set_client signaller.pipe
|
111
|
+
|
112
|
+
require 'spider-gazelle/gazelle'
|
113
|
+
::SpiderGazelle::Gazelle.new(logger.thread, :process).run!(options)
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
# ---------------------------------------
|
118
|
+
# TTY SIGNALLING CONTROL
|
119
|
+
# ---------------------------------------
|
120
|
+
def signal_master(reactor, signaller, logger, options)
|
121
|
+
# This is a signal request
|
122
|
+
promise = signaller.request(options)
|
123
|
+
|
124
|
+
promise.then do |result|
|
125
|
+
logger.info "signal recieved #{result}"
|
126
|
+
end
|
127
|
+
promise.catch do |error|
|
128
|
+
logger.info "there was an error #{error}"
|
129
|
+
end
|
130
|
+
promise.finally do
|
131
|
+
reactor.shutdown
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
protected
|
137
|
+
|
138
|
+
|
139
|
+
def boot(running, signaller, options)
|
140
|
+
logger = ::SpiderGazelle::Logger.instance
|
141
|
+
|
142
|
+
begin
|
143
|
+
# What do we want to do?
|
144
|
+
master = options[0]
|
145
|
+
|
146
|
+
if running
|
147
|
+
if master[:spider]
|
148
|
+
logger.verbose "Starting Spider".freeze
|
149
|
+
start_spider(signaller, logger, options)
|
150
|
+
elsif master[:gazelle]
|
151
|
+
logger.verbose "Starting Gazelle".freeze
|
152
|
+
start_gazelle(signaller, logger, options)
|
153
|
+
else
|
154
|
+
logger.verbose "Sending signal to SG Master".freeze
|
155
|
+
signal_master(reactor, signaller, logger, options)
|
156
|
+
end
|
157
|
+
|
158
|
+
elsif master[:debug]
|
159
|
+
logger.verbose "SG is now running in debug mode".freeze
|
160
|
+
else
|
161
|
+
logger.verbose "SG was not running, launching Spider".freeze
|
162
|
+
launch_spider(@args)
|
163
|
+
end
|
164
|
+
rescue => e
|
165
|
+
logger.verbose "Error performing requested operation".freeze
|
166
|
+
logger.print_error(e)
|
167
|
+
shutdown
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
22
171
|
end
|
@@ -1,162 +1,179 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "rack" # Ruby webserver abstraction
|
2
|
+
require "rack/lock_patch" # Serialize execution in development mode
|
3
|
+
require 'spider-gazelle/gazelle/app_store'
|
4
|
+
require 'spider-gazelle/gazelle/http1'
|
3
5
|
|
4
|
-
module SpiderGazelle
|
5
|
-
class Gazelle
|
6
|
-
include Const
|
7
6
|
|
8
|
-
|
7
|
+
# Reactor aware websocket implementation
|
8
|
+
require "spider-gazelle/upgrades/websocket"
|
9
9
|
|
10
|
-
def set_instance_type(inst)
|
11
|
-
inst.type = :request
|
12
|
-
end
|
13
10
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
11
|
+
module SpiderGazelle
|
12
|
+
class Gazelle
|
13
|
+
SPACE = ' '.freeze
|
14
|
+
|
15
|
+
def initialize(thread, type)
|
16
|
+
raise ArgumentError, "type must be one of #{MODES}" unless MODES.include?(type)
|
17
|
+
|
18
|
+
@type = type
|
19
|
+
@logger = Logger.instance
|
20
|
+
@thread = thread
|
21
|
+
|
22
|
+
@http1_cache = []
|
23
|
+
@http2_cache = []
|
24
|
+
@return_http1 = method(:return_http1)
|
25
|
+
@return_http2 = method(:return_http2)
|
26
|
+
@parser_count = 0
|
27
|
+
|
28
|
+
@on_progress = method(:on_progress)
|
29
|
+
@set_protocol = method(:set_protocol)
|
30
|
+
|
31
|
+
# Register the gazelle with the signaller so we can shutdown elegantly
|
32
|
+
if @type == :process
|
33
|
+
Signaller.instance.gazelle = self
|
34
|
+
end
|
35
|
+
end
|
20
36
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
@connection_queue = ::Libuv::Q::ResolvedPromise.new @gazelle, true
|
37
|
+
def run!(options)
|
38
|
+
@options = options
|
39
|
+
@logger.verbose { "Gazelle: #{@type} Pid: #{Process.pid} started" }
|
25
40
|
|
26
|
-
|
27
|
-
@parser = ::HttpParser::Parser.new self
|
28
|
-
@set_instance_type = method :set_instance_type
|
41
|
+
connect_to_spider unless @type == :no_ipc
|
29
42
|
|
30
|
-
|
31
|
-
|
32
|
-
|
43
|
+
load_required_applications
|
44
|
+
self
|
45
|
+
end
|
33
46
|
|
34
|
-
|
35
|
-
|
36
|
-
logger.progress do |level, errorid, error|
|
37
|
-
begin
|
38
|
-
msg = "Gazelle log: #{level}: #{errorid}\n#{error.message}\n#{error.backtrace.join("\n") if error.backtrace}\n"
|
39
|
-
@logger.error msg
|
40
|
-
rescue Exception
|
41
|
-
puts 'error in gazelle logger'
|
42
|
-
end
|
47
|
+
def new_app(options)
|
48
|
+
# TODO:: load this app into all of the gazelles dynamically
|
43
49
|
end
|
44
50
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
@socket_server.progress &method(:new_connection)
|
50
|
-
@socket_server.start_read
|
51
|
-
end
|
52
|
-
|
53
|
-
# A pipe used to signal various control commands (shutdown, etc)
|
54
|
-
@signal_server = @gazelle.pipe
|
55
|
-
@signal_server.connect(SIGNAL_PIPE) do
|
56
|
-
@signal_server.progress &method(:process_signal)
|
57
|
-
@signal_server.start_read
|
58
|
-
end
|
51
|
+
def new_connection(data, binding)
|
52
|
+
socket = @pipe.check_pending
|
53
|
+
return if socket.nil?
|
54
|
+
process_connection(socket, data.to_i)
|
59
55
|
end
|
60
|
-
end
|
61
|
-
end
|
62
56
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
57
|
+
def shutdown(finished = nil)
|
58
|
+
# Wait for the requests to finish (give them 15 seconds)
|
59
|
+
# TODO::
|
60
|
+
|
61
|
+
@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
|
+
end
|
67
73
|
|
68
|
-
def on_url(parser, url)
|
69
|
-
@connection.parsing.url << url
|
70
|
-
end
|
71
74
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
75
|
+
protected
|
76
|
+
|
77
|
+
|
78
|
+
def connect_to_spider
|
79
|
+
@pipe = @thread.pipe :ipc
|
80
|
+
@pipe.connect(@options[0][:gazelle_ipc]) do |client|
|
81
|
+
client.progress method(:new_connection)
|
82
|
+
client.start_read
|
83
|
+
|
84
|
+
authenticate
|
85
|
+
end
|
76
86
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
req.env[header] << value
|
90
|
-
else
|
91
|
-
req.env[header] = value
|
87
|
+
@pipe.catch do |error|
|
88
|
+
@logger.print_error(error)
|
89
|
+
end
|
90
|
+
|
91
|
+
@pipe.finally do
|
92
|
+
if @type == :process
|
93
|
+
Reactor.instance.shutdown
|
94
|
+
else
|
95
|
+
# Threaded mode
|
96
|
+
shutdown
|
97
|
+
end
|
98
|
+
end
|
92
99
|
end
|
93
|
-
end
|
94
|
-
end
|
95
100
|
|
96
|
-
|
97
|
-
|
98
|
-
|
101
|
+
def authenticate
|
102
|
+
@pipe.write "#{@options[0][:gazelle]} #{@type}"
|
103
|
+
end
|
99
104
|
|
100
|
-
|
101
|
-
|
102
|
-
|
105
|
+
def load_required_applications
|
106
|
+
@options.each do |app|
|
107
|
+
if app[:rackup]
|
108
|
+
AppStore.load(app[:rackup], app)
|
109
|
+
elsif app[:app]
|
110
|
+
AppStore.add(app[:app], app)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
103
114
|
|
104
|
-
def on_message_complete(parser)
|
105
|
-
@connection.finished_parsing
|
106
|
-
end
|
107
115
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
116
|
+
# ---------------------
|
117
|
+
# Connection Management
|
118
|
+
# ---------------------
|
119
|
+
def process_connection(socket, app_id)
|
120
|
+
# Put application details in the socket storage as we negotiate protocols
|
121
|
+
details = AppStore.get(app_id)
|
122
|
+
socket.storage = details
|
123
|
+
tls = details[-1]
|
124
|
+
|
125
|
+
# Hook up the socket and kick off TLS if required
|
126
|
+
if tls
|
127
|
+
socket.on_handshake @set_protocol
|
128
|
+
socket.start_tls(tls)
|
129
|
+
else
|
130
|
+
set_protocol(socket, :http1)
|
131
|
+
end
|
132
|
+
|
133
|
+
socket.start_read
|
134
|
+
socket.enable_nodelay
|
135
|
+
end
|
114
136
|
|
115
|
-
|
137
|
+
def on_progress(data, socket)
|
138
|
+
# Storage contains the parser for this connection
|
139
|
+
parser = socket.storage
|
140
|
+
parser.parse(data)
|
141
|
+
end
|
116
142
|
|
117
|
-
|
118
|
-
|
119
|
-
@connection = socket.storage
|
143
|
+
def set_protocol(socket, version)
|
144
|
+
app, app_mode, port, tls = socket.storage
|
120
145
|
|
121
|
-
|
122
|
-
|
123
|
-
|
146
|
+
parser = if version == :h2
|
147
|
+
@http2_cache.pop || new_http2_parser
|
148
|
+
else
|
149
|
+
@http1_cache.pop || new_http1_parser
|
150
|
+
end
|
124
151
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
socket = @socket_server.check_pending
|
130
|
-
return if socket.nil?
|
131
|
-
end
|
132
|
-
|
133
|
-
# Data == "TLS_indicator Port APP_ID"
|
134
|
-
tls, port, app_id = data.split(SPACE, 3)
|
135
|
-
app = @app_cache[app_id.to_sym] ||= AppStore.get(app_id)
|
136
|
-
inst = @parser_cache.pop || ::HttpParser::Parser.new_instance(&@set_instance_type)
|
137
|
-
|
138
|
-
# process any data coming from the socket
|
139
|
-
socket.progress @on_progress
|
140
|
-
# TODO:: Allow some globals for supplying the certs
|
141
|
-
# --> We could store these in the AppStore
|
142
|
-
socket.start_tls(:server => true) if tls == USE_TLS
|
143
|
-
|
144
|
-
# Keep track of the connection
|
145
|
-
connection = Connection.new self, @gazelle, socket, port, inst, app, @connection_queue
|
146
|
-
@connections.add connection
|
147
|
-
# This allows us to re-use the one proc for parsing
|
148
|
-
socket.storage = connection
|
149
|
-
|
150
|
-
socket.start_read
|
151
|
-
end
|
152
|
+
parser.load(socket, port, app, app_mode, tls)
|
153
|
+
socket.progress @on_progress
|
154
|
+
socket.storage = parser
|
155
|
+
end
|
152
156
|
|
153
|
-
def process_signal(data, pipe)
|
154
|
-
shutdown if data == KILL_GAZELLE
|
155
|
-
end
|
156
157
|
|
157
|
-
|
158
|
-
|
159
|
-
|
158
|
+
def new_http1_parser
|
159
|
+
@h1_parser_obj ||= Http1::Callbacks.new
|
160
|
+
|
161
|
+
@parser_count += 1
|
162
|
+
Http1.new(@return_http1, @h1_parser_obj, @thread, @logger)
|
163
|
+
end
|
164
|
+
|
165
|
+
def return_http1(parser)
|
166
|
+
@http1_cache.push parser
|
167
|
+
end
|
168
|
+
|
169
|
+
def new_http2_parser
|
170
|
+
raise NotImplementedError.new 'TODO:: Create HTTP2 parser class'
|
171
|
+
@parser_count += 1
|
172
|
+
Http2.new(@return_http2)
|
173
|
+
end
|
174
|
+
|
175
|
+
def return_http2(parser)
|
176
|
+
@http2_cache << parser
|
177
|
+
end
|
160
178
|
end
|
161
|
-
end
|
162
179
|
end
|