em-http-request 0.2.12 → 0.2.13
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.
Potentially problematic release.
This version of em-http-request might be problematic. Click here for more details.
- data/Changelog.md +5 -0
- data/README.md +12 -2
- data/VERSION +1 -1
- data/em-http-request.gemspec +4 -2
- data/examples/socks5.rb +26 -0
- data/lib/em-http.rb +20 -19
- data/lib/em-http/client.rb +167 -10
- data/lib/em-http/core_ext/bytesize.rb +5 -5
- data/lib/em-http/core_ext/hash.rb +53 -53
- data/spec/hash_spec.rb +24 -24
- data/spec/request_spec.rb +25 -0
- data/spec/spec.opts +6 -6
- data/spec/stallion.rb +5 -1
- metadata +5 -3
data/Changelog.md
CHANGED
data/README.md
CHANGED
@@ -9,7 +9,7 @@ Asynchronous HTTP client for Ruby, based on EventMachine runtime.
|
|
9
9
|
- Basic-Auth & OAuth support
|
10
10
|
- Custom timeout support
|
11
11
|
- Stream response processing
|
12
|
-
- Proxy support (with SSL Tunneling)
|
12
|
+
- Proxy support (with SSL Tunneling): CONNECT, direct & SOCKS5
|
13
13
|
- Auto-follow 3xx redirects with custom max depth
|
14
14
|
- Bi-directional communication with web-socket services
|
15
15
|
- [Native mocking support](http://wiki.github.com/igrigorik/em-http-request/mocking-httprequest) and through [Webmock](http://github.com/bblimke/webmock)
|
@@ -114,7 +114,6 @@ Allows you to efficiently stream a (large) file from disk via EventMachine's Fil
|
|
114
114
|
|
115
115
|
Proxy example
|
116
116
|
-------------
|
117
|
-
|
118
117
|
Full transparent proxy support with support for SSL tunneling.
|
119
118
|
|
120
119
|
EventMachine.run {
|
@@ -124,6 +123,17 @@ Full transparent proxy support with support for SSL tunneling.
|
|
124
123
|
:authorization => ['username', 'password'] # authorization is optional
|
125
124
|
}
|
126
125
|
|
126
|
+
SOCKS5 Proxy example
|
127
|
+
-------------
|
128
|
+
Tunnel your requests via connect via SOCKS5 proxies (ssh -D port somehost).
|
129
|
+
|
130
|
+
EventMachine.run {
|
131
|
+
http = EventMachine::HttpRequest.new('http://www.website.com/').get :proxy => {
|
132
|
+
:host => 'www.myproxy.com',
|
133
|
+
:port => 8080,
|
134
|
+
:type => :socks
|
135
|
+
}
|
136
|
+
|
127
137
|
Auto-follow 3xx redirects
|
128
138
|
-------------------------
|
129
139
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.13
|
data/em-http-request.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{em-http-request}
|
8
|
-
s.version = "0.2.
|
8
|
+
s.version = "0.2.13"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Ilya Grigorik"]
|
12
|
-
s.date = %q{2010-09-
|
12
|
+
s.date = %q{2010-09-25}
|
13
13
|
s.description = %q{EventMachine based, async HTTP Request interface}
|
14
14
|
s.email = %q{ilya@igvita.com}
|
15
15
|
s.extensions = ["ext/buffer/extconf.rb", "ext/http11_client/extconf.rb"]
|
@@ -28,6 +28,7 @@ Gem::Specification.new do |s|
|
|
28
28
|
"examples/fetch.rb",
|
29
29
|
"examples/fibered-http.rb",
|
30
30
|
"examples/oauth-tweet.rb",
|
31
|
+
"examples/socks5.rb",
|
31
32
|
"examples/websocket-handler.rb",
|
32
33
|
"examples/websocket-server.rb",
|
33
34
|
"ext/buffer/em_buffer.c",
|
@@ -76,6 +77,7 @@ Gem::Specification.new do |s|
|
|
76
77
|
"examples/fetch.rb",
|
77
78
|
"examples/fibered-http.rb",
|
78
79
|
"examples/oauth-tweet.rb",
|
80
|
+
"examples/socks5.rb",
|
79
81
|
"examples/websocket-handler.rb",
|
80
82
|
"examples/websocket-server.rb"
|
81
83
|
]
|
data/examples/socks5.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'eventmachine'
|
3
|
+
require '../lib/em-http'
|
4
|
+
|
5
|
+
EM.run do
|
6
|
+
# Establish a SOCKS5 tunnel via SSH
|
7
|
+
# ssh -D 8000 some_remote_machine
|
8
|
+
|
9
|
+
# http = EM::HttpRequest.new('http://whatismyip.org/').get({
|
10
|
+
http = EM::HttpRequest.new('http://igvita.com/').get({
|
11
|
+
:proxy => {:host => '127.0.0.1', :port => 8000, :type => :socks},
|
12
|
+
:redirects => 2
|
13
|
+
})
|
14
|
+
|
15
|
+
http.callback {
|
16
|
+
puts "#{http.response_header.status} - #{http.response.length} bytes\n"
|
17
|
+
puts http.response
|
18
|
+
EM.stop
|
19
|
+
}
|
20
|
+
|
21
|
+
http.errback {
|
22
|
+
puts "Error: " + http.error
|
23
|
+
puts http.inspect
|
24
|
+
EM.stop
|
25
|
+
}
|
26
|
+
end
|
data/lib/em-http.rb
CHANGED
@@ -1,20 +1,21 @@
|
|
1
|
-
#--
|
2
|
-
# Copyright (C)2008 Ilya Grigorik
|
3
|
-
# You can redistribute this under the terms of the Ruby license
|
4
|
-
# See file LICENSE for details
|
5
|
-
#++
|
6
|
-
|
7
|
-
require 'eventmachine'
|
8
|
-
|
9
|
-
|
10
|
-
require File.dirname(__FILE__) + '/
|
11
|
-
|
12
|
-
|
13
|
-
require File.dirname(__FILE__) + '/em-http/core_ext/
|
14
|
-
|
15
|
-
|
16
|
-
require File.dirname(__FILE__) + '/em-http/
|
17
|
-
require File.dirname(__FILE__) + '/em-http/
|
18
|
-
require File.dirname(__FILE__) + '/em-http/
|
19
|
-
require File.dirname(__FILE__) + '/em-http/
|
1
|
+
#--
|
2
|
+
# Copyright (C)2008 Ilya Grigorik
|
3
|
+
# You can redistribute this under the terms of the Ruby license
|
4
|
+
# See file LICENSE for details
|
5
|
+
#++
|
6
|
+
|
7
|
+
require 'eventmachine'
|
8
|
+
require 'socket'
|
9
|
+
|
10
|
+
require File.dirname(__FILE__) + '/http11_client'
|
11
|
+
require File.dirname(__FILE__) + '/em_buffer'
|
12
|
+
|
13
|
+
require File.dirname(__FILE__) + '/em-http/core_ext/hash'
|
14
|
+
require File.dirname(__FILE__) + '/em-http/core_ext/bytesize'
|
15
|
+
|
16
|
+
require File.dirname(__FILE__) + '/em-http/client'
|
17
|
+
require File.dirname(__FILE__) + '/em-http/multi'
|
18
|
+
require File.dirname(__FILE__) + '/em-http/request'
|
19
|
+
require File.dirname(__FILE__) + '/em-http/decoders'
|
20
|
+
require File.dirname(__FILE__) + '/em-http/http_options'
|
20
21
|
require File.dirname(__FILE__) + '/em-http/mock'
|
data/lib/em-http/client.rb
CHANGED
@@ -125,7 +125,7 @@ module EventMachine
|
|
125
125
|
|
126
126
|
# Non CONNECT proxies require that you provide the full request
|
127
127
|
# uri in request header, as opposed to a relative path.
|
128
|
-
query = uri.join(query) if proxy
|
128
|
+
query = uri.join(query) if proxy && proxy[:type] != :socks && !proxy[:use_connect]
|
129
129
|
|
130
130
|
HTTP_REQUEST_HEADER % [method.to_s.upcase, query]
|
131
131
|
end
|
@@ -224,19 +224,28 @@ module EventMachine
|
|
224
224
|
@stream = nil
|
225
225
|
@disconnect = nil
|
226
226
|
@state = :response_header
|
227
|
+
@socks_state = nil
|
227
228
|
end
|
228
229
|
|
229
230
|
# start HTTP request once we establish connection to host
|
230
231
|
def connection_completed
|
232
|
+
# if a socks proxy is specified, then a connection request
|
233
|
+
# has to be made to the socks server and we need to wait
|
234
|
+
# for a response code
|
235
|
+
if socks_proxy? and @state == :response_header
|
236
|
+
@state = :connect_socks_proxy
|
237
|
+
send_socks_handshake
|
238
|
+
|
231
239
|
# if we need to negotiate the proxy connection first, then
|
232
240
|
# issue a CONNECT query and wait for 200 response
|
233
|
-
|
234
|
-
@state = :
|
241
|
+
elsif connect_proxy? and @state == :response_header
|
242
|
+
@state = :connect_http_proxy
|
235
243
|
send_request_header
|
236
244
|
|
237
245
|
# if connecting via proxy, then state will be :proxy_connected,
|
238
246
|
# indicating successful tunnel. from here, initiate normal http
|
239
247
|
# exchange
|
248
|
+
|
240
249
|
else
|
241
250
|
@state = :response_header
|
242
251
|
ssl = @options[:tls] || @options[:ssl] || {}
|
@@ -306,9 +315,44 @@ module EventMachine
|
|
306
315
|
end
|
307
316
|
end
|
308
317
|
|
318
|
+
# determines if there is enough data in the buffer
|
319
|
+
def has_bytes?(num)
|
320
|
+
@data.size >= num
|
321
|
+
end
|
322
|
+
|
309
323
|
def websocket?; @uri.scheme == 'ws'; end
|
310
324
|
def proxy?; !@options[:proxy].nil?; end
|
311
|
-
|
325
|
+
|
326
|
+
# determines if a proxy should be used that uses
|
327
|
+
# http-headers as proxy-mechanism
|
328
|
+
#
|
329
|
+
# this is the default proxy type if none is specified
|
330
|
+
def http_proxy?; proxy? && [nil, :http].include?(@options[:proxy][:type]); end
|
331
|
+
|
332
|
+
# determines if a http-proxy should be used with
|
333
|
+
# the CONNECT verb
|
334
|
+
def connect_proxy?; http_proxy? && (@options[:proxy][:use_connect] == true); end
|
335
|
+
|
336
|
+
# determines if a SOCKS5 proxy should be used
|
337
|
+
def socks_proxy?; proxy? && (@options[:proxy][:type] == :socks); end
|
338
|
+
|
339
|
+
def socks_methods
|
340
|
+
methods = []
|
341
|
+
methods << 2 if !options[:proxy][:authorization].nil? # 2 => Username/Password Authentication
|
342
|
+
methods << 0 # 0 => No Authentication Required
|
343
|
+
|
344
|
+
methods
|
345
|
+
end
|
346
|
+
|
347
|
+
def send_socks_handshake
|
348
|
+
# Method Negotiation as described on
|
349
|
+
# http://www.faqs.org/rfcs/rfc1928.html Section 3
|
350
|
+
|
351
|
+
@socks_state = :method_negotiation
|
352
|
+
|
353
|
+
methods = socks_methods
|
354
|
+
send_data [5, methods.size].pack('CC') + methods.pack('C*')
|
355
|
+
end
|
312
356
|
|
313
357
|
def send_request_header
|
314
358
|
query = @options[:query]
|
@@ -319,7 +363,7 @@ module EventMachine
|
|
319
363
|
|
320
364
|
request_header = nil
|
321
365
|
|
322
|
-
if
|
366
|
+
if http_proxy?
|
323
367
|
# initialize headers for the http proxy
|
324
368
|
head = proxy[:head] ? munge_header_keys(proxy[:head]) : {}
|
325
369
|
head['proxy-authorization'] = proxy[:authorization] if proxy[:authorization]
|
@@ -327,7 +371,7 @@ module EventMachine
|
|
327
371
|
# if we need to negotiate the tunnel connection first, then
|
328
372
|
# issue a CONNECT query to the proxy first. This is an optional
|
329
373
|
# flag, by default we will provide full URIs to the proxy
|
330
|
-
if @state == :
|
374
|
+
if @state == :connect_http_proxy
|
331
375
|
request_header = HTTP_REQUEST_HEADER % ['CONNECT', "#{@uri.host}:#{@uri.port}"]
|
332
376
|
end
|
333
377
|
end
|
@@ -443,7 +487,9 @@ module EventMachine
|
|
443
487
|
|
444
488
|
def dispatch
|
445
489
|
while case @state
|
446
|
-
when :
|
490
|
+
when :connect_socks_proxy
|
491
|
+
parse_socks_response
|
492
|
+
when :connect_http_proxy
|
447
493
|
parse_response_header
|
448
494
|
when :response_header
|
449
495
|
parse_response_header
|
@@ -499,7 +545,7 @@ module EventMachine
|
|
499
545
|
return false
|
500
546
|
end
|
501
547
|
|
502
|
-
if @state == :
|
548
|
+
if @state == :connect_http_proxy
|
503
549
|
# when a successfull tunnel is established, the proxy responds with a
|
504
550
|
# 200 response code. from here, the tunnel is transparent.
|
505
551
|
if @response_header.http_status.to_i == 200
|
@@ -535,10 +581,13 @@ module EventMachine
|
|
535
581
|
end
|
536
582
|
end
|
537
583
|
|
538
|
-
#
|
584
|
+
# Fire callbacks immediately after recieving header requests
|
585
|
+
# if the request method is HEAD. In case of a redirect, terminate
|
586
|
+
# current connection and reinitialize the process.
|
539
587
|
if @method == "HEAD"
|
540
588
|
@state = :finished
|
541
|
-
|
589
|
+
close_connection
|
590
|
+
return false
|
542
591
|
end
|
543
592
|
|
544
593
|
if websocket?
|
@@ -570,6 +619,114 @@ module EventMachine
|
|
570
619
|
true
|
571
620
|
end
|
572
621
|
|
622
|
+
def send_socks_connect_request
|
623
|
+
# TO-DO: Implement address types for IPv6 and Domain
|
624
|
+
begin
|
625
|
+
ip_address = Socket.gethostbyname(@uri.host).last
|
626
|
+
send_data [5, 1, 0, 1, ip_address, @uri.port].flatten.pack('CCCCA4n')
|
627
|
+
|
628
|
+
rescue
|
629
|
+
@state = :invalid
|
630
|
+
on_error "could not resolve host", true
|
631
|
+
return false
|
632
|
+
end
|
633
|
+
|
634
|
+
true
|
635
|
+
end
|
636
|
+
|
637
|
+
# parses socks 5 server responses as specified
|
638
|
+
# on http://www.faqs.org/rfcs/rfc1928.html
|
639
|
+
def parse_socks_response
|
640
|
+
if @socks_state == :method_negotiation
|
641
|
+
return false unless has_bytes? 2
|
642
|
+
|
643
|
+
_, method = @data.read(2).unpack('CC')
|
644
|
+
|
645
|
+
if socks_methods.include?(method)
|
646
|
+
if method == 0
|
647
|
+
@socks_state = :connecting
|
648
|
+
|
649
|
+
return send_socks_connect_request
|
650
|
+
|
651
|
+
elsif method == 2
|
652
|
+
@socks_state = :authenticating
|
653
|
+
|
654
|
+
credentials = @options[:proxy][:authorization]
|
655
|
+
if credentials.size < 2
|
656
|
+
@state = :invalid
|
657
|
+
on_error "username and password are not supplied"
|
658
|
+
return false
|
659
|
+
end
|
660
|
+
|
661
|
+
username, password = credentials
|
662
|
+
|
663
|
+
send_data [5, username.length, username, password.length, password].pack('CCA*CA*')
|
664
|
+
end
|
665
|
+
|
666
|
+
else
|
667
|
+
@state = :invalid
|
668
|
+
on_error "proxy did not accept method"
|
669
|
+
return false
|
670
|
+
end
|
671
|
+
|
672
|
+
elsif @socks_state == :authenticating
|
673
|
+
return false unless has_bytes? 2
|
674
|
+
|
675
|
+
_, status_code = @data.read(2).unpack('CC')
|
676
|
+
|
677
|
+
if status_code == 0
|
678
|
+
# success
|
679
|
+
@socks_state = :connecting
|
680
|
+
|
681
|
+
return send_socks_connect_request
|
682
|
+
|
683
|
+
else
|
684
|
+
# error
|
685
|
+
@state = :invalid
|
686
|
+
on_error "access denied by proxy"
|
687
|
+
return false
|
688
|
+
end
|
689
|
+
|
690
|
+
elsif @socks_state == :connecting
|
691
|
+
return false unless has_bytes? 10
|
692
|
+
|
693
|
+
_, response_code, _, address_type, _, _ = @data.read(10).unpack('CCCCNn')
|
694
|
+
|
695
|
+
if response_code == 0
|
696
|
+
# success
|
697
|
+
@socks_state = :connected
|
698
|
+
@state = :proxy_connected
|
699
|
+
|
700
|
+
@response_header = HttpResponseHeader.new
|
701
|
+
|
702
|
+
# connection_completed will invoke actions to
|
703
|
+
# start sending all http data transparently
|
704
|
+
# over the socks connection
|
705
|
+
connection_completed
|
706
|
+
|
707
|
+
else
|
708
|
+
# error
|
709
|
+
@state = :invalid
|
710
|
+
|
711
|
+
error_messages = {
|
712
|
+
1 => "general socks server failure",
|
713
|
+
2 => "connection not allowed by ruleset",
|
714
|
+
3 => "network unreachable",
|
715
|
+
4 => "host unreachable",
|
716
|
+
5 => "connection refused",
|
717
|
+
6 => "TTL expired",
|
718
|
+
7 => "command not supported",
|
719
|
+
8 => "address type not supported"
|
720
|
+
}
|
721
|
+
error_message = error_messages[response_code] || "unknown error (code: #{response_code})"
|
722
|
+
on_error "socks5 connect error: #{error_message}"
|
723
|
+
return false
|
724
|
+
end
|
725
|
+
end
|
726
|
+
|
727
|
+
true
|
728
|
+
end
|
729
|
+
|
573
730
|
def parse_chunk_header
|
574
731
|
return false unless parse_header(@chunk_header)
|
575
732
|
|
@@ -1,6 +1,6 @@
|
|
1
|
-
# bytesize was introduced in 1.8.7+
|
2
|
-
if RUBY_VERSION <= "1.8.6"
|
3
|
-
class String
|
4
|
-
def bytesize; self.size; end
|
5
|
-
end
|
1
|
+
# bytesize was introduced in 1.8.7+
|
2
|
+
if RUBY_VERSION <= "1.8.6"
|
3
|
+
class String
|
4
|
+
def bytesize; self.size; end
|
5
|
+
end
|
6
6
|
end
|
@@ -1,53 +1,53 @@
|
|
1
|
-
class Hash
|
2
|
-
# Stolen partially from Merb : http://noobkit.com/show/ruby/gems/development/merb/hash/to_params.html
|
3
|
-
# Convert this hash to a query string:
|
4
|
-
#
|
5
|
-
# { :name => "Bob",
|
6
|
-
# :address => {
|
7
|
-
# :street => '111 Ruby Ave.',
|
8
|
-
# :city => 'Ruby Central',
|
9
|
-
# :phones => ['111-111-1111', '222-222-2222']
|
10
|
-
# }
|
11
|
-
# }.to_params
|
12
|
-
# #=> "name=Bob&address[city]=Ruby Central&address[phones]=111-111-1111222-222-2222&address[street]=111 Ruby Ave."
|
13
|
-
#
|
14
|
-
def to_params
|
15
|
-
params = ''
|
16
|
-
stack = []
|
17
|
-
|
18
|
-
each do |k, v|
|
19
|
-
if v.is_a?(Hash)
|
20
|
-
stack << [k,v]
|
21
|
-
elsif v.is_a?(Array)
|
22
|
-
stack << [k,Hash.from_array(v)]
|
23
|
-
else
|
24
|
-
params << "#{k}=#{v}&"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
stack.each do |parent, hash|
|
29
|
-
hash.each do |k, v|
|
30
|
-
if v.is_a?(Hash)
|
31
|
-
stack << ["#{parent}[#{k}]", v]
|
32
|
-
else
|
33
|
-
params << "#{parent}[#{k}]=#{v}&"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
params.chop! # trailing &
|
39
|
-
params
|
40
|
-
end
|
41
|
-
|
42
|
-
##
|
43
|
-
# Builds a hash from an array with keys as array indices.
|
44
|
-
def self.from_array(array = [])
|
45
|
-
h = Hash.new
|
46
|
-
array.size.times do |t|
|
47
|
-
h[t] = array[t]
|
48
|
-
end
|
49
|
-
h
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
53
|
-
|
1
|
+
class Hash
|
2
|
+
# Stolen partially from Merb : http://noobkit.com/show/ruby/gems/development/merb/hash/to_params.html
|
3
|
+
# Convert this hash to a query string:
|
4
|
+
#
|
5
|
+
# { :name => "Bob",
|
6
|
+
# :address => {
|
7
|
+
# :street => '111 Ruby Ave.',
|
8
|
+
# :city => 'Ruby Central',
|
9
|
+
# :phones => ['111-111-1111', '222-222-2222']
|
10
|
+
# }
|
11
|
+
# }.to_params
|
12
|
+
# #=> "name=Bob&address[city]=Ruby Central&address[phones]=111-111-1111222-222-2222&address[street]=111 Ruby Ave."
|
13
|
+
#
|
14
|
+
def to_params
|
15
|
+
params = ''
|
16
|
+
stack = []
|
17
|
+
|
18
|
+
each do |k, v|
|
19
|
+
if v.is_a?(Hash)
|
20
|
+
stack << [k,v]
|
21
|
+
elsif v.is_a?(Array)
|
22
|
+
stack << [k,Hash.from_array(v)]
|
23
|
+
else
|
24
|
+
params << "#{k}=#{v}&"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
stack.each do |parent, hash|
|
29
|
+
hash.each do |k, v|
|
30
|
+
if v.is_a?(Hash)
|
31
|
+
stack << ["#{parent}[#{k}]", v]
|
32
|
+
else
|
33
|
+
params << "#{parent}[#{k}]=#{v}&"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
params.chop! # trailing &
|
39
|
+
params
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Builds a hash from an array with keys as array indices.
|
44
|
+
def self.from_array(array = [])
|
45
|
+
h = Hash.new
|
46
|
+
array.size.times do |t|
|
47
|
+
h[t] = array[t]
|
48
|
+
end
|
49
|
+
h
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
data/spec/hash_spec.rb
CHANGED
@@ -1,24 +1,24 @@
|
|
1
|
-
require 'spec/helper'
|
2
|
-
|
3
|
-
describe Hash do
|
4
|
-
|
5
|
-
describe ".to_params" do
|
6
|
-
it "should transform a basic hash into HTTP POST Params" do
|
7
|
-
{:a => "alpha", :b => "beta"}.to_params.split("&").should include "a=alpha"
|
8
|
-
{:a => "alpha", :b => "beta"}.to_params.split("&").should include "b=beta"
|
9
|
-
end
|
10
|
-
|
11
|
-
it "should transform a more complex hash into HTTP POST Params" do
|
12
|
-
{:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "a=a"
|
13
|
-
{:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "b[0]=c"
|
14
|
-
{:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "b[1]=d"
|
15
|
-
{:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "b[2]=e"
|
16
|
-
end
|
17
|
-
|
18
|
-
it "should transform a very complex hash into HTTP POST Params" do
|
19
|
-
params = {:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}]}.to_params.split("&")
|
20
|
-
params.should include "a=a"
|
21
|
-
params.should include "b[0][d]=d"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
1
|
+
require 'spec/helper'
|
2
|
+
|
3
|
+
describe Hash do
|
4
|
+
|
5
|
+
describe ".to_params" do
|
6
|
+
it "should transform a basic hash into HTTP POST Params" do
|
7
|
+
{:a => "alpha", :b => "beta"}.to_params.split("&").should include "a=alpha"
|
8
|
+
{:a => "alpha", :b => "beta"}.to_params.split("&").should include "b=beta"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should transform a more complex hash into HTTP POST Params" do
|
12
|
+
{:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "a=a"
|
13
|
+
{:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "b[0]=c"
|
14
|
+
{:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "b[1]=d"
|
15
|
+
{:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "b[2]=e"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should transform a very complex hash into HTTP POST Params" do
|
19
|
+
params = {:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}]}.to_params.split("&")
|
20
|
+
params.should include "a=a"
|
21
|
+
params.should include "b[0][d]=d"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/spec/request_spec.rb
CHANGED
@@ -99,6 +99,31 @@ describe EventMachine::HttpRequest do
|
|
99
99
|
}
|
100
100
|
}
|
101
101
|
end
|
102
|
+
|
103
|
+
it "should follow redirects on HEAD method" do
|
104
|
+
EventMachine.run {
|
105
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect/head').head :redirects => 1
|
106
|
+
http.errback { failed }
|
107
|
+
http.callback {
|
108
|
+
http.response_header.status.should == 200
|
109
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8080/'
|
110
|
+
EM.stop
|
111
|
+
}
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should follow redirects on HEAD method (external)" do
|
116
|
+
|
117
|
+
EventMachine.run {
|
118
|
+
http = EventMachine::HttpRequest.new('http://www.google.com/').head :redirects => 1
|
119
|
+
http.errback { failed }
|
120
|
+
http.callback {
|
121
|
+
http.response_header.status.should == 200
|
122
|
+
EM.stop
|
123
|
+
}
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
102
127
|
end
|
103
128
|
|
104
129
|
it "should perform successfull GET with a URI passed as argument" do
|
data/spec/spec.opts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
--colour
|
2
|
-
--format
|
3
|
-
specdoc
|
4
|
-
--loadby
|
5
|
-
mtime
|
6
|
-
--reverse
|
1
|
+
--colour
|
2
|
+
--format
|
3
|
+
specdoc
|
4
|
+
--loadby
|
5
|
+
mtime
|
6
|
+
--reverse
|
7
7
|
--backtrace
|
data/spec/stallion.rb
CHANGED
@@ -85,7 +85,7 @@ Stallion.saddle :spec do |stable|
|
|
85
85
|
elsif stable.request.path_info == '/echo_content_length'
|
86
86
|
stable.response.write stable.request.content_length
|
87
87
|
|
88
|
-
elsif stable.request.head?
|
88
|
+
elsif stable.request.head? && stable.request.path_info == '/'
|
89
89
|
stable.response.status = 200
|
90
90
|
|
91
91
|
elsif stable.request.delete?
|
@@ -121,6 +121,10 @@ Stallion.saddle :spec do |stable|
|
|
121
121
|
stable.response.status = 301
|
122
122
|
stable.response["Location"] = "http://127.0.0.1:8080"
|
123
123
|
|
124
|
+
elsif stable.request.path_info == '/redirect/head'
|
125
|
+
stable.response.status = 301
|
126
|
+
stable.response["Location"] = "/"
|
127
|
+
|
124
128
|
elsif stable.request.path_info == '/redirect/nohost'
|
125
129
|
stable.response.status = 301
|
126
130
|
stable.response["Location"] = "http:/"
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
version: 0.2.
|
8
|
+
- 13
|
9
|
+
version: 0.2.13
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Ilya Grigorik
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-09-
|
17
|
+
date: 2010-09-25 00:00:00 -04:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -68,6 +68,7 @@ files:
|
|
68
68
|
- examples/fetch.rb
|
69
69
|
- examples/fibered-http.rb
|
70
70
|
- examples/oauth-tweet.rb
|
71
|
+
- examples/socks5.rb
|
71
72
|
- examples/websocket-handler.rb
|
72
73
|
- examples/websocket-server.rb
|
73
74
|
- ext/buffer/em_buffer.c
|
@@ -142,5 +143,6 @@ test_files:
|
|
142
143
|
- examples/fetch.rb
|
143
144
|
- examples/fibered-http.rb
|
144
145
|
- examples/oauth-tweet.rb
|
146
|
+
- examples/socks5.rb
|
145
147
|
- examples/websocket-handler.rb
|
146
148
|
- examples/websocket-server.rb
|