em-http-request-samesite 1.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +9 -0
  4. data/.rspec +0 -0
  5. data/.travis.yml +7 -0
  6. data/Changelog.md +68 -0
  7. data/Gemfile +14 -0
  8. data/README.md +63 -0
  9. data/Rakefile +10 -0
  10. data/benchmarks/clients.rb +170 -0
  11. data/benchmarks/em-excon.rb +87 -0
  12. data/benchmarks/em-profile.gif +0 -0
  13. data/benchmarks/em-profile.txt +65 -0
  14. data/benchmarks/server.rb +48 -0
  15. data/em-http-request.gemspec +32 -0
  16. data/examples/.gitignore +1 -0
  17. data/examples/digest_auth/client.rb +25 -0
  18. data/examples/digest_auth/server.rb +28 -0
  19. data/examples/fetch.rb +30 -0
  20. data/examples/fibered-http.rb +51 -0
  21. data/examples/multi.rb +25 -0
  22. data/examples/oauth-tweet.rb +35 -0
  23. data/examples/socks5.rb +23 -0
  24. data/lib/em-http-request.rb +1 -0
  25. data/lib/em-http.rb +20 -0
  26. data/lib/em-http/client.rb +341 -0
  27. data/lib/em-http/core_ext/bytesize.rb +6 -0
  28. data/lib/em-http/decoders.rb +252 -0
  29. data/lib/em-http/http_client_options.rb +49 -0
  30. data/lib/em-http/http_connection.rb +321 -0
  31. data/lib/em-http/http_connection_options.rb +70 -0
  32. data/lib/em-http/http_encoding.rb +149 -0
  33. data/lib/em-http/http_header.rb +83 -0
  34. data/lib/em-http/http_status_codes.rb +57 -0
  35. data/lib/em-http/middleware/digest_auth.rb +112 -0
  36. data/lib/em-http/middleware/json_response.rb +15 -0
  37. data/lib/em-http/middleware/oauth.rb +40 -0
  38. data/lib/em-http/middleware/oauth2.rb +28 -0
  39. data/lib/em-http/multi.rb +57 -0
  40. data/lib/em-http/request.rb +23 -0
  41. data/lib/em-http/version.rb +5 -0
  42. data/lib/em/io_streamer.rb +49 -0
  43. data/spec/client_fiber_spec.rb +23 -0
  44. data/spec/client_spec.rb +1000 -0
  45. data/spec/digest_auth_spec.rb +48 -0
  46. data/spec/dns_spec.rb +41 -0
  47. data/spec/encoding_spec.rb +49 -0
  48. data/spec/external_spec.rb +150 -0
  49. data/spec/fixtures/google.ca +16 -0
  50. data/spec/fixtures/gzip-sample.gz +0 -0
  51. data/spec/gzip_spec.rb +91 -0
  52. data/spec/helper.rb +31 -0
  53. data/spec/http_proxy_spec.rb +268 -0
  54. data/spec/middleware/oauth2_spec.rb +15 -0
  55. data/spec/middleware_spec.rb +143 -0
  56. data/spec/multi_spec.rb +104 -0
  57. data/spec/pipelining_spec.rb +66 -0
  58. data/spec/redirect_spec.rb +430 -0
  59. data/spec/socksify_proxy_spec.rb +60 -0
  60. data/spec/spec_helper.rb +25 -0
  61. data/spec/ssl_spec.rb +71 -0
  62. data/spec/stallion.rb +334 -0
  63. data/spec/stub_server.rb +45 -0
  64. metadata +265 -0
@@ -0,0 +1,60 @@
1
+ require 'helper'
2
+
3
+ requires_connection do
4
+
5
+ requires_port(8080) do
6
+ describe EventMachine::HttpRequest do
7
+
8
+ # ssh -D 8080 igvita
9
+ let(:proxy) { {:proxy => { :host => '127.0.0.1', :port => 8080, :type => :socks5 }} }
10
+
11
+ it "should use SOCKS5 proxy" do
12
+ EventMachine.run {
13
+ http = EventMachine::HttpRequest.new('http://jsonip.com/', proxy).get
14
+
15
+ http.errback { failed(http) }
16
+ http.callback {
17
+ http.response_header.status.should == 200
18
+ http.response.should match('173.230.151.99')
19
+ EventMachine.stop
20
+ }
21
+ }
22
+ end
23
+ end
24
+ end
25
+
26
+ requires_port(8081) do
27
+ describe EventMachine::HttpRequest do
28
+
29
+ # brew install tinyproxy
30
+ let(:http_proxy) { {:proxy => { :host => '127.0.0.1', :port => 8081 }} }
31
+
32
+ it "should use HTTP proxy by default" do
33
+ EventMachine.run {
34
+ http = EventMachine::HttpRequest.new('http://jsonip.com/', http_proxy).get
35
+
36
+ http.errback { failed(http) }
37
+ http.callback {
38
+ http.response_header.status.should == 200
39
+ http.response.should match(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/)
40
+ EventMachine.stop
41
+ }
42
+ }
43
+ end
44
+
45
+ it "should auto CONNECT via HTTP proxy for HTTPS requests" do
46
+ EventMachine.run {
47
+ http = EventMachine::HttpRequest.new('https://ipjson.herokuapp.com/', http_proxy).get
48
+
49
+ http.errback { failed(http) }
50
+ http.callback {
51
+ http.response_header.status.should == 200
52
+ http.response.should match(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/)
53
+ EventMachine.stop
54
+ }
55
+ }
56
+ end
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,25 @@
1
+ PROXY_ENV_VARS = %w[HTTP_PROXY http_proxy HTTPS_PROXY https_proxy ALL_PROXY]
2
+
3
+ RSpec.configure do |config|
4
+ proxy_envs = {}
5
+
6
+ config.mock_with :rspec do |c|
7
+ c.syntax = [:should, :expect]
8
+ end
9
+ config.expect_with :rspec do |c|
10
+ c.syntax = [:should, :expect]
11
+ end
12
+
13
+ config.before :all do
14
+ # Back-up ENV *_PROXY vars
15
+ orig_proxy_envs = Hash[
16
+ PROXY_ENV_VARS.select {|k| ENV.key? k }.map {|k| [k, ENV.delete(k)] }
17
+ ]
18
+ proxy_envs.replace(orig_proxy_envs)
19
+ end
20
+
21
+ config.after :all do
22
+ # Restore ENV *_PROXY vars
23
+ ENV.update(proxy_envs)
24
+ end
25
+ end
data/spec/ssl_spec.rb ADDED
@@ -0,0 +1,71 @@
1
+ require 'helper'
2
+
3
+ requires_connection do
4
+
5
+ describe EventMachine::HttpRequest do
6
+ it "should initiate SSL/TLS on HTTPS connections" do
7
+ EventMachine.run {
8
+ http = EventMachine::HttpRequest.new('https://mail.google.com:443/mail/').get
9
+
10
+ http.errback { failed(http) }
11
+ http.callback {
12
+ http.response_header.status.should == 302
13
+ EventMachine.stop
14
+ }
15
+ }
16
+ end
17
+
18
+ describe "TLS hostname verification" do
19
+ before do
20
+ @cve_warning = "[WARNING; em-http-request] TLS hostname validation is disabled (use 'tls: {verify_peer: true}'), see" +
21
+ " CVE-2020-13482 and https://github.com/igrigorik/em-http-request/issues/339 for details"
22
+ @orig_stderr = $stderr
23
+ $stderr = StringIO.new
24
+ end
25
+
26
+ after do
27
+ $stderr = @orig_stderr
28
+ end
29
+
30
+ it "should not warn if verify_peer is specified" do
31
+ EventMachine.run {
32
+ http = EventMachine::HttpRequest.new('https://mail.google.com:443/mail', {tls: {verify_peer: false}}).get
33
+
34
+ http.callback {
35
+ $stderr.rewind
36
+ $stderr.string.chomp.should_not eq(@cve_warning)
37
+
38
+ EventMachine.stop
39
+ }
40
+ }
41
+ end
42
+
43
+ it "should not warn if verify_peer is true" do
44
+ EventMachine.run {
45
+ http = EventMachine::HttpRequest.new('https://mail.google.com:443/mail', {tls: {verify_peer: true}}).get
46
+
47
+ http.callback {
48
+ $stderr.rewind
49
+ $stderr.string.chomp.should_not eq(@cve_warning)
50
+
51
+ EventMachine.stop
52
+ }
53
+ }
54
+ end
55
+
56
+ it "should warn if verify_peer is unspecified" do
57
+ EventMachine.run {
58
+ http = EventMachine::HttpRequest.new('https://mail.google.com:443/mail').get
59
+
60
+ http.callback {
61
+ $stderr.rewind
62
+ $stderr.string.chomp.should eq(@cve_warning)
63
+
64
+ EventMachine.stop
65
+ }
66
+ }
67
+ end
68
+ end
69
+ end
70
+
71
+ end
data/spec/stallion.rb ADDED
@@ -0,0 +1,334 @@
1
+ # #--
2
+ # Includes portion originally Copyright (C)2008 Michael Fellinger
3
+ # MIT License
4
+ # #--
5
+
6
+ require 'rack'
7
+
8
+ module Stallion
9
+ class Mount
10
+ def initialize(name, *methods, &block)
11
+ @name, @methods, @block = name, methods, block
12
+ end
13
+
14
+ def ride
15
+ @block.call
16
+ end
17
+
18
+ def match?(request)
19
+ method = request['REQUEST_METHOD']
20
+ @methods.empty? or @methods.include?(method)
21
+ end
22
+ end
23
+
24
+ class Stable
25
+ attr_reader :request, :response
26
+
27
+ def initialize
28
+ @boxes = {}
29
+ end
30
+
31
+ def in(path, *methods, &block)
32
+ mount = Mount.new(path, *methods, &block)
33
+ @boxes[[path, methods]] = mount
34
+ mount
35
+ end
36
+
37
+ def call(request, response)
38
+ @request, @response = request, response
39
+ @boxes.each do |_, mount|
40
+ if mount.match?(request)
41
+ mount.ride
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ STABLES = {}
48
+
49
+ def self.saddle(name = nil)
50
+ STABLES[name] = stable = Stable.new
51
+ yield stable
52
+ end
53
+
54
+ def self.run(options = {})
55
+ options = {:Host => "127.0.0.1", :Port => 8090}.merge(options)
56
+
57
+ ruby_version = RUBY_VERSION.split('.').map(&:to_i)
58
+ if ruby_version[0] >= 3
59
+ Rack::Handler::Mongrel.run(Rack::Lint.new(self), **options)
60
+ else
61
+ Rack::Handler::Mongrel.run(Rack::Lint.new(self), options)
62
+ end
63
+ end
64
+
65
+ def self.call(env)
66
+ request = Rack::Request.new(env)
67
+ response = Rack::Response.new
68
+
69
+ STABLES.each do |name, stable|
70
+ stable.call(request, response)
71
+ end
72
+
73
+ response.finish
74
+ end
75
+ end
76
+
77
+ Stallion.saddle :spec do |stable|
78
+ stable.in '/' do
79
+
80
+ if stable.request.path_info == '/fail'
81
+ stable.response.status = 404
82
+
83
+ elsif stable.request.path_info == '/fail_with_nonstandard_response'
84
+ stable.response.status = 420
85
+
86
+ elsif stable.request.query_string == 'q=test'
87
+ stable.response.write 'test'
88
+
89
+ elsif stable.request.path_info == '/echo_query'
90
+ stable.response["ETag"] = "abcdefg"
91
+ stable.response["Last-Modified"] = "Fri, 13 Aug 2010 17:31:21 GMT"
92
+ stable.response.write stable.request.query_string
93
+
94
+ elsif stable.request.path_info == '/echo_headers'
95
+ stable.response["Set-Cookie"] = "test=yes"
96
+ stable.response["X-Forward-Host"] = "proxy.local"
97
+ stable.response.write stable.request.query_string
98
+
99
+ elsif stable.request.path_info == '/echo_content_length'
100
+ stable.response.write stable.request.content_length
101
+
102
+ elsif stable.request.path_info == '/echo_content_length_from_header'
103
+ stable.response.write "content-length:#{stable.request.env["CONTENT_LENGTH"]}"
104
+
105
+ elsif stable.request.path_info == '/echo_authorization_header'
106
+ stable.response.write "authorization:#{stable.request.env["HTTP_AUTHORIZATION"]}"
107
+
108
+ elsif stable.request.head? && stable.request.path_info == '/'
109
+ stable.response.status = 200
110
+
111
+ elsif stable.request.delete?
112
+ stable.response.status = 200
113
+
114
+ elsif stable.request.put?
115
+ stable.response.write stable.request.body.read
116
+
117
+ elsif stable.request.post? || stable.request.patch?
118
+ if stable.request.path_info == '/echo_content_type'
119
+ stable.response["Content-Type"] = stable.request.env["CONTENT_TYPE"] || 'text/html'
120
+ stable.response.write stable.request.env["CONTENT_TYPE"]
121
+ else
122
+ stable.response.write stable.request.body.read
123
+ end
124
+
125
+ elsif stable.request.path_info == '/set_cookie'
126
+ stable.response["Set-Cookie"] = "id=1; expires=Sat, 09 Aug 2031 17:53:39 GMT; path=/;"
127
+ stable.response.write "cookie set"
128
+
129
+ elsif stable.request.path_info == '/set_multiple_cookies'
130
+ stable.response["Set-Cookie"] = [
131
+ "id=1; expires=Sat, 09 Aug 2031 17:53:39 GMT; path=/;",
132
+ "id=2;"
133
+ ]
134
+ stable.response.write "cookies set"
135
+
136
+ elsif stable.request.path_info == '/echo_cookie'
137
+ stable.response.write stable.request.env["HTTP_COOKIE"]
138
+
139
+ elsif stable.request.path_info == '/timeout'
140
+ sleep(10)
141
+ stable.response.write 'timeout'
142
+
143
+ elsif stable.request.path_info == '/cookie_parrot'
144
+ stable.response.status = 200
145
+ stable.response["Set-Cookie"] = stable.request.env['HTTP_COOKIE']
146
+
147
+ elsif stable.request.path_info == '/redirect'
148
+ stable.response.status = 301
149
+ stable.response["Location"] = "/gzip"
150
+ stable.response.write 'redirect'
151
+
152
+ elsif stable.request.path_info == '/redirect/created'
153
+ stable.response.status = 201
154
+ stable.response["Location"] = "/"
155
+ stable.response.write 'Hello, World!'
156
+
157
+ elsif stable.request.path_info == '/redirect/multiple-with-cookie'
158
+ stable.response.status = 301
159
+ stable.response["Set-Cookie"] = "another_id=1; expires=Sat, 09 Aug 2031 17:53:39 GMT; path=/;"
160
+ stable.response["Location"] = "/redirect"
161
+ stable.response.write 'redirect'
162
+
163
+ elsif stable.request.path_info == '/redirect/bad'
164
+ stable.response.status = 301
165
+ stable.response["Location"] = "http://127.0.0.1:8090"
166
+
167
+ elsif stable.request.path_info == '/redirect/timeout'
168
+ stable.response.status = 301
169
+ stable.response["Location"] = "http://127.0.0.1:8090/timeout"
170
+
171
+ elsif stable.request.path_info == '/redirect/head'
172
+ stable.response.status = 301
173
+ stable.response["Location"] = "/"
174
+
175
+ elsif stable.request.path_info == '/redirect/middleware_redirects_1'
176
+ stable.response.status = 301
177
+ stable.response["EM-Middleware"] = stable.request.env["HTTP_EM_MIDDLEWARE"]
178
+ stable.response["Location"] = "/redirect/middleware_redirects_2"
179
+
180
+ elsif stable.request.path_info == '/redirect/middleware_redirects_2'
181
+ stable.response.status = 301
182
+ stable.response["EM-Middleware"] = stable.request.env["HTTP_EM_MIDDLEWARE"]
183
+ stable.response["Location"] = "/redirect/middleware_redirects_3"
184
+
185
+ elsif stable.request.path_info == '/redirect/middleware_redirects_3'
186
+ stable.response.status = 200
187
+ stable.response["EM-Middleware"] = stable.request.env["HTTP_EM_MIDDLEWARE"]
188
+
189
+ elsif stable.request.path_info == '/redirect/nohost'
190
+ stable.response.status = 301
191
+ stable.response["Location"] = "http:/"
192
+
193
+ elsif stable.request.path_info == '/redirect/badhost'
194
+ stable.response.status = 301
195
+ stable.response["Location"] = "http://$$$@$!%&^"
196
+
197
+ elsif stable.request.path_info == '/redirect/http_no_port'
198
+ stable.response.status = 301
199
+ stable.response["Location"] = "http://host/"
200
+
201
+ elsif stable.request.path_info == '/redirect/https_no_port'
202
+ stable.response.status = 301
203
+ stable.response["Location"] = "https://host/"
204
+
205
+ elsif stable.request.path_info == '/redirect/http_with_port'
206
+ stable.response.status = 301
207
+ stable.response["Location"] = "http://host:80/"
208
+
209
+ elsif stable.request.path_info == '/redirect/https_with_port'
210
+ stable.response.status = 301
211
+ stable.response["Location"] = "https://host:443/"
212
+
213
+ elsif stable.request.path_info == '/redirect/ignore_query_option'
214
+ stable.response.status = 301
215
+ stable.response['Location'] = '/redirect/url'
216
+
217
+ elsif stable.request.path_info == '/redirect/url'
218
+ stable.response.status = 200
219
+ stable.response.write stable.request.url
220
+
221
+ elsif stable.request.path_info == '/gzip'
222
+ io = StringIO.new
223
+ gzip = Zlib::GzipWriter.new(io)
224
+ gzip << "compressed"
225
+ gzip.close
226
+
227
+ stable.response.write io.string
228
+ stable.response["Content-Encoding"] = "gzip"
229
+
230
+ elsif stable.request.path_info == '/gzip-large'
231
+ contents = File.open(File.dirname(__FILE__) + "/fixtures/gzip-sample.gz", 'r') { |f| f.read }
232
+
233
+ stable.response.write contents
234
+ stable.response["Content-Encoding"] = "gzip"
235
+
236
+ elsif stable.request.path_info == '/deflate'
237
+ deflater = Zlib::Deflate.new(
238
+ Zlib::DEFAULT_COMPRESSION,
239
+ -Zlib::MAX_WBITS, # drop the zlib header which causes both Safari and IE to choke
240
+ Zlib::DEF_MEM_LEVEL,
241
+ Zlib::DEFAULT_STRATEGY
242
+ )
243
+ deflater.deflate("compressed")
244
+ stable.response.write deflater.finish
245
+ stable.response["Content-Encoding"] = "deflate"
246
+
247
+ elsif stable.request.path_info == '/echo_accept_encoding'
248
+ stable.response.status = 200
249
+ stable.response.write stable.request.env["HTTP_ACCEPT_ENCODING"]
250
+
251
+ elsif stable.request.env["HTTP_IF_NONE_MATCH"]
252
+ stable.response.status = 304
253
+
254
+ elsif stable.request.path_info == '/auth' && stable.request.env["HTTP_AUTHORIZATION"]
255
+ stable.response.status = 200
256
+ stable.response.write stable.request.env["HTTP_AUTHORIZATION"]
257
+ elsif stable.request.path_info == '/authtest'
258
+ auth = "Basic %s" % Base64.strict_encode64(['user', 'pass'].join(':')).split.join
259
+ if auth == stable.request.env["HTTP_AUTHORIZATION"]
260
+ stable.response.status = 200
261
+ stable.response.write 'success'
262
+ else
263
+ stable.response.status = 401
264
+ end
265
+ elsif stable.request.path_info == '/relative-location'
266
+ stable.response.status = 301
267
+ stable.response["Location"] = '/forwarded'
268
+ elsif stable.request.path_info == '/echo-user-agent'
269
+ stable.response.write stable.request.env["HTTP_USER_AGENT"].inspect
270
+
271
+ elsif
272
+ stable.response.write 'Hello, World!'
273
+ end
274
+
275
+ end
276
+ end
277
+
278
+ Thread.new do
279
+ begin
280
+ Stallion.run :Host => '127.0.0.1', :Port => 8090
281
+ rescue => e
282
+ print e
283
+ end
284
+ end
285
+
286
+ #
287
+ # Simple HTTP Proxy server
288
+ #
289
+ Thread.new do
290
+ server = TCPServer.new('127.0.0.1', 8083)
291
+ loop do
292
+ session = server.accept
293
+ request = ""
294
+ while (data = session.gets) != "\r\n"
295
+ request << data
296
+ end
297
+ parts = request.split("\r\n")
298
+ method, destination, http_version = parts.first.split(' ')
299
+ proxy = parts.find { |part| part =~ /Proxy-Authorization/ }
300
+ if destination =~ /^http:/
301
+ uri = Addressable::URI.parse(destination)
302
+ absolute_path = uri.path + (uri.query ? "?#{uri.query}" : "")
303
+ client = TCPSocket.open(uri.host, uri.port || 80)
304
+
305
+ client.write "#{method} #{absolute_path} #{http_version}\r\n"
306
+ parts[1..-1].each do |part|
307
+ client.write "#{part}\r\n"
308
+ end
309
+
310
+ client.write "\r\n"
311
+ client.flush
312
+ client.close_write
313
+
314
+ # Take the initial line from the upstream response
315
+ session.write client.gets
316
+
317
+ if proxy
318
+ session.write "X-Proxy-Auth: #{proxy}\r\n"
319
+ end
320
+
321
+ # What (absolute) uri was requested? Send it back in a header
322
+ session.write "X-The-Requested-URI: #{destination}\r\n"
323
+
324
+ while data = client.gets
325
+ session.write data
326
+ end
327
+ session.flush
328
+ client.close
329
+ end
330
+ session.close
331
+ end
332
+ end
333
+
334
+ sleep(1)