Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make httpclient_adapter threadsafe #909

Merged
merged 1 commit into from Oct 16, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 23 additions & 6 deletions lib/webmock/http_lib_adapters/httpclient_adapter.rb
Expand Up @@ -43,6 +43,9 @@ def self.disable!
end

module WebMockHTTPClients

REQUEST_RESPONSE_LOCK = Mutex.new

def do_get_block(req, proxy, conn, &block)
do_get(req, proxy, conn, false, &block)
end
Expand All @@ -57,7 +60,7 @@ def do_get(req, proxy, conn, stream = false, &block)
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)

if webmock_responses[request_signature]
webmock_response = webmock_responses.delete(request_signature)
webmock_response = synchronize_request_response { webmock_responses.delete(request_signature) }
response = build_httpclient_response(webmock_response, stream, req.header, &block)
@request_filter.each do |filter|
filter.filter_response(req, response)
Expand All @@ -68,7 +71,7 @@ def do_get(req, proxy, conn, stream = false, &block)
res
elsif WebMock.net_connect_allowed?(request_signature.uri)
# in case there is a nil entry in the hash...
webmock_responses.delete(request_signature)
synchronize_request_response { webmock_responses.delete(request_signature) }

res = if stream
do_get_stream_without_webmock(req, proxy, conn, &block)
Expand Down Expand Up @@ -100,7 +103,7 @@ def do_get(req, proxy, conn, stream = false, &block)
def do_request_async(method, uri, query, body, extheader)
req = create_request(method, uri, query, body, extheader)
request_signature = build_request_signature(req)
webmock_request_signatures << request_signature
synchronize_request_response { webmock_request_signatures << request_signature }

if webmock_responses[request_signature] || WebMock.net_connect_allowed?(request_signature.uri)
super
Expand Down Expand Up @@ -184,7 +187,9 @@ def build_request_signature(req, reuse_existing = false)

def webmock_responses
@webmock_responses ||= Hash.new do |hash, request_signature|
hash[request_signature] = WebMock::StubRegistry.instance.response_for_request(request_signature)
synchronize_request_response do
hash[request_signature] = WebMock::StubRegistry.instance.response_for_request(request_signature)
end
end
end

Expand All @@ -193,8 +198,10 @@ def webmock_request_signatures
end

def previous_signature_for(signature)
return nil unless index = webmock_request_signatures.index(signature)
webmock_request_signatures.delete_at(index)
synchronize_request_response do
return nil unless index = webmock_request_signatures.index(signature)
webmock_request_signatures.delete_at(index)
end
end

private
Expand All @@ -209,6 +216,16 @@ def headers_from_session(uri)
hdrs
end
end

def synchronize_request_response
if REQUEST_RESPONSE_LOCK.owned?
yield
else
REQUEST_RESPONSE_LOCK.synchronize do
yield
end
end
end
end

class WebMockHTTPClient < HTTPClient
Expand Down