m2r 0.0.3 → 1.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.
- data/Gemfile +6 -0
- data/README.md +141 -35
- data/Rakefile +13 -45
- data/example/Procfile +4 -0
- data/example/config.sqlite +0 -0
- data/example/http_0mq.rb +37 -19
- data/example/lobster.ru +14 -6
- data/example/mongrel2.conf +47 -0
- data/example/tmp/access.log +505 -0
- data/example/uploading.ru +37 -0
- data/lib/m2r.rb +49 -3
- data/lib/m2r/connection.rb +66 -0
- data/lib/m2r/connection_factory.rb +41 -0
- data/lib/m2r/handler.rb +130 -0
- data/lib/m2r/headers.rb +72 -0
- data/lib/m2r/rack_handler.rb +47 -0
- data/lib/m2r/request.rb +129 -0
- data/lib/m2r/request/base.rb +33 -0
- data/lib/m2r/request/upload.rb +60 -0
- data/lib/m2r/response.rb +102 -0
- data/lib/m2r/response/content_length.rb +18 -0
- data/lib/m2r/version.rb +5 -0
- data/lib/rack/handler/mongrel2.rb +33 -0
- data/m2r.gemspec +30 -63
- data/test/acceptance/examples_test.rb +32 -0
- data/test/support/capybara.rb +4 -0
- data/test/support/mongrel_helper.rb +40 -0
- data/test/support/test_handler.rb +51 -0
- data/test/support/test_user.rb +37 -0
- data/test/test_helper.rb +5 -0
- data/test/unit/connection_factory_test.rb +29 -0
- data/test/unit/connection_test.rb +49 -0
- data/test/unit/handler_test.rb +41 -0
- data/test/unit/headers_test.rb +50 -0
- data/test/unit/m2r_test.rb +40 -0
- data/test/unit/rack_handler_test.rb +52 -0
- data/test/unit/request_test.rb +38 -0
- data/test/unit/response_test.rb +30 -0
- metadata +310 -105
- data/.document +0 -5
- data/.gitignore +0 -21
- data/ISSUES +0 -62
- data/VERSION +0 -1
- data/benchmarks/jruby +0 -60
- data/example/rack_handler.rb +0 -69
- data/lib/connection.rb +0 -158
- data/lib/fiber_handler.rb +0 -43
- data/lib/handler.rb +0 -66
- data/lib/request.rb +0 -44
- data/test/helper.rb +0 -10
- data/test/test_m2r.rb +0 -7
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module M2R
|
4
|
+
class ExamplesTest < MiniTest::Unit::TestCase
|
5
|
+
include MongrelHelper
|
6
|
+
|
7
|
+
def test_rack_example
|
8
|
+
user = TestUser.new
|
9
|
+
user.visit("/handler")
|
10
|
+
user.see!("SENDER", "PATH", "HEADERS", "x-forwarded-for", "x-forwarded-for", "BODY")
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_handler_example
|
14
|
+
user = TestUser.new
|
15
|
+
user.visit("/rack")
|
16
|
+
assert user.find("pre").text.include?(" {{{{{{ { ( ( ( ( (-----:=")
|
17
|
+
user.click_on("flip!")
|
18
|
+
assert user.find("pre").text.include?("=:-----( ( ( ( ( { {{{{{{")
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_handler_async_uploading
|
22
|
+
Dir.glob('example/tmp/upload*') {|p| File.delete(p) }
|
23
|
+
user = TestUser.new
|
24
|
+
user.visit("/uploading")
|
25
|
+
user.attach_and_submit_first_file('uploading_form', user.generate_file)
|
26
|
+
user.visit("/uploading")
|
27
|
+
user.see!("Last submitted file was of size: 10240")
|
28
|
+
assert_equal 0, Dir.glob('example/tmp/upload*').size
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
module MongrelHelper
|
4
|
+
attr_accessor :pid
|
5
|
+
|
6
|
+
def setup
|
7
|
+
check_mongrel
|
8
|
+
`cd example && m2sh load -config mongrel2.conf --db config.sqlite`
|
9
|
+
self.pid = Process.spawn("bundle exec foreman start --procfile=example/Procfile", pgroup: 0, out: "/dev/null", err: "/dev/null")
|
10
|
+
wait_for_pid('example/tmp/mongrel2.pid')
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
Process.kill("SIGTERM", pid) if pid
|
15
|
+
sleep 1
|
16
|
+
end
|
17
|
+
|
18
|
+
def check_mongrel
|
19
|
+
skip("You must install mongrel2 to run this test") if `which mongrel2`.empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
def read_pid_from_file(pidfile)
|
23
|
+
pid = nil
|
24
|
+
Timeout.timeout(5) do
|
25
|
+
loop do
|
26
|
+
begin
|
27
|
+
pid = File.read(pidfile)
|
28
|
+
break unless pid.empty?
|
29
|
+
rescue Errno::ENOENT
|
30
|
+
sleep(0.25)
|
31
|
+
next
|
32
|
+
end
|
33
|
+
end
|
34
|
+
pid.to_i
|
35
|
+
end
|
36
|
+
rescue Timeout::Error
|
37
|
+
raise "Unable to read PID from file #{pidfile}."
|
38
|
+
end
|
39
|
+
alias :wait_for_pid :read_pid_from_file
|
40
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'm2r/handler'
|
2
|
+
|
3
|
+
class TestHandler < M2R::Handler
|
4
|
+
attr_reader :called_methods
|
5
|
+
def initialize(connection)
|
6
|
+
super
|
7
|
+
@called_methods = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def on_wait()
|
11
|
+
unless @called_methods.empty?
|
12
|
+
stop
|
13
|
+
return
|
14
|
+
end
|
15
|
+
@called_methods << :wait
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_request(request)
|
19
|
+
@called_methods << :request
|
20
|
+
end
|
21
|
+
|
22
|
+
def process(request)
|
23
|
+
@called_methods << :process
|
24
|
+
return "response"
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_disconnect(request)
|
28
|
+
@called_methods << :disconnect
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_upload_start(request)
|
32
|
+
@called_methods << :start
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_upload_done(request)
|
36
|
+
@called_methods << :done
|
37
|
+
end
|
38
|
+
|
39
|
+
def after_process(request, response)
|
40
|
+
@called_methods << :after
|
41
|
+
return response
|
42
|
+
end
|
43
|
+
|
44
|
+
def after_reply(request, response)
|
45
|
+
@called_methods << :reply
|
46
|
+
end
|
47
|
+
|
48
|
+
def after_all(request, response)
|
49
|
+
@called_methods << :all
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'bbq/test'
|
2
|
+
require 'bbq/test_user'
|
3
|
+
require 'support/capybara'
|
4
|
+
|
5
|
+
class TestUser < Bbq::TestUser
|
6
|
+
include MiniTest::Assertions
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
super(:driver => :mechanize)
|
10
|
+
end
|
11
|
+
|
12
|
+
def see!(*args)
|
13
|
+
msg = "Expected to see %s but not found"
|
14
|
+
args.each { |arg| assert has_content?(arg), msg % arg }
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate_file
|
18
|
+
File.open(@path = "./tmp/#{Time.now.to_i}", "w") do |f|
|
19
|
+
f.write SecureRandom.hex(5120)
|
20
|
+
end
|
21
|
+
file_path
|
22
|
+
end
|
23
|
+
|
24
|
+
def attach_first_file(form_id, file_path)
|
25
|
+
page.driver.browser.agent.current_page.form_with(id: form_id) do |form|
|
26
|
+
form.file_uploads.first.file_name = file_path
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def attach_and_submit_first_file(form_id, file_path)
|
31
|
+
attach_first_file(form_id, file_path).submit
|
32
|
+
end
|
33
|
+
|
34
|
+
def file_path
|
35
|
+
@path
|
36
|
+
end
|
37
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
module M2R
|
5
|
+
class ConnectionFactoryTest < MiniTest::Unit::TestCase
|
6
|
+
def test_factory
|
7
|
+
sender_id = "sid"
|
8
|
+
request_addr = "req"
|
9
|
+
response_addr = "req"
|
10
|
+
request_parser = Object.new
|
11
|
+
|
12
|
+
pull = stub(:pull)
|
13
|
+
pub = stub(:pub)
|
14
|
+
context = stub(:context)
|
15
|
+
|
16
|
+
context.expects(:socket).with(ZMQ::PULL).returns(pull)
|
17
|
+
context.expects(:socket).with(ZMQ::PUB).returns(pub)
|
18
|
+
|
19
|
+
pull.expects(:connect).with(request_addr)
|
20
|
+
|
21
|
+
pub.expects(:connect).with(response_addr)
|
22
|
+
pub.expects(:setsockopt).with(ZMQ::IDENTITY, sender_id)
|
23
|
+
|
24
|
+
Connection.expects(:new).with(pull, pub, request_parser)
|
25
|
+
cf = ConnectionFactory.new sender_id, request_addr, response_addr, request_parser, context
|
26
|
+
cf.connection
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
module M2R
|
5
|
+
class ConnectionTest < MiniTest::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@request_addr = "inproc://#{SecureRandom.hex}"
|
9
|
+
@response_addr = "inproc://#{SecureRandom.hex}"
|
10
|
+
|
11
|
+
@push = M2R.zmq_context.socket(ZMQ::PUSH)
|
12
|
+
assert_equal 0, @push.bind(@request_addr), "Could not bind push socket in tests"
|
13
|
+
|
14
|
+
@sub = M2R.zmq_context.socket(ZMQ::SUB)
|
15
|
+
assert_equal 0, @sub.bind(@response_addr), "Could not bind sub socket in tests"
|
16
|
+
|
17
|
+
|
18
|
+
@request_socket = M2R.zmq_context.socket(ZMQ::PULL)
|
19
|
+
@request_socket.connect(@request_addr)
|
20
|
+
|
21
|
+
@response_socket = M2R.zmq_context.socket(ZMQ::PUB)
|
22
|
+
@response_socket.connect(@response_addr)
|
23
|
+
@response_socket.setsockopt(ZMQ::IDENTITY, @sender_id = SecureRandom.uuid)
|
24
|
+
end
|
25
|
+
|
26
|
+
def teardown
|
27
|
+
@request_socket.close if @request_socket
|
28
|
+
@response_socket.close if @response_socket
|
29
|
+
@push.close if @push
|
30
|
+
@sub.close if @sub
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_receive_message
|
34
|
+
connection = Connection.new(@request_socket, @response_socket)
|
35
|
+
@push.send_string("1c5fd481-1121-49d8-a706-69127975db1a ebb407b2-49aa-48a5-9f96-9db121051484 / 2:{},0:,", ZMQ::NOBLOCK)
|
36
|
+
assert_instance_of Request, connection.receive
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_different_parser
|
40
|
+
msg = "1c5fd481-1121-49d8-a706-69127975db1a ebb407b2-49aa-48a5-9f96-9db121051484 / 2:{},0:,"
|
41
|
+
parser = stub(:parser)
|
42
|
+
parser.expects(:parse).with(msg).returns(request = Object.new)
|
43
|
+
connection = Connection.new(@request_socket, @response_socket, parser)
|
44
|
+
@push.send_string(msg = "1c5fd481-1121-49d8-a706-69127975db1a ebb407b2-49aa-48a5-9f96-9db121051484 / 2:{},0:,", ZMQ::NOBLOCK)
|
45
|
+
assert_equal request, connection.receive
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module M2R
|
4
|
+
class HandlerTest < MiniTest::Unit::TestCase
|
5
|
+
def test_lifecycle_for_disconnect
|
6
|
+
connection = stub(:receive => disconnect_request)
|
7
|
+
connection.stubs(:connection).returns(connection)
|
8
|
+
h = TestHandler.new(connection)
|
9
|
+
h.listen
|
10
|
+
assert_equal [:wait, :request, :disconnect, :all], h.called_methods
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_lifecycle_for_upload_start
|
14
|
+
connection = stub(:receive => upload_start_request)
|
15
|
+
connection.stubs(:connection).returns(connection)
|
16
|
+
h = TestHandler.new(connection)
|
17
|
+
h.listen
|
18
|
+
assert_equal [:wait, :request, :start, :all], h.called_methods
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_lifecycle_for_upload_done
|
22
|
+
connection = stub(:receive => upload_done_request, :reply => nil)
|
23
|
+
connection.stubs(:connection).returns(connection)
|
24
|
+
h = TestHandler.new(connection)
|
25
|
+
h.listen
|
26
|
+
assert_equal [:wait, :request, :done, :process, :after, :reply, :all], h.called_methods
|
27
|
+
end
|
28
|
+
|
29
|
+
def disconnect_request
|
30
|
+
Request.new("sender", "conn_id", "/path", Headers.new({"METHOD" => "JSON"}), '{"type":"disconnect"}')
|
31
|
+
end
|
32
|
+
|
33
|
+
def upload_start_request
|
34
|
+
Request.new("sender", "conn_id", "/path", Headers.new({"x-mongrel2-upload-start" => "/tmp/file"}), '')
|
35
|
+
end
|
36
|
+
|
37
|
+
def upload_done_request
|
38
|
+
Request.new("sender", "conn_id", "/path", Headers.new({"x-mongrel2-upload-start" => "/tmp/file", "x-mongrel2-upload-done" => "/tmp/file"}), '')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module M2R
|
4
|
+
class HeadersTest < MiniTest::Unit::TestCase
|
5
|
+
def test_case_insensitivity
|
6
|
+
headers = Headers.new({"Content-Type" => "CT"})
|
7
|
+
assert_equal "CT", headers['content-type']
|
8
|
+
assert_equal "CT", headers['Content-Type']
|
9
|
+
assert_equal "CT", headers['Content-type']
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_underscore
|
13
|
+
headers = Headers.new({"URL_SCHEME" => "https"})
|
14
|
+
assert_equal "https", headers['url_scheme']
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_symbols_as_keys
|
18
|
+
headers = Headers.new({"type" => "Ty"})
|
19
|
+
assert_equal "Ty", headers[:type]
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_rackify
|
23
|
+
headers = Headers.new({
|
24
|
+
"Content-Type" => "CT",
|
25
|
+
"type" => "Ty",
|
26
|
+
"Accept-Charset" => "utf8",
|
27
|
+
"cOnTenT-LeNgTh" => "123"
|
28
|
+
})
|
29
|
+
env = {"rack.version" => [1,1]}
|
30
|
+
headers.rackify(env)
|
31
|
+
assert_equal({
|
32
|
+
"rack.version" => [1,1],
|
33
|
+
"CONTENT_TYPE" => "CT",
|
34
|
+
"HTTP_TYPE" => "Ty",
|
35
|
+
"HTTP_ACCEPT_CHARSET" => "utf8",
|
36
|
+
"CONTENT_LENGTH" => "123"
|
37
|
+
}, env)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_rackify_empty_headers
|
41
|
+
headers = Headers.new({})
|
42
|
+
env = {"rack.something" => "value"}
|
43
|
+
headers.rackify(env)
|
44
|
+
assert_equal({
|
45
|
+
"rack.something" => "value",
|
46
|
+
}, env)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
module M2R
|
5
|
+
class ModuleTest < MiniTest::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
if context = M2R.instance_variable_get(:@zmq_context)
|
8
|
+
context.send(:remove_finalizer) if context.respond_to?(:remove_finalizer)
|
9
|
+
M2R.instance_variable_set(:@zmq_context, nil)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_mongrel2_context_getter
|
14
|
+
assert_instance_of ZMQ::Context, M2R.zmq_context
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_mongrel2_context_setter
|
18
|
+
ctx = ZMQ::Context.new(2)
|
19
|
+
M2R.zmq_context = ctx
|
20
|
+
assert_equal ctx, M2R.zmq_context
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_only_1_context_created_when_race_condition
|
24
|
+
threads = nil
|
25
|
+
ZMQ::Context.expects(:new).returns(true).once
|
26
|
+
|
27
|
+
Thread.exclusive do
|
28
|
+
threads = 512.times.map do
|
29
|
+
Thread.new do
|
30
|
+
M2R.zmq_context
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
Timeout.timeout(5) do
|
35
|
+
threads.each(&:join)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'm2r/rack_handler'
|
3
|
+
|
4
|
+
class HelloWorld
|
5
|
+
def call(env)
|
6
|
+
return [200, {'Content-Type' => 'text/plain'}, ["Hello world!"]]
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module M2R
|
11
|
+
class RackHandlerTest < MiniTest::Unit::TestCase
|
12
|
+
def test_discoverability
|
13
|
+
handler = ::Rack::Handler.get(:mongrel2)
|
14
|
+
assert_equal ::Rack::Handler::Mongrel2, handler
|
15
|
+
|
16
|
+
handler = ::Rack::Handler.get('Mongrel2')
|
17
|
+
assert_equal ::Rack::Handler::Mongrel2, handler
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_options
|
21
|
+
require 'rack/handler/mongrel2'
|
22
|
+
handler = ::Rack::Handler::Mongrel2
|
23
|
+
options = {
|
24
|
+
'recv_addr' => recv = 'tcp://1.2.3.4:1234',
|
25
|
+
'send_addr' => send = 'tcp://1.2.3.4:4321',
|
26
|
+
'sender_id' => id = SecureRandom.uuid
|
27
|
+
}
|
28
|
+
cf = mock(:connection)
|
29
|
+
ConnectionFactory.expects(:new).with(id, recv, send).returns(cf)
|
30
|
+
RackHandler.any_instance.stubs(:stop? => true)
|
31
|
+
handler.run(HelloWorld.new, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_lint_rack_adapter
|
35
|
+
factory = stub(:connection)
|
36
|
+
handler = RackHandler.new(app, factory)
|
37
|
+
response = handler.process(root_request)
|
38
|
+
|
39
|
+
assert_equal "Hello world!", response.body
|
40
|
+
assert_equal 200, response.status
|
41
|
+
end
|
42
|
+
|
43
|
+
def root_request
|
44
|
+
data = %q("1c5fd481-1121-49d8-a706-69127975db1a ebb407b2-49aa-48a5-9f96-9db121051484 / 96:{"PATH":"/","host":"127.0.0.1:6767","PATTERN":"/","METHOD":"GET","VERSION":"HTTP/1.1","URI":"/"},0:,)
|
45
|
+
Request.parse(data)
|
46
|
+
end
|
47
|
+
|
48
|
+
def app
|
49
|
+
Rack::Lint.new(HelloWorld.new)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|