m2r 1.0.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.
- data/README.md +64 -1
 - data/example/config.sqlite +0 -0
 - data/example/http_0mq.rb +10 -3
 - data/example/tmp/access.log +215 -0
 - data/lib/m2r.rb +1 -0
 - data/lib/m2r/connection.rb +21 -10
 - data/lib/m2r/connection_factory.rb +20 -17
 - data/lib/m2r/handler.rb +40 -3
 - data/lib/m2r/headers.rb +9 -1
 - data/lib/m2r/http/close.rb +30 -0
 - data/lib/m2r/rack_handler.rb +3 -3
 - data/lib/m2r/reply.rb +15 -0
 - data/lib/m2r/request.rb +38 -31
 - data/lib/m2r/request/base.rb +0 -8
 - data/lib/m2r/request/upload.rb +0 -10
 - data/lib/m2r/response.rb +64 -18
 - data/lib/m2r/response/always_close.rb +26 -0
 - data/lib/m2r/response/content_length.rb +8 -2
 - data/lib/m2r/response/to_request.rb +26 -0
 - data/lib/m2r/version.rb +1 -1
 - data/lib/rack/handler/mongrel2.rb +17 -3
 - data/test/support/test_handler.rb +5 -1
 - data/test/unit/connection_factory_test.rb +3 -3
 - data/test/unit/connection_test.rb +60 -9
 - data/test/unit/handler_test.rb +46 -9
 - data/test/unit/headers_test.rb +13 -0
 - data/test/unit/rack_handler_test.rb +26 -2
 - data/test/unit/request_test.rb +1 -0
 - data/test/unit/response_test.rb +76 -5
 - metadata +7 -3
 
| 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'm2r/response'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module M2R
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Response
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Use to disable persisent connections even though
         
     | 
| 
      
 6 
     | 
    
         
            +
                # your client would prefer otherwise.
         
     | 
| 
      
 7 
     | 
    
         
            +
                #
         
     | 
| 
      
 8 
     | 
    
         
            +
                # @api public
         
     | 
| 
      
 9 
     | 
    
         
            +
                module AlwaysClose
         
     | 
| 
      
 10 
     | 
    
         
            +
                  def close?
         
     | 
| 
      
 11 
     | 
    
         
            +
                    true
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def headers(value = GETTER)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    if value == GETTER
         
     | 
| 
      
 16 
     | 
    
         
            +
                      h = super
         
     | 
| 
      
 17 
     | 
    
         
            +
                      h['Connection'] = 'close'
         
     | 
| 
      
 18 
     | 
    
         
            +
                      h
         
     | 
| 
      
 19 
     | 
    
         
            +
                    else
         
     | 
| 
      
 20 
     | 
    
         
            +
                      super
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
         @@ -10,8 +10,14 @@ module M2R 
     | 
|
| 
       10 
10 
     | 
    
         
             
                #
         
     | 
| 
       11 
11 
     | 
    
         
             
                # @api public
         
     | 
| 
       12 
12 
     | 
    
         
             
                module ContentLength
         
     | 
| 
       13 
     | 
    
         
            -
                  def headers
         
     | 
| 
       14 
     | 
    
         
            -
                     
     | 
| 
      
 13 
     | 
    
         
            +
                  def headers(value = GETTER)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    if value == GETTER
         
     | 
| 
      
 15 
     | 
    
         
            +
                      h = super
         
     | 
| 
      
 16 
     | 
    
         
            +
                      h['Content-Length'] ||= body.bytesize
         
     | 
| 
      
 17 
     | 
    
         
            +
                      h
         
     | 
| 
      
 18 
     | 
    
         
            +
                    else
         
     | 
| 
      
 19 
     | 
    
         
            +
                      super
         
     | 
| 
      
 20 
     | 
    
         
            +
                    end
         
     | 
| 
       15 
21 
     | 
    
         
             
                  end
         
     | 
| 
       16 
22 
     | 
    
         
             
                end
         
     | 
| 
       17 
23 
     | 
    
         
             
              end
         
     | 
| 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'm2r/response'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module M2R
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Response
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                # Handles the logic of having response with proper
         
     | 
| 
      
 7 
     | 
    
         
            +
                # http version and 'Connection' header in relation to
         
     | 
| 
      
 8 
     | 
    
         
            +
                # given request
         
     | 
| 
      
 9 
     | 
    
         
            +
                #
         
     | 
| 
      
 10 
     | 
    
         
            +
                # @api public
         
     | 
| 
      
 11 
     | 
    
         
            +
                module ToRequest
         
     | 
| 
      
 12 
     | 
    
         
            +
                  # params [Request] request Request that response handles
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # params [true, false] identical Whether http version in response should be identical
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #   to the received one.
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # @return [self] Response object
         
     | 
| 
      
 16 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 17 
     | 
    
         
            +
                  def to(request, identical = false)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    # http://www.ietf.org/rfc/rfc2145.txt
         
     | 
| 
      
 19 
     | 
    
         
            +
                    # 2.3 Which version number to send in a message
         
     | 
| 
      
 20 
     | 
    
         
            +
                    http_version(request.http_version) if identical
         
     | 
| 
      
 21 
     | 
    
         
            +
                    headers['Connection'] = 'close' if request.close?
         
     | 
| 
      
 22 
     | 
    
         
            +
                    self
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/m2r/version.rb
    CHANGED
    
    
| 
         @@ -14,8 +14,8 @@ module Rack 
     | 
|
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
                  def self.run(app, options = {})
         
     | 
| 
       16 
16 
     | 
    
         
             
                    options = OpenStruct.new( DEFAULT_OPTIONS.merge(options) )
         
     | 
| 
       17 
     | 
    
         
            -
                     
     | 
| 
       18 
     | 
    
         
            -
                    adapter = M2R::RackHandler.new(app,  
     | 
| 
      
 17 
     | 
    
         
            +
                    parser  = M2R::Request
         
     | 
| 
      
 18 
     | 
    
         
            +
                    adapter = M2R::RackHandler.new(app, connection_factory(options), parser)
         
     | 
| 
       19 
19 
     | 
    
         
             
                    adapter.listen
         
     | 
| 
       20 
20 
     | 
    
         
             
                  end
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
         @@ -23,9 +23,23 @@ module Rack 
     | 
|
| 
       23 
23 
     | 
    
         
             
                    {
         
     | 
| 
       24 
24 
     | 
    
         
             
                      'recv_addr=RECV_ADDR' => 'Receive address',
         
     | 
| 
       25 
25 
     | 
    
         
             
                      'send_addr=SEND_ADDR' => 'Send address',
         
     | 
| 
       26 
     | 
    
         
            -
                      'sender_id=UUID' 
     | 
| 
      
 26 
     | 
    
         
            +
                      'sender_id=UUID'      => 'Sender UUID'
         
     | 
| 
       27 
27 
     | 
    
         
             
                    }
         
     | 
| 
       28 
28 
     | 
    
         
             
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  def self.connection_factory(options)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    klass = if custom = options.connection_factory
         
     | 
| 
      
 32 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 33 
     | 
    
         
            +
                        M2R::ConnectionFactory.const_get(custom.classify)
         
     | 
| 
      
 34 
     | 
    
         
            +
                      rescue NameError
         
     | 
| 
      
 35 
     | 
    
         
            +
                        require "m2r/connection_factory/#{custom.underscore}"
         
     | 
| 
      
 36 
     | 
    
         
            +
                        M2R::ConnectionFactory.const_get(custom.classify)
         
     | 
| 
      
 37 
     | 
    
         
            +
                      end
         
     | 
| 
      
 38 
     | 
    
         
            +
                    else
         
     | 
| 
      
 39 
     | 
    
         
            +
                      M2R::ConnectionFactory
         
     | 
| 
      
 40 
     | 
    
         
            +
                    end
         
     | 
| 
      
 41 
     | 
    
         
            +
                    klass.new(options)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
       29 
43 
     | 
    
         
             
                end
         
     | 
| 
       30 
44 
     | 
    
         | 
| 
       31 
45 
     | 
    
         
             
                register :mongrel2, ::Rack::Handler::Mongrel2
         
     | 
| 
         @@ -2,7 +2,7 @@ require 'm2r/handler' 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            class TestHandler < M2R::Handler
         
     | 
| 
       4 
4 
     | 
    
         
             
              attr_reader :called_methods
         
     | 
| 
       5 
     | 
    
         
            -
              def initialize(connection)
         
     | 
| 
      
 5 
     | 
    
         
            +
              def initialize(connection, parser)
         
     | 
| 
       6 
6 
     | 
    
         
             
                super
         
     | 
| 
       7 
7 
     | 
    
         
             
                @called_methods = []
         
     | 
| 
       8 
8 
     | 
    
         
             
              end
         
     | 
| 
         @@ -48,4 +48,8 @@ class TestHandler < M2R::Handler 
     | 
|
| 
       48 
48 
     | 
    
         
             
              def after_all(request, response)
         
     | 
| 
       49 
49 
     | 
    
         
             
                @called_methods << :all
         
     | 
| 
       50 
50 
     | 
    
         
             
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
              def on_error(request, response, error)
         
     | 
| 
      
 53 
     | 
    
         
            +
                @called_methods << :error
         
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
       51 
55 
     | 
    
         
             
            end
         
     | 
| 
         @@ -7,7 +7,6 @@ module M2R 
     | 
|
| 
       7 
7 
     | 
    
         
             
                  sender_id = "sid"
         
     | 
| 
       8 
8 
     | 
    
         
             
                  request_addr = "req"
         
     | 
| 
       9 
9 
     | 
    
         
             
                  response_addr = "req"
         
     | 
| 
       10 
     | 
    
         
            -
                  request_parser = Object.new
         
     | 
| 
       11 
10 
     | 
    
         | 
| 
       12 
11 
     | 
    
         
             
                  pull    = stub(:pull)
         
     | 
| 
       13 
12 
     | 
    
         
             
                  pub     = stub(:pub)
         
     | 
| 
         @@ -21,9 +20,10 @@ module M2R 
     | 
|
| 
       21 
20 
     | 
    
         
             
                  pub.expects(:connect).with(response_addr)
         
     | 
| 
       22 
21 
     | 
    
         
             
                  pub.expects(:setsockopt).with(ZMQ::IDENTITY, sender_id)
         
     | 
| 
       23 
22 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
                  Connection.expects(:new).with(pull, pub 
     | 
| 
       25 
     | 
    
         
            -
                  cf = ConnectionFactory.new sender_id, request_addr, response_addr,  
     | 
| 
      
 23 
     | 
    
         
            +
                  Connection.expects(:new).with(pull, pub)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  cf = ConnectionFactory.new ConnectionFactory::Options.new(sender_id, request_addr, response_addr), context
         
     | 
| 
       26 
25 
     | 
    
         
             
                  cf.connection
         
     | 
| 
       27 
26 
     | 
    
         
             
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
       28 
28 
     | 
    
         
             
              end
         
     | 
| 
       29 
29 
     | 
    
         
             
            end
         
     | 
| 
         @@ -13,6 +13,7 @@ module M2R 
     | 
|
| 
       13 
13 
     | 
    
         | 
| 
       14 
14 
     | 
    
         
             
                  @sub = M2R.zmq_context.socket(ZMQ::SUB)
         
     | 
| 
       15 
15 
     | 
    
         
             
                  assert_equal 0, @sub.bind(@response_addr), "Could not bind sub socket in tests"
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @sub.setsockopt(ZMQ::SUBSCRIBE, "")
         
     | 
| 
       16 
17 
     | 
    
         | 
| 
       17 
18 
     | 
    
         | 
| 
       18 
19 
     | 
    
         
             
                  @request_socket = M2R.zmq_context.socket(ZMQ::PULL)
         
     | 
| 
         @@ -32,17 +33,67 @@ module M2R 
     | 
|
| 
       32 
33 
     | 
    
         | 
| 
       33 
34 
     | 
    
         
             
                def test_receive_message
         
     | 
| 
       34 
35 
     | 
    
         
             
                  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 
     | 
    
         
            -
                   
     | 
| 
      
 36 
     | 
    
         
            +
                  @push.send_string(msg = "1c5fd481-1121-49d8-a706-69127975db1a ebb407b2-49aa-48a5-9f96-9db121051484 / 2:{},0:,", ZMQ::NOBLOCK)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  assert_equal msg, connection.receive
         
     | 
| 
       37 
38 
     | 
    
         
             
                end
         
     | 
| 
       38 
39 
     | 
    
         | 
| 
       39 
     | 
    
         
            -
                def  
     | 
| 
       40 
     | 
    
         
            -
                   
     | 
| 
       41 
     | 
    
         
            -
                   
     | 
| 
       42 
     | 
    
         
            -
                   
     | 
| 
       43 
     | 
    
         
            -
                   
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
      
 40 
     | 
    
         
            +
                def test_deliver_message
         
     | 
| 
      
 41 
     | 
    
         
            +
                  connection = Connection.new(@request_socket, @response_socket)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  connection.deliver('uuid', ['conn1', 'conn2'], 'ddaattaa')
         
     | 
| 
      
 43 
     | 
    
         
            +
                  assert_equal 0, @sub.recv_string(msg = "")
         
     | 
| 
      
 44 
     | 
    
         
            +
                  assert_equal "uuid 11:conn1 conn2, ddaattaa", msg
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                def test_string_replay_non_close
         
     | 
| 
      
 48 
     | 
    
         
            +
                  connection = Connection.new(@request_socket, @response_socket)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  connection.reply( stub(sender: 'uuid', conn_id: 'conn1', close?: false), 'ddaattaa')
         
     | 
| 
      
 50 
     | 
    
         
            +
                  assert_equal 0, @sub.recv_string(msg = "")
         
     | 
| 
      
 51 
     | 
    
         
            +
                  assert_equal "uuid 5:conn1, ddaattaa", msg
         
     | 
| 
      
 52 
     | 
    
         
            +
                  assert_equal -1, @sub.recv_string(msg = "", ZMQ::NOBLOCK)
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                def test_string_replay_close
         
     | 
| 
      
 56 
     | 
    
         
            +
                  connection = Connection.new(@request_socket, @response_socket)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  connection.reply( stub(sender: 'uuid', conn_id: 'conn1', close?: true), 'ddaattaa')
         
     | 
| 
      
 58 
     | 
    
         
            +
                  assert_equal 0, @sub.recv_string(msg = "")
         
     | 
| 
      
 59 
     | 
    
         
            +
                  assert_equal "uuid 5:conn1, ddaattaa", msg
         
     | 
| 
      
 60 
     | 
    
         
            +
                  assert_equal 0, @sub.recv_string(msg = "")
         
     | 
| 
      
 61 
     | 
    
         
            +
                  assert_equal "uuid 5:conn1, ", msg
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                def test_response_replay_non_close
         
     | 
| 
      
 65 
     | 
    
         
            +
                  connection = Connection.new(@request_socket, @response_socket)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  connection.reply( stub(sender: 'uuid', conn_id: 'conn1'), mock(to_s: 'ddaattaa', close?: false))
         
     | 
| 
      
 67 
     | 
    
         
            +
                  assert_equal 0, @sub.recv_string(msg = "")
         
     | 
| 
      
 68 
     | 
    
         
            +
                  assert_equal "uuid 5:conn1, ddaattaa", msg
         
     | 
| 
      
 69 
     | 
    
         
            +
                  assert_equal -1, @sub.recv_string(msg = "", ZMQ::NOBLOCK)
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                def test_response_replay_close
         
     | 
| 
      
 73 
     | 
    
         
            +
                  connection = Connection.new(@request_socket, @response_socket)
         
     | 
| 
      
 74 
     | 
    
         
            +
                  connection.reply( stub(sender: 'uuid', conn_id: 'conn1'), mock(to_s: 'ddaattaa', close?: true))
         
     | 
| 
      
 75 
     | 
    
         
            +
                  assert_equal 0, @sub.recv_string(msg = "")
         
     | 
| 
      
 76 
     | 
    
         
            +
                  assert_equal "uuid 5:conn1, ddaattaa", msg
         
     | 
| 
      
 77 
     | 
    
         
            +
                  assert_equal 0, @sub.recv_string(msg = "")
         
     | 
| 
      
 78 
     | 
    
         
            +
                  assert_equal "uuid 5:conn1, ", msg
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                def test_exception_when_receiving
         
     | 
| 
      
 82 
     | 
    
         
            +
                  request_socket = mock(:recv_string => -1)
         
     | 
| 
      
 83 
     | 
    
         
            +
                  connection = Connection.new request_socket, nil
         
     | 
| 
      
 84 
     | 
    
         
            +
                  assert_raises(Connection::Error) { connection.receive }
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                def test_exception_when_deliverying
         
     | 
| 
      
 88 
     | 
    
         
            +
                  response_socket = mock(:send_string => -1)
         
     | 
| 
      
 89 
     | 
    
         
            +
                  connection = Connection.new nil, response_socket
         
     | 
| 
      
 90 
     | 
    
         
            +
                  assert_raises(Connection::Error) { connection.deliver('uuid', ['connection_ids'], 'data') }
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                def test_exception_when_replying
         
     | 
| 
      
 94 
     | 
    
         
            +
                  response_socket = mock(:send_string => -1)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  connection = Connection.new nil, response_socket
         
     | 
| 
      
 96 
     | 
    
         
            +
                  assert_raises(Connection::Error) { connection.reply( Struct.new(:sender, :conn_id).new('sender', 'conn_id') , 'data' ) }
         
     | 
| 
       46 
97 
     | 
    
         
             
                end
         
     | 
| 
       47 
98 
     | 
    
         | 
| 
       48 
99 
     | 
    
         
             
              end
         
     | 
    
        data/test/unit/handler_test.rb
    CHANGED
    
    | 
         @@ -2,40 +2,77 @@ require 'test_helper' 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module M2R
         
     | 
| 
       4 
4 
     | 
    
         
             
              class HandlerTest < MiniTest::Unit::TestCase
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
       5 
6 
     | 
    
         
             
                def test_lifecycle_for_disconnect
         
     | 
| 
       6 
     | 
    
         
            -
                  connection = stub(:receive =>  
     | 
| 
      
 7 
     | 
    
         
            +
                  connection = stub(:receive => "")
         
     | 
| 
       7 
8 
     | 
    
         
             
                  connection.stubs(:connection).returns(connection)
         
     | 
| 
       8 
     | 
    
         
            -
                   
     | 
| 
      
 9 
     | 
    
         
            +
                  parser = stub(:parse => disconnect_request)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  h = TestHandler.new(connection, parser)
         
     | 
| 
       9 
11 
     | 
    
         
             
                  h.listen
         
     | 
| 
       10 
12 
     | 
    
         
             
                  assert_equal [:wait, :request, :disconnect, :all], h.called_methods
         
     | 
| 
       11 
13 
     | 
    
         
             
                end
         
     | 
| 
       12 
14 
     | 
    
         | 
| 
       13 
15 
     | 
    
         
             
                def test_lifecycle_for_upload_start
         
     | 
| 
       14 
     | 
    
         
            -
                  connection = stub(:receive =>  
     | 
| 
      
 16 
     | 
    
         
            +
                  connection = stub(:receive => "")
         
     | 
| 
       15 
17 
     | 
    
         
             
                  connection.stubs(:connection).returns(connection)
         
     | 
| 
       16 
     | 
    
         
            -
                   
     | 
| 
      
 18 
     | 
    
         
            +
                  parser = stub(:parse => upload_start_request)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  h = TestHandler.new(connection, parser)
         
     | 
| 
       17 
20 
     | 
    
         
             
                  h.listen
         
     | 
| 
       18 
21 
     | 
    
         
             
                  assert_equal [:wait, :request, :start, :all], h.called_methods
         
     | 
| 
       19 
22 
     | 
    
         
             
                end
         
     | 
| 
       20 
23 
     | 
    
         | 
| 
       21 
24 
     | 
    
         
             
                def test_lifecycle_for_upload_done
         
     | 
| 
       22 
     | 
    
         
            -
                  connection = stub(:receive =>  
     | 
| 
      
 25 
     | 
    
         
            +
                  connection = stub(:receive => "", :reply => nil)
         
     | 
| 
       23 
26 
     | 
    
         
             
                  connection.stubs(:connection).returns(connection)
         
     | 
| 
       24 
     | 
    
         
            -
                   
     | 
| 
      
 27 
     | 
    
         
            +
                  parser = stub(:parse => upload_done_request)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  h = TestHandler.new(connection, parser)
         
     | 
| 
       25 
29 
     | 
    
         
             
                  h.listen
         
     | 
| 
       26 
30 
     | 
    
         
             
                  assert_equal [:wait, :request, :done, :process, :after, :reply, :all], h.called_methods
         
     | 
| 
       27 
31 
     | 
    
         
             
                end
         
     | 
| 
       28 
32 
     | 
    
         | 
| 
      
 33 
     | 
    
         
            +
                def test_lifecycle_for_exception_when_getting_request
         
     | 
| 
      
 34 
     | 
    
         
            +
                  connection = stub()
         
     | 
| 
      
 35 
     | 
    
         
            +
                  connection.stubs(:receive).raises(StandardError)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  connection.stubs(:connection).returns(connection)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  h = TestHandler.new(connection, nil)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  h.listen
         
     | 
| 
      
 39 
     | 
    
         
            +
                  assert_equal [:wait, :error], h.called_methods
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                def test_lifecycle_for_exception_when_processing
         
     | 
| 
      
 43 
     | 
    
         
            +
                  connection = stub(:receive => "", :reply => nil)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  connection.stubs(:connection).returns(connection)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  parser = stub(:parse => request)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  h = TestHandler.new(connection, parser)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  h.extend(Module.new(){
         
     | 
| 
      
 48 
     | 
    
         
            +
                    def process(request)
         
     | 
| 
      
 49 
     | 
    
         
            +
                      super
         
     | 
| 
      
 50 
     | 
    
         
            +
                      raise StandardError
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end
         
     | 
| 
      
 52 
     | 
    
         
            +
                  })
         
     | 
| 
      
 53 
     | 
    
         
            +
                  h.listen
         
     | 
| 
      
 54 
     | 
    
         
            +
                  assert_equal [:wait, :request, :process, :all, :error], h.called_methods
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                private
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
       29 
61 
     | 
    
         
             
                def disconnect_request
         
     | 
| 
       30 
     | 
    
         
            -
                  Request.new("sender", "conn_id", "/path", Headers.new({"METHOD" => "JSON"}), '{"type":"disconnect"}')
         
     | 
| 
      
 62 
     | 
    
         
            +
                  Request.new("sender", "conn_id", "/path", Headers.new({}), Headers.new({"METHOD" => "JSON"}), '{"type":"disconnect"}')
         
     | 
| 
       31 
63 
     | 
    
         
             
                end
         
     | 
| 
       32 
64 
     | 
    
         | 
| 
       33 
65 
     | 
    
         
             
                def upload_start_request
         
     | 
| 
       34 
     | 
    
         
            -
                  Request.new("sender", "conn_id", "/path", Headers.new({"x-mongrel2-upload-start" => "/tmp/file"}), '')
         
     | 
| 
      
 66 
     | 
    
         
            +
                  Request.new("sender", "conn_id", "/path", Headers.new({}), Headers.new({"x-mongrel2-upload-start" => "/tmp/file"}), '')
         
     | 
| 
       35 
67 
     | 
    
         
             
                end
         
     | 
| 
       36 
68 
     | 
    
         | 
| 
       37 
69 
     | 
    
         
             
                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"}), '')
         
     | 
| 
      
 70 
     | 
    
         
            +
                  Request.new("sender", "conn_id", "/path", Headers.new({}), Headers.new({"x-mongrel2-upload-start" => "/tmp/file", "x-mongrel2-upload-done" => "/tmp/file"}), '')
         
     | 
| 
       39 
71 
     | 
    
         
             
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                def request
         
     | 
| 
      
 74 
     | 
    
         
            +
                  Request.new("sender", "conn_id", "/path", Headers.new({}), Headers.new({}), '')
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
       40 
77 
     | 
    
         
             
              end
         
     | 
| 
       41 
78 
     | 
    
         
             
            end
         
     | 
    
        data/test/unit/headers_test.rb
    CHANGED
    
    | 
         @@ -46,5 +46,18 @@ module M2R 
     | 
|
| 
       46 
46 
     | 
    
         
             
                  }, env)
         
     | 
| 
       47 
47 
     | 
    
         
             
                end
         
     | 
| 
       48 
48 
     | 
    
         | 
| 
      
 49 
     | 
    
         
            +
                def test_compatibility_trust
         
     | 
| 
      
 50 
     | 
    
         
            +
                  headers = Headers.new({"Content-Type" => "CT"}, true)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  assert_equal nil, headers['content-type']
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def test_compatibility_direct_access
         
     | 
| 
      
 55 
     | 
    
         
            +
                  headers = Headers.new(source = {"content-type" => "CT"}, true)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  assert_equal "CT", headers['content-type']
         
     | 
| 
      
 57 
     | 
    
         
            +
                  headers['Content-type'] = "NEW"
         
     | 
| 
      
 58 
     | 
    
         
            +
                  assert_equal "NEW", headers['content-Type']
         
     | 
| 
      
 59 
     | 
    
         
            +
                  assert_equal "NEW", source['content-type']
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
       49 
62 
     | 
    
         
             
              end
         
     | 
| 
       50 
63 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,5 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'test_helper'
         
     | 
| 
       2 
2 
     | 
    
         
             
            require 'm2r/rack_handler'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'm2r/connection_factory'
         
     | 
| 
       3 
4 
     | 
    
         | 
| 
       4 
5 
     | 
    
         
             
            class HelloWorld
         
     | 
| 
       5 
6 
     | 
    
         
             
              def call(env)
         
     | 
| 
         @@ -8,6 +9,13 @@ class HelloWorld 
     | 
|
| 
       8 
9 
     | 
    
         
             
            end
         
     | 
| 
       9 
10 
     | 
    
         | 
| 
       10 
11 
     | 
    
         
             
            module M2R
         
     | 
| 
      
 12 
     | 
    
         
            +
              class ConnectionFactory
         
     | 
| 
      
 13 
     | 
    
         
            +
                class Custom
         
     | 
| 
      
 14 
     | 
    
         
            +
                  def initialize(*)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
       11 
19 
     | 
    
         
             
              class RackHandlerTest < MiniTest::Unit::TestCase
         
     | 
| 
       12 
20 
     | 
    
         
             
                def test_discoverability
         
     | 
| 
       13 
21 
     | 
    
         
             
                  handler = ::Rack::Handler.get(:mongrel2)
         
     | 
| 
         @@ -26,20 +34,36 @@ module M2R 
     | 
|
| 
       26 
34 
     | 
    
         
             
                    'sender_id' => id   = SecureRandom.uuid
         
     | 
| 
       27 
35 
     | 
    
         
             
                  }
         
     | 
| 
       28 
36 
     | 
    
         
             
                  cf = mock(:connection)
         
     | 
| 
       29 
     | 
    
         
            -
                  ConnectionFactory.expects(:new).with( 
     | 
| 
      
 37 
     | 
    
         
            +
                  ConnectionFactory.expects(:new).with(responds_with(:sender_id, id)).returns(cf)
         
     | 
| 
       30 
38 
     | 
    
         
             
                  RackHandler.any_instance.stubs(:stop? => true)
         
     | 
| 
       31 
39 
     | 
    
         
             
                  handler.run(HelloWorld.new, options)
         
     | 
| 
       32 
40 
     | 
    
         
             
                end
         
     | 
| 
       33 
41 
     | 
    
         | 
| 
       34 
42 
     | 
    
         
             
                def test_lint_rack_adapter
         
     | 
| 
       35 
43 
     | 
    
         
             
                  factory    = stub(:connection)
         
     | 
| 
       36 
     | 
    
         
            -
                  handler    = RackHandler.new(app, factory)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  handler    = RackHandler.new(app, factory, Request)
         
     | 
| 
       37 
45 
     | 
    
         
             
                  response   = handler.process(root_request)
         
     | 
| 
       38 
46 
     | 
    
         | 
| 
       39 
47 
     | 
    
         
             
                  assert_equal "Hello world!", response.body
         
     | 
| 
       40 
48 
     | 
    
         
             
                  assert_equal 200, response.status
         
     | 
| 
       41 
49 
     | 
    
         
             
                end
         
     | 
| 
       42 
50 
     | 
    
         | 
| 
      
 51 
     | 
    
         
            +
                def test_custom_connection_factory
         
     | 
| 
      
 52 
     | 
    
         
            +
                  require 'rack/handler/mongrel2'
         
     | 
| 
      
 53 
     | 
    
         
            +
                  handler = ::Rack::Handler::Mongrel2
         
     | 
| 
      
 54 
     | 
    
         
            +
                  options = {
         
     | 
| 
      
 55 
     | 
    
         
            +
                    'connection_factory' => 'custom'
         
     | 
| 
      
 56 
     | 
    
         
            +
                  }
         
     | 
| 
      
 57 
     | 
    
         
            +
                  cf = mock(:connection)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  ConnectionFactory::Custom.expects(:new).with(responds_with(:connection_factory, 'custom')).returns(cf)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  RackHandler.any_instance.stubs(:stop? => true)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  handler.run(HelloWorld.new, options)
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                private
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
       43 
67 
     | 
    
         
             
                def root_request
         
     | 
| 
       44 
68 
     | 
    
         
             
                  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 
69 
     | 
    
         
             
                  Request.parse(data)
         
     | 
    
        data/test/unit/request_test.rb
    CHANGED
    
    
    
        data/test/unit/response_test.rb
    CHANGED
    
    | 
         @@ -3,28 +3,99 @@ require 'test_helper' 
     | 
|
| 
       3 
3 
     | 
    
         
             
            module M2R
         
     | 
| 
       4 
4 
     | 
    
         
             
              class ResponseTest < MiniTest::Unit::TestCase
         
     | 
| 
       5 
5 
     | 
    
         
             
                def test_response_with_nil_body
         
     | 
| 
       6 
     | 
    
         
            -
                  ok = Response.new(200 
     | 
| 
      
 6 
     | 
    
         
            +
                  ok = Response.new.status(200).headers({"Transfer-Encoding" => "chunked"}).body(nil)
         
     | 
| 
       7 
7 
     | 
    
         
             
                  http = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"
         
     | 
| 
       8 
8 
     | 
    
         
             
                  assert_equal http, ok.to_s
         
     | 
| 
       9 
9 
     | 
    
         
             
                end
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
                def test_response_with_empty_body
         
     | 
| 
       12 
     | 
    
         
            -
                  ok = Response.new(200 
     | 
| 
      
 12 
     | 
    
         
            +
                  ok = Response.new.status(200).headers({"Transfer-Encoding" => "chunked"}).body("")
         
     | 
| 
       13 
13 
     | 
    
         
             
                  http = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"
         
     | 
| 
       14 
14 
     | 
    
         
             
                  assert_equal http, ok.to_s
         
     | 
| 
       15 
15 
     | 
    
         
             
                end
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
                def test_response_with_no_body
         
     | 
| 
       18 
     | 
    
         
            -
                  ok = Response.new(200 
     | 
| 
      
 18 
     | 
    
         
            +
                  ok = Response.new.status(200).headers({"Transfer-Encoding" => "chunked"})
         
     | 
| 
       19 
19 
     | 
    
         
             
                  http = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"
         
     | 
| 
       20 
20 
     | 
    
         
             
                  assert_equal http, ok.to_s
         
     | 
| 
       21 
21 
     | 
    
         
             
                end
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
       23 
23 
     | 
    
         
             
                def test_response_with_content_length
         
     | 
| 
       24 
     | 
    
         
            -
                  ok = Response.new( 
     | 
| 
      
 24 
     | 
    
         
            +
                  ok = Response.new.body('data')
         
     | 
| 
       25 
25 
     | 
    
         
             
                  ok.extend Response::ContentLength
         
     | 
| 
       26 
     | 
    
         
            -
                  http = "HTTP/1.1 200 OK\r\ 
     | 
| 
      
 26 
     | 
    
         
            +
                  http = "HTTP/1.1 200 OK\r\ncontent-length: 4\r\n\r\ndata"
         
     | 
| 
       27 
27 
     | 
    
         
             
                  assert_equal http, ok.to_s
         
     | 
| 
       28 
28 
     | 
    
         
             
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def test_response_with_header
         
     | 
| 
      
 31 
     | 
    
         
            +
                  ok = Response.new.body('data').header('X-Man', 'Wolverine')
         
     | 
| 
      
 32 
     | 
    
         
            +
                  ok.extend Response::ContentLength
         
     | 
| 
      
 33 
     | 
    
         
            +
                  http = "HTTP/1.1 200 OK\r\nx-man: Wolverine\r\ncontent-length: 4\r\n\r\ndata"
         
     | 
| 
      
 34 
     | 
    
         
            +
                  assert_equal http, ok.to_s
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def test_response_with_old_version
         
     | 
| 
      
 38 
     | 
    
         
            +
                  ok = Response.new.http_version('HTTP/1.0').body('data')
         
     | 
| 
      
 39 
     | 
    
         
            +
                  ok.extend Response::ContentLength
         
     | 
| 
      
 40 
     | 
    
         
            +
                  http = "HTTP/1.0 200 OK\r\ncontent-length: 4\r\n\r\ndata"
         
     | 
| 
      
 41 
     | 
    
         
            +
                  assert_equal http, ok.to_s
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def test_getters
         
     | 
| 
      
 45 
     | 
    
         
            +
                  ok = Response.new.http_version(v = 'HTTP/1.0').status(s = 300).headers({'X-Man' => xman = 'Summers'}).header("X-Angel", angel = "Warren").body(data = 'data')
         
     | 
| 
      
 46 
     | 
    
         
            +
                  assert_equal v,     ok.http_version
         
     | 
| 
      
 47 
     | 
    
         
            +
                  assert_equal s,     ok.status
         
     | 
| 
      
 48 
     | 
    
         
            +
                  assert_equal xman,  ok.header("X-Man")
         
     | 
| 
      
 49 
     | 
    
         
            +
                  assert_equal angel, ok.header("X-Angel")
         
     | 
| 
      
 50 
     | 
    
         
            +
                  assert_equal data , ok.body
         
     | 
| 
      
 51 
     | 
    
         
            +
                  assert_equal 2,     ok.headers.size
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def test_default_close
         
     | 
| 
      
 55 
     | 
    
         
            +
                  ok = Response.new
         
     | 
| 
      
 56 
     | 
    
         
            +
                  refute ok.close?
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                def test_http10_close
         
     | 
| 
      
 60 
     | 
    
         
            +
                  ok = Response.new.http_version('HTTP/1.0')
         
     | 
| 
      
 61 
     | 
    
         
            +
                  assert ok.close?
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                def test_header_close
         
     | 
| 
      
 65 
     | 
    
         
            +
                  ok = Response.new.header('Connection', 'close')
         
     | 
| 
      
 66 
     | 
    
         
            +
                  assert ok.close?
         
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                def test_response_to_http10_identical
         
     | 
| 
      
 70 
     | 
    
         
            +
                  ok = Response.new
         
     | 
| 
      
 71 
     | 
    
         
            +
                  ok.extend(Response::ToRequest)
         
     | 
| 
      
 72 
     | 
    
         
            +
                  ok.to(stub(http_version: 'HTTP/1.0', close?:true), true)
         
     | 
| 
      
 73 
     | 
    
         
            +
                  assert_equal 'HTTP/1.0', ok.http_version
         
     | 
| 
      
 74 
     | 
    
         
            +
                  assert_equal 'close', ok.header('Connection')
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                def test_response_to_http10_rfc2145
         
     | 
| 
      
 78 
     | 
    
         
            +
                  ok = Response.new
         
     | 
| 
      
 79 
     | 
    
         
            +
                  ok.extend(Response::ToRequest)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  ok.to(stub(http_version: 'HTTP/1.0', close?:true))
         
     | 
| 
      
 81 
     | 
    
         
            +
                  assert_equal 'HTTP/1.1', ok.http_version
         
     | 
| 
      
 82 
     | 
    
         
            +
                  assert_equal 'close', ok.header('Connection')
         
     | 
| 
      
 83 
     | 
    
         
            +
                end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                def test_response_to_http11
         
     | 
| 
      
 86 
     | 
    
         
            +
                  ok = Response.new
         
     | 
| 
      
 87 
     | 
    
         
            +
                  ok.extend(Response::ToRequest)
         
     | 
| 
      
 88 
     | 
    
         
            +
                  ok.to(stub(http_version: 'HTTP/1.1', close?:false))
         
     | 
| 
      
 89 
     | 
    
         
            +
                  assert_equal 'HTTP/1.1', ok.http_version
         
     | 
| 
      
 90 
     | 
    
         
            +
                  assert_equal nil, ok.header('Connection')
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                def test_response_to_http11_with_close
         
     | 
| 
      
 94 
     | 
    
         
            +
                  ok = Response.new
         
     | 
| 
      
 95 
     | 
    
         
            +
                  ok.extend(Response::ToRequest)
         
     | 
| 
      
 96 
     | 
    
         
            +
                  ok.to(stub(http_version: 'HTTP/1.1', close?:true))
         
     | 
| 
      
 97 
     | 
    
         
            +
                  assert_equal 'HTTP/1.1', ok.http_version
         
     | 
| 
      
 98 
     | 
    
         
            +
                  assert_equal 'close', ok.header('Connection')
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
       29 
100 
     | 
    
         
             
              end
         
     | 
| 
       30 
101 
     | 
    
         
             
            end
         
     |