em-http-request 0.2.10 → 0.2.14

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.
@@ -23,17 +23,16 @@ module EventMachine
23
23
 
24
24
  # E-Tag
25
25
  def etag
26
- self["ETag"]
26
+ self[HttpClient::ETAG]
27
27
  end
28
28
 
29
29
  def last_modified
30
- time = self["Last-Modified"]
31
- Time.parse(time) if time
30
+ self[HttpClient::LAST_MODIFIED]
32
31
  end
33
32
 
34
33
  # HTTP response status as an integer
35
34
  def status
36
- Integer(http_status) rescue nil
35
+ Integer(http_status) rescue 0
37
36
  end
38
37
 
39
38
  # Length of content as an integer, or nil if chunked/unspecified
@@ -83,9 +82,9 @@ module EventMachine
83
82
 
84
83
  # Escapes a URI.
85
84
  def escape(s)
86
- s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
87
- '%'+$1.unpack('H2'*$1.bytesize).join('%').upcase
88
- }.tr(' ', '+')
85
+ s.to_s.gsub(/([^a-zA-Z0-9_.-]+)/n) {
86
+ '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
87
+ }
89
88
  end
90
89
 
91
90
  # Unescapes a URI escaped string.
@@ -95,6 +94,16 @@ module EventMachine
95
94
  }
96
95
  end
97
96
 
97
+ if ''.respond_to?(:bytesize)
98
+ def bytesize(string)
99
+ string.bytesize
100
+ end
101
+ else
102
+ def bytesize(string)
103
+ string.size
104
+ end
105
+ end
106
+
98
107
  # Map all header keys to a downcased string version
99
108
  def munge_header_keys(head)
100
109
  head.inject({}) { |h, (k, v)| h[k.to_s.downcase] = v; h }
@@ -111,21 +120,27 @@ module EventMachine
111
120
  end
112
121
  end
113
122
 
114
- def encode_request(method, path, query, uri_query)
115
- HTTP_REQUEST_HEADER % [method.to_s.upcase, encode_query(path, query, uri_query)]
123
+ def encode_request(method, uri, query, proxy)
124
+ query = encode_query(uri, query)
125
+
126
+ # Non CONNECT proxies require that you provide the full request
127
+ # uri in request header, as opposed to a relative path.
128
+ query = uri.join(query) if proxy && proxy[:type] != :socks && !proxy[:use_connect]
129
+
130
+ HTTP_REQUEST_HEADER % [method.to_s.upcase, query]
116
131
  end
117
132
 
118
- def encode_query(path, query, uri_query)
133
+ def encode_query(uri, query)
119
134
  encoded_query = if query.kind_of?(Hash)
120
135
  query.map { |k, v| encode_param(k, v) }.join('&')
121
136
  else
122
137
  query.to_s
123
138
  end
124
- if !uri_query.to_s.empty?
125
- encoded_query = [encoded_query, uri_query].reject {|part| part.empty?}.join("&")
139
+
140
+ if !uri.query.to_s.empty?
141
+ encoded_query = [encoded_query, uri.query].reject {|part| part.empty?}.join("&")
126
142
  end
127
- return path if encoded_query.to_s.empty?
128
- "#{path}?#{encoded_query}"
143
+ encoded_query.to_s.empty? ? uri.path : "#{uri.path}?#{encoded_query}"
129
144
  end
130
145
 
131
146
  # URL encodes query parameters:
@@ -138,6 +153,29 @@ module EventMachine
138
153
  end
139
154
  end
140
155
 
156
+ def form_encode_body(obj)
157
+ pairs = []
158
+ recursive = Proc.new do |h, prefix|
159
+ h.each do |k,v|
160
+ key = prefix == '' ? escape(k) : "#{prefix}[#{escape(k)}]"
161
+
162
+ if v.is_a? Array
163
+ nh = Hash.new
164
+ v.size.times { |t| nh[t] = v[t] }
165
+ recursive.call(nh, key)
166
+
167
+ elsif v.is_a? Hash
168
+ recursive.call(v, key)
169
+ else
170
+ pairs << "#{key}=#{escape(v)}"
171
+ end
172
+ end
173
+ end
174
+
175
+ recursive.call(obj, '')
176
+ return pairs.join('&')
177
+ end
178
+
141
179
  # Encode a field in an HTTP header
142
180
  def encode_field(k, v)
143
181
  FIELD_ENCODING % [k, v]
@@ -183,10 +221,13 @@ module EventMachine
183
221
  TRANSFER_ENCODING="TRANSFER_ENCODING"
184
222
  CONTENT_ENCODING="CONTENT_ENCODING"
185
223
  CONTENT_LENGTH="CONTENT_LENGTH"
224
+ LAST_MODIFIED="LAST_MODIFIED"
186
225
  KEEP_ALIVE="CONNECTION"
187
226
  SET_COOKIE="SET_COOKIE"
188
227
  LOCATION="LOCATION"
189
228
  HOST="HOST"
229
+ ETAG="ETAG"
230
+
190
231
  CRLF="\r\n"
191
232
 
192
233
  attr_accessor :method, :options, :uri
@@ -206,19 +247,28 @@ module EventMachine
206
247
  @stream = nil
207
248
  @disconnect = nil
208
249
  @state = :response_header
250
+ @socks_state = nil
209
251
  end
210
252
 
211
253
  # start HTTP request once we establish connection to host
212
254
  def connection_completed
213
- # if connecting to proxy, then first negotiate the connection
214
- # to intermediate server and wait for 200 response
215
- if @options[:proxy] and @state == :response_header
216
- @state = :response_proxy
255
+ # if a socks proxy is specified, then a connection request
256
+ # has to be made to the socks server and we need to wait
257
+ # for a response code
258
+ if socks_proxy? and @state == :response_header
259
+ @state = :connect_socks_proxy
260
+ send_socks_handshake
261
+
262
+ # if we need to negotiate the proxy connection first, then
263
+ # issue a CONNECT query and wait for 200 response
264
+ elsif connect_proxy? and @state == :response_header
265
+ @state = :connect_http_proxy
217
266
  send_request_header
218
267
 
219
268
  # if connecting via proxy, then state will be :proxy_connected,
220
269
  # indicating successful tunnel. from here, initiate normal http
221
270
  # exchange
271
+
222
272
  else
223
273
  @state = :response_header
224
274
  ssl = @options[:tls] || @options[:ssl] || {}
@@ -247,6 +297,7 @@ module EventMachine
247
297
  # fail the connection directly
248
298
  dns_error == true ? fail(self) : unbind
249
299
  end
300
+ alias :close :on_error
250
301
 
251
302
  # assign a stream processing block
252
303
  def stream(&blk)
@@ -258,6 +309,11 @@ module EventMachine
258
309
  @disconnect = blk
259
310
  end
260
311
 
312
+ # assign a headers parse callback
313
+ def headers(&blk)
314
+ @headers = blk
315
+ end
316
+
261
317
  # raw data push from the client (WebSocket) should
262
318
  # only be invoked after handshake, otherwise it will
263
319
  # inject data into the header exchange
@@ -275,31 +331,75 @@ module EventMachine
275
331
  def normalize_body
276
332
  @normalized_body ||= begin
277
333
  if @options[:body].is_a? Hash
278
- @options[:body].to_params
334
+ form_encode_body(@options[:body])
279
335
  else
280
336
  @options[:body]
281
337
  end
282
338
  end
283
339
  end
284
340
 
341
+ # determines if there is enough data in the buffer
342
+ def has_bytes?(num)
343
+ @data.size >= num
344
+ end
345
+
285
346
  def websocket?; @uri.scheme == 'ws'; end
347
+ def proxy?; !@options[:proxy].nil?; end
348
+
349
+ # determines if a proxy should be used that uses
350
+ # http-headers as proxy-mechanism
351
+ #
352
+ # this is the default proxy type if none is specified
353
+ def http_proxy?; proxy? && [nil, :http].include?(@options[:proxy][:type]); end
354
+
355
+ # determines if a http-proxy should be used with
356
+ # the CONNECT verb
357
+ def connect_proxy?; http_proxy? && (@options[:proxy][:use_connect] == true); end
358
+
359
+ # determines if a SOCKS5 proxy should be used
360
+ def socks_proxy?; proxy? && (@options[:proxy][:type] == :socks); end
361
+
362
+ def socks_methods
363
+ methods = []
364
+ methods << 2 if !options[:proxy][:authorization].nil? # 2 => Username/Password Authentication
365
+ methods << 0 # 0 => No Authentication Required
366
+
367
+ methods
368
+ end
369
+
370
+ def send_socks_handshake
371
+ # Method Negotiation as described on
372
+ # http://www.faqs.org/rfcs/rfc1928.html Section 3
373
+
374
+ @socks_state = :method_negotiation
375
+
376
+ methods = socks_methods
377
+ send_data [5, methods.size].pack('CC') + methods.pack('C*')
378
+ end
286
379
 
287
380
  def send_request_header
288
381
  query = @options[:query]
289
382
  head = @options[:head] ? munge_header_keys(@options[:head]) : {}
290
383
  file = @options[:file]
384
+ proxy = @options[:proxy]
291
385
  body = normalize_body
292
- request_header = nil
293
386
 
294
- if @state == :response_proxy
295
- proxy = @options[:proxy]
387
+ request_header = nil
296
388
 
297
- # initialize headers to establish the HTTP tunnel
389
+ if http_proxy?
390
+ # initialize headers for the http proxy
298
391
  head = proxy[:head] ? munge_header_keys(proxy[:head]) : {}
299
392
  head['proxy-authorization'] = proxy[:authorization] if proxy[:authorization]
300
- request_header = HTTP_REQUEST_HEADER % ['CONNECT', "#{@uri.host}:#{@uri.port}"]
301
393
 
302
- elsif websocket?
394
+ # if we need to negotiate the tunnel connection first, then
395
+ # issue a CONNECT query to the proxy first. This is an optional
396
+ # flag, by default we will provide full URIs to the proxy
397
+ if @state == :connect_http_proxy
398
+ request_header = HTTP_REQUEST_HEADER % ['CONNECT', "#{@uri.host}:#{@uri.port}"]
399
+ end
400
+ end
401
+
402
+ if websocket?
303
403
  head['upgrade'] = 'WebSocket'
304
404
  head['connection'] = 'Upgrade'
305
405
  head['origin'] = @options[:origin] || @uri.host
@@ -332,7 +432,7 @@ module EventMachine
332
432
  @last_effective_url = @uri
333
433
 
334
434
  # Build the request headers
335
- request_header ||= encode_request(@method, @uri.path, query, @uri.query)
435
+ request_header ||= encode_request(@method, @uri, query, proxy)
336
436
  request_header << encode_headers(head)
337
437
  request_header << CRLF
338
438
  send_data request_header
@@ -376,19 +476,24 @@ module EventMachine
376
476
 
377
477
  def unbind
378
478
  if (@state == :finished) && (@last_effective_url != @uri) && (@redirects < @options[:redirects])
379
- # update uri to redirect location if we're allowed to traverse deeper
380
- @uri = @last_effective_url
479
+ begin
480
+ # update uri to redirect location if we're allowed to traverse deeper
481
+ @uri = @last_effective_url
381
482
 
382
- # keep track of the depth of requests we made in this session
383
- @redirects += 1
483
+ # keep track of the depth of requests we made in this session
484
+ @redirects += 1
384
485
 
385
- # swap current connection and reassign current handler
386
- req = HttpOptions.new(@method, @uri, @options)
387
- reconnect(req.host, req.port)
486
+ # swap current connection and reassign current handler
487
+ req = HttpOptions.new(@method, @uri, @options)
488
+ reconnect(req.host, req.port)
388
489
 
389
- @response_header = HttpResponseHeader.new
390
- @state = :response_header
391
- @data.clear
490
+ @response_header = HttpResponseHeader.new
491
+ @state = :response_header
492
+ @response = ''
493
+ @data.clear
494
+ rescue EventMachine::ConnectionError => e
495
+ on_error(e.message, true)
496
+ end
392
497
  else
393
498
  if @state == :finished || (@state == :body && @bytes_remaining.nil?)
394
499
  succeed(self)
@@ -405,7 +510,9 @@ module EventMachine
405
510
 
406
511
  def dispatch
407
512
  while case @state
408
- when :response_proxy
513
+ when :connect_socks_proxy
514
+ parse_socks_response
515
+ when :connect_http_proxy
409
516
  parse_response_header
410
517
  when :response_header
411
518
  parse_response_header
@@ -451,13 +558,17 @@ module EventMachine
451
558
  def parse_response_header
452
559
  return false unless parse_header(@response_header)
453
560
 
561
+ # invoke headers callback after full parse if one
562
+ # is specified by the user
563
+ @headers.call(@response_header) if @headers
564
+
454
565
  unless @response_header.http_status and @response_header.http_reason
455
566
  @state = :invalid
456
567
  on_error "no HTTP response"
457
568
  return false
458
569
  end
459
570
 
460
- if @state == :response_proxy
571
+ if @state == :connect_http_proxy
461
572
  # when a successfull tunnel is established, the proxy responds with a
462
573
  # 200 response code. from here, the tunnel is transparent.
463
574
  if @response_header.http_status.to_i == 200
@@ -475,9 +586,13 @@ module EventMachine
475
586
  if @response_header.location
476
587
  begin
477
588
  location = Addressable::URI.parse(@response_header.location)
589
+
478
590
  if location.relative?
479
591
  location = @uri.join(location)
480
592
  @response_header[LOCATION] = location.to_s
593
+ else
594
+ # if redirect is to an absolute url, check for correct URI structure
595
+ raise if location.host.nil?
481
596
  end
482
597
 
483
598
  # store last url on any sign of redirect
@@ -489,10 +604,13 @@ module EventMachine
489
604
  end
490
605
  end
491
606
 
492
- # shortcircuit on HEAD requests
607
+ # Fire callbacks immediately after recieving header requests
608
+ # if the request method is HEAD. In case of a redirect, terminate
609
+ # current connection and reinitialize the process.
493
610
  if @method == "HEAD"
494
611
  @state = :finished
495
- unbind
612
+ close_connection
613
+ return false
496
614
  end
497
615
 
498
616
  if websocket?
@@ -524,6 +642,114 @@ module EventMachine
524
642
  true
525
643
  end
526
644
 
645
+ def send_socks_connect_request
646
+ # TO-DO: Implement address types for IPv6 and Domain
647
+ begin
648
+ ip_address = Socket.gethostbyname(@uri.host).last
649
+ send_data [5, 1, 0, 1, ip_address, @uri.port].flatten.pack('CCCCA4n')
650
+
651
+ rescue
652
+ @state = :invalid
653
+ on_error "could not resolve host", true
654
+ return false
655
+ end
656
+
657
+ true
658
+ end
659
+
660
+ # parses socks 5 server responses as specified
661
+ # on http://www.faqs.org/rfcs/rfc1928.html
662
+ def parse_socks_response
663
+ if @socks_state == :method_negotiation
664
+ return false unless has_bytes? 2
665
+
666
+ _, method = @data.read(2).unpack('CC')
667
+
668
+ if socks_methods.include?(method)
669
+ if method == 0
670
+ @socks_state = :connecting
671
+
672
+ return send_socks_connect_request
673
+
674
+ elsif method == 2
675
+ @socks_state = :authenticating
676
+
677
+ credentials = @options[:proxy][:authorization]
678
+ if credentials.size < 2
679
+ @state = :invalid
680
+ on_error "username and password are not supplied"
681
+ return false
682
+ end
683
+
684
+ username, password = credentials
685
+
686
+ send_data [5, username.length, username, password.length, password].pack('CCA*CA*')
687
+ end
688
+
689
+ else
690
+ @state = :invalid
691
+ on_error "proxy did not accept method"
692
+ return false
693
+ end
694
+
695
+ elsif @socks_state == :authenticating
696
+ return false unless has_bytes? 2
697
+
698
+ _, status_code = @data.read(2).unpack('CC')
699
+
700
+ if status_code == 0
701
+ # success
702
+ @socks_state = :connecting
703
+
704
+ return send_socks_connect_request
705
+
706
+ else
707
+ # error
708
+ @state = :invalid
709
+ on_error "access denied by proxy"
710
+ return false
711
+ end
712
+
713
+ elsif @socks_state == :connecting
714
+ return false unless has_bytes? 10
715
+
716
+ _, response_code, _, address_type, _, _ = @data.read(10).unpack('CCCCNn')
717
+
718
+ if response_code == 0
719
+ # success
720
+ @socks_state = :connected
721
+ @state = :proxy_connected
722
+
723
+ @response_header = HttpResponseHeader.new
724
+
725
+ # connection_completed will invoke actions to
726
+ # start sending all http data transparently
727
+ # over the socks connection
728
+ connection_completed
729
+
730
+ else
731
+ # error
732
+ @state = :invalid
733
+
734
+ error_messages = {
735
+ 1 => "general socks server failure",
736
+ 2 => "connection not allowed by ruleset",
737
+ 3 => "network unreachable",
738
+ 4 => "host unreachable",
739
+ 5 => "connection refused",
740
+ 6 => "TTL expired",
741
+ 7 => "command not supported",
742
+ 8 => "address type not supported"
743
+ }
744
+ error_message = error_messages[response_code] || "unknown error (code: #{response_code})"
745
+ on_error "socks5 connect error: #{error_message}"
746
+ return false
747
+ end
748
+ end
749
+
750
+ true
751
+ end
752
+
527
753
  def parse_chunk_header
528
754
  return false unless parse_header(@chunk_header)
529
755
 
@@ -628,8 +854,8 @@ module EventMachine
628
854
  # slice the message out of the buffer and pass in
629
855
  # for processing, and buffer data otherwise
630
856
  buffer = @data.read
631
- while msg = buffer.slice!(/\000([^\377]*)\377/)
632
- msg.gsub!(/^\x00|\xff$/, '')
857
+ while msg = buffer.slice!(/\000([^\377]*)\377/n)
858
+ msg.gsub!(/\A\x00|\xff\z/n, '')
633
859
  @stream.call(msg)
634
860
  end
635
861
 
@@ -2,7 +2,7 @@ class HttpOptions
2
2
  attr_reader :uri, :method, :host, :port, :options
3
3
 
4
4
  def initialize(method, uri, options)
5
- raise ArgumentError, "invalid request path" unless /^\// === uri.path
5
+ uri.normalize!
6
6
 
7
7
  @options = options
8
8
  @method = method.to_s.upcase
@@ -12,7 +12,9 @@ class HttpOptions
12
12
  @host = proxy[:host]
13
13
  @port = proxy[:port]
14
14
  else
15
- @host = uri.host
15
+ # optional host for cases where you may have
16
+ # pre-resolved the host, or you need an override
17
+ @host = options.delete(:host) || uri.host
16
18
  @port = uri.port
17
19
  end
18
20
 
data/lib/em-http/mock.rb CHANGED
@@ -1,91 +1,131 @@
1
1
  module EventMachine
2
+ OriginalHttpRequest = HttpRequest unless const_defined?(:OriginalHttpRequest)
3
+
2
4
  class MockHttpRequest < EventMachine::HttpRequest
3
-
5
+
4
6
  include HttpEncoding
5
-
6
- class FakeHttpClient < EventMachine::HttpClient
7
7
 
8
+ class RegisteredRequest < Struct.new(:uri, :method, :headers)
9
+ def self.build(uri, method, headers)
10
+ new(uri, method.to_s.upcase, headers || {})
11
+ end
12
+ end
13
+
14
+ class FakeHttpClient < EventMachine::HttpClient
15
+ attr_writer :response
16
+ attr_reader :data
8
17
  def setup(response, uri)
9
18
  @uri = uri
10
19
  if response == :fail
11
20
  fail(self)
12
21
  else
13
- receive_data(response)
14
- succeed(self)
22
+ if response.respond_to?(:call)
23
+ response.call(self)
24
+ @state = :body
25
+ else
26
+ receive_data(response)
27
+ end
28
+ @state == :body ? succeed(self) : fail(self)
15
29
  end
16
30
  end
17
-
31
+
18
32
  def unbind
19
33
  end
20
-
21
34
  end
22
-
23
- @@registry = nil
24
- @@registry_count = nil
25
-
35
+
36
+ @@registry = Hash.new
37
+ @@registry_count = Hash.new{|h,k| h[k] = 0}
38
+
39
+ def self.use
40
+ activate!
41
+ yield
42
+ ensure
43
+ deactivate!
44
+ end
45
+
46
+ def self.activate!
47
+ EventMachine.send(:remove_const, :HttpRequest)
48
+ EventMachine.send(:const_set, :HttpRequest, MockHttpRequest)
49
+ end
50
+
51
+ def self.deactivate!
52
+ EventMachine.send(:remove_const, :HttpRequest)
53
+ EventMachine.send(:const_set, :HttpRequest, OriginalHttpRequest)
54
+ end
55
+
26
56
  def self.reset_counts!
27
- @@registry_count = Hash.new do |registry,query|
28
- registry[query] = Hash.new{|h,k| h[k] = Hash.new(0)}
29
- end
57
+ @@registry_count.clear
30
58
  end
31
-
59
+
32
60
  def self.reset_registry!
33
- @@registry = Hash.new do |registry,query|
34
- registry[query] = Hash.new{|h,k| h[k] = {}}
35
- end
61
+ @@registry.clear
36
62
  end
37
-
38
- reset_counts!
39
- reset_registry!
40
-
63
+
41
64
  @@pass_through_requests = true
42
65
 
43
66
  def self.pass_through_requests=(pass_through_requests)
44
67
  @@pass_through_requests = pass_through_requests
45
68
  end
46
-
69
+
47
70
  def self.pass_through_requests
48
71
  @@pass_through_requests
49
72
  end
50
-
51
- def self.register(uri, method, headers, data)
52
- method = method.to_s.upcase
53
- headers = headers.to_s
54
- @@registry[uri][method][headers] = data
73
+
74
+ def self.parse_register_args(args, &proc)
75
+ args << proc{|client| proc.call(client); ''} if proc
76
+ headers, data = case args.size
77
+ when 3
78
+ args[2].is_a?(Hash) ?
79
+ [args[2][:headers], args[2][:data]] :
80
+ [{}, args[2]]
81
+ when 4
82
+ [args[2], args[3]]
83
+ else
84
+ raise
85
+ end
86
+
87
+ url = args[0]
88
+ method = args[1]
89
+ [headers, url, method, data]
55
90
  end
56
-
57
- def self.register_file(uri, method, headers, file)
58
- register(uri, method, headers, File.read(file))
91
+
92
+ def self.register(*args, &proc)
93
+ headers, url, method, data = parse_register_args(args, &proc)
94
+ @@registry[RegisteredRequest.build(url, method, headers)] = data
59
95
  end
60
-
61
- def self.count(uri, method, headers)
62
- method = method.to_s.upcase
63
- headers = headers.to_s
64
- @@registry_count[uri][method][headers] rescue 0
96
+
97
+ def self.register_file(*args)
98
+ headers, url, method, data = parse_register_args(args)
99
+ @@registry[RegisteredRequest.build(url, method, headers)] = File.read(data)
100
+ end
101
+
102
+ def self.count(url, method, headers = {})
103
+ @@registry_count[RegisteredRequest.build(url, method, headers)]
65
104
  end
66
-
67
- def self.registered?(query, method, headers)
68
- @@registry[query] and @@registry[query][method] and @@registry[query][method][headers]
105
+
106
+ def self.registered?(url, method, headers = {})
107
+ @@registry.key?(RegisteredRequest.build(url, method, headers))
69
108
  end
70
-
71
- def self.registered_content(query, method, headers)
72
- @@registry[query][method][headers]
109
+
110
+ def self.registered_content(url, method, headers = {})
111
+ @@registry[RegisteredRequest.build(url, method, headers)]
73
112
  end
74
-
75
- def self.increment_access(query, method, headers)
76
- @@registry_count[query][method][headers] += 1
113
+
114
+ def self.increment_access(url, method, headers = {})
115
+ @@registry_count[RegisteredRequest.build(url, method, headers)] += 1
77
116
  end
78
-
117
+
79
118
  alias_method :real_send_request, :send_request
80
-
119
+
81
120
  protected
82
121
  def send_request(&blk)
83
- query = "#{@req.uri.scheme}://#{@req.uri.host}:#{@req.uri.port}#{encode_query(@req.uri.path, @req.options[:query], @req.uri.query)}"
84
- headers = @req.options[:head].to_s
122
+ query = "#{@req.uri.scheme}://#{@req.uri.host}:#{@req.uri.port}#{encode_query(@req.uri, @req.options[:query])}"
123
+ headers = @req.options[:head]
85
124
  if self.class.registered?(query, @req.method, headers)
86
125
  self.class.increment_access(query, @req.method, headers)
87
126
  client = FakeHttpClient.new(nil)
88
- client.setup(self.class.registered_content(query, @req.method, headers), @req.uri)
127
+ content = self.class.registered_content(query, @req.method, headers)
128
+ client.setup(content, @req.uri)
89
129
  client
90
130
  elsif @@pass_through_requests
91
131
  real_send_request