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.
Files changed (51) hide show
  1. data/Gemfile +6 -0
  2. data/README.md +141 -35
  3. data/Rakefile +13 -45
  4. data/example/Procfile +4 -0
  5. data/example/config.sqlite +0 -0
  6. data/example/http_0mq.rb +37 -19
  7. data/example/lobster.ru +14 -6
  8. data/example/mongrel2.conf +47 -0
  9. data/example/tmp/access.log +505 -0
  10. data/example/uploading.ru +37 -0
  11. data/lib/m2r.rb +49 -3
  12. data/lib/m2r/connection.rb +66 -0
  13. data/lib/m2r/connection_factory.rb +41 -0
  14. data/lib/m2r/handler.rb +130 -0
  15. data/lib/m2r/headers.rb +72 -0
  16. data/lib/m2r/rack_handler.rb +47 -0
  17. data/lib/m2r/request.rb +129 -0
  18. data/lib/m2r/request/base.rb +33 -0
  19. data/lib/m2r/request/upload.rb +60 -0
  20. data/lib/m2r/response.rb +102 -0
  21. data/lib/m2r/response/content_length.rb +18 -0
  22. data/lib/m2r/version.rb +5 -0
  23. data/lib/rack/handler/mongrel2.rb +33 -0
  24. data/m2r.gemspec +30 -63
  25. data/test/acceptance/examples_test.rb +32 -0
  26. data/test/support/capybara.rb +4 -0
  27. data/test/support/mongrel_helper.rb +40 -0
  28. data/test/support/test_handler.rb +51 -0
  29. data/test/support/test_user.rb +37 -0
  30. data/test/test_helper.rb +5 -0
  31. data/test/unit/connection_factory_test.rb +29 -0
  32. data/test/unit/connection_test.rb +49 -0
  33. data/test/unit/handler_test.rb +41 -0
  34. data/test/unit/headers_test.rb +50 -0
  35. data/test/unit/m2r_test.rb +40 -0
  36. data/test/unit/rack_handler_test.rb +52 -0
  37. data/test/unit/request_test.rb +38 -0
  38. data/test/unit/response_test.rb +30 -0
  39. metadata +310 -105
  40. data/.document +0 -5
  41. data/.gitignore +0 -21
  42. data/ISSUES +0 -62
  43. data/VERSION +0 -1
  44. data/benchmarks/jruby +0 -60
  45. data/example/rack_handler.rb +0 -69
  46. data/lib/connection.rb +0 -158
  47. data/lib/fiber_handler.rb +0 -43
  48. data/lib/handler.rb +0 -66
  49. data/lib/request.rb +0 -44
  50. data/test/helper.rb +0 -10
  51. 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,4 @@
1
+ require 'capybara'
2
+ require 'capybara/mechanize'
3
+
4
+ Capybara.app_host = "http://localhost:6767/"
@@ -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
@@ -0,0 +1,5 @@
1
+ require 'minitest/autorun'
2
+ require 'mocha'
3
+ require 'm2r'
4
+
5
+ Dir[File.expand_path(File.join(__FILE__, '../support/*.rb'))].each { |m| require m }
@@ -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