Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

file 212 lines (172 sloc) 7.045 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
if defined?(EventMachine::HttpClient)
  module WebMock
    module HttpLibAdapters
      class EmHttpRequestAdapter < HttpLibAdapter
        adapter_for :em_http_request

        OriginalHttpClient = EventMachine::HttpClient unless const_defined?(:OriginalHttpClient)
        OriginalHttpConnection = EventMachine::HttpConnection unless const_defined?(:OriginalHttpConnection)


        def self.enable!
          EventMachine.send(:remove_const, :HttpConnection)
          EventMachine.send(:const_set, :HttpConnection, EventMachine::WebMockHttpConnection)
          EventMachine.send(:remove_const, :HttpClient)
          EventMachine.send(:const_set, :HttpClient, EventMachine::WebMockHttpClient)
        end

        def self.disable!
          EventMachine.send(:remove_const, :HttpConnection)
          EventMachine.send(:const_set, :HttpConnection, OriginalHttpConnection)
          EventMachine.send(:remove_const, :HttpClient)
          EventMachine.send(:const_set, :HttpClient, OriginalHttpClient)
        end
      end
    end
  end

  module EventMachine

    if defined?(Synchrony) && HTTPMethods.instance_methods.include?(:aget)
      # have to make the callbacks fire on the next tick in order
      # to avoid the dreaded "double resume" exception
      module HTTPMethods
        %w[get head post delete put].each do |type|
          class_eval %[
def #{type}(options = {}, &blk)
f = Fiber.current

conn = setup_request(:#{type}, options, &blk)
conn.callback { EM.next_tick { f.resume(conn) } }
conn.errback { EM.next_tick { f.resume(conn) } }

Fiber.yield
end
]
        end
      end
    end

    class WebMockHttpConnection < HttpConnection
      def webmock_activate_connection(client)
        request_signature = client.request_signature

        if client.stubbed_webmock_response
          conn = HttpStubConnection.new rand(10000)
          post_init

          @deferred = false
          @conn = conn

          conn.parent = self
          conn.pending_connect_timeout = @connopts.connect_timeout
          conn.comm_inactivity_timeout = @connopts.inactivity_timeout

          finalize_request(client)
          @conn.set_deferred_status :succeeded
        elsif WebMock.net_connect_allowed?(request_signature.uri)
          real_activate_connection(client)
        else
          raise WebMock::NetConnectNotAllowedError.new(request_signature)
        end
      end
      alias_method :real_activate_connection, :activate_connection
      alias_method :activate_connection, :webmock_activate_connection
    end

    class WebMockHttpClient < EventMachine::HttpClient
      include HttpEncoding

      def uri
        @req.uri
      end

      def setup(response, uri, error = nil)
        @last_effective_url = @uri = uri
        if error
          on_error(error)
          fail(self)
        else
          @conn.receive_data(response)
          succeed(self)
        end
      end

      def send_request_with_webmock(head, body)
        WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)

        if stubbed_webmock_response
          on_error("WebMock timeout error") if stubbed_webmock_response.should_timeout
          WebMock::CallbackRegistry.invoke_callbacks({:lib => :em_http_request}, request_signature, stubbed_webmock_response)
          EM.next_tick {
            setup(make_raw_response(stubbed_webmock_response), @uri,
                  stubbed_webmock_response.should_timeout ? "WebMock timeout error" : nil)
          }
          self
        elsif WebMock.net_connect_allowed?(request_signature.uri)
          send_request_without_webmock(head, body)
        else
          raise WebMock::NetConnectNotAllowedError.new(request_signature)
        end
      end

      alias_method :send_request_without_webmock, :send_request
      alias_method :send_request, :send_request_with_webmock

      def set_deferred_status(status, *args)
        if status == :succeeded && !stubbed_webmock_response && WebMock::CallbackRegistry.any_callbacks?
          webmock_response = build_webmock_response
          WebMock::CallbackRegistry.invoke_callbacks(
            {:lib => :em_http_request, :real_request => true},
            request_signature,
            webmock_response)
        end

        super
      end

      def request_signature
        @request_signature ||= build_request_signature
      end

      def stubbed_webmock_response
        unless defined?(@stubbed_webmock_response)
          @stubbed_webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature)
        end

        @stubbed_webmock_response
      end

      private

      def build_webmock_response
        webmock_response = WebMock::Response.new
        webmock_response.status = [response_header.status, response_header.http_reason]
        webmock_response.headers = response_header
        webmock_response.body = response
        webmock_response
      end

      def build_request_signature
        headers, body = @req.headers, @req.body

        @conn.middleware.select {|m| m.respond_to?(:request) }.each do |m|
          headers, body = m.request(self, headers, body)
        end

        method = @req.method
        uri = @req.uri.clone
        auth = @req.proxy[:authorization] if @req.proxy
        query = @req.query

        if auth
          userinfo = auth.join(':')
          userinfo = WebMock::Util::URI.encode_unsafe_chars_in_userinfo(userinfo)
          if @req
            @req.proxy.reject! {|k,v| t.to_s == 'authorization' }
          else
            options.reject! {|k,v| k.to_s == 'authorization' } #we added it to url userinfo
          end
          uri.userinfo = userinfo
        end

        uri.query = encode_query(@req.uri, query).slice(/\?(.*)/, 1)

        body = form_encode_body(body) if body.is_a?(Hash)

        WebMock::RequestSignature.new(
          method.downcase.to_sym,
          uri.to_s,
          :body => body,
          :headers => headers
        )
      end

      def make_raw_response(response)
        response.raise_error_if_any

        status, headers, body = response.status, response.headers, response.body
        headers ||= {}

        response_string = []
        response_string << "HTTP/1.1 #{status[0]} #{status[1]}"

        headers["Content-Length"] = body.bytesize unless headers["Content-Length"]
        headers.each do |header, value|
          value = value.join(", ") if value.is_a?(Array)

          # WebMock's internal processing will not handle the body
          # correctly if the header indicates that it is chunked, unless
          # we also create all the chunks.
          # It's far easier just to remove the header.
          next if header =~ /transfer-encoding/i && value =~/chunked/i

          response_string << "#{header}: #{value}"
        end if headers

        response_string << "" << body
        response_string.join("\n")
      end
    end
  end
end
Something went wrong with that request. Please try again.