module Moxy class WebMockHandler def self.build_request_signature(req) uri = WebMock::Util::URI.heuristic_parse(req.url) uri.path = uri.normalized_path.gsub("[^:]//","/") #todo: authorization, see #https://github.com/bblimke/webmock/blob/master/lib/webmock/http_lib_adapters/net_http.rb client_headers = {} req.env.keys.grep(/^HTTP_/).each do |k| client_headers[k[5..-1]] = req.env[k] #remove client headers prefix 'http_' end request_signature = WebMock::RequestSignature.new( req.request_method.downcase.to_sym, uri.to_s, :body => req.body.read, :headers => client_headers ) request_signature end def self.build_rack_response(webmock_response) webmock_response.raise_error_if_any [webmock_response.status[0], webmock_response.headers || {}, bodify(webmock_response.body)] end def self.bodify(body) body.respond_to?(:each) ? body : [body] end def call(env) req = Rack::Request.new(env) request_signature = Moxy::WebMockHandler.build_request_signature(req) WebMock::RequestRegistry.instance.requested_signatures.put(request_signature) begin if WebMock::StubRegistry.instance.registered_request?(request_signature) webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature) # todo - originally 'handle file name' in patron. why? # WebMock::HttpLibAdapters::PatronAdapter.handle_file_name(req, webmock_response) res = Moxy::WebMockHandler.build_rack_response(webmock_response) WebMock::CallbackRegistry.invoke_callbacks({:lib => :patron}, request_signature, webmock_response) elsif WebMock.net_connect_allowed?(request_signature.uri) res = handle_request_without_webmock(req) if WebMock::CallbackRegistry.any_callbacks? webmock_response = WebMock::HttpLibAdapters::PatronAdapter. build_webmock_response(res) WebMock::CallbackRegistry.invoke_callbacks({:lib => :patron, :real_request => true}, request_signature, webmock_response) end else raise WebMock::NetConnectNotAllowedError.new(request_signature) end res[1]['content-type'] ||= 'text/html' #passing rack-lint, see http://rack.rubyforge.org/doc/SPEC.html #todo - what about content-length when it doesn't come from #webmock? does it get calculated automatically be webmock? return res rescue WebMock::NetConnectNotAllowedError => ex return [500, {"content-type" => "text/plain", "content-length" => ex.message.length.to_s}, Moxy::WebMockHandler.bodify(ex.message.to_s)] end end end end