http 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of http might be problematic. Click here for more details.
- data/.travis.yml +1 -1
- data/CHANGES.md +5 -0
- data/README.md +34 -14
- data/lib/http.rb +0 -5
- data/lib/http/client.rb +10 -10
- data/lib/http/compat/curb.rb +86 -0
- data/lib/http/version.rb +1 -1
- data/spec/http/compat/curb_spec.rb +40 -0
- data/spec/http_spec.rb +8 -11
- data/spec/spec_helper.rb +2 -1
- data/spec/support/mock_server.rb +59 -0
- metadata +81 -47
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
Http
|
2
2
|
====
|
3
|
+
[![Build Status](http://travis-ci.org/tarcieri/http.png)](http://travis-ci.org/tarcieri/http)
|
3
4
|
|
4
5
|
Ruby has always been this extremely web-focused language, and yet despite the
|
5
6
|
selection of HTTP libraries out there, I always find myself falling back on
|
@@ -17,19 +18,17 @@ Making Requests
|
|
17
18
|
|
18
19
|
Let's start with getting things:
|
19
20
|
|
20
|
-
|
21
|
+
```ruby
|
22
|
+
Http.get "http://www.google.com"
|
23
|
+
```
|
21
24
|
|
22
25
|
That's it! The result is the response body.
|
23
26
|
|
24
|
-
Don't like "Http"? No worries, this works as well:
|
25
|
-
|
26
|
-
HTTP.get "http://www.google.com"
|
27
|
-
|
28
|
-
After all, There Is More Than One Way To Do It!
|
29
|
-
|
30
27
|
Making POST requests is simple too. Want to POST a form?
|
31
28
|
|
32
|
-
|
29
|
+
```ruby
|
30
|
+
Http.post "http://example.com/resource", :form => {:foo => "42"}
|
31
|
+
```
|
33
32
|
|
34
33
|
It's easy!
|
35
34
|
|
@@ -40,7 +39,9 @@ The Http library uses the concept of chaining to simplify requests. Let's say
|
|
40
39
|
you want to get the latest commit of this library from Github in JSON format.
|
41
40
|
One way we could do this is by tacking a filename on the end of the URL:
|
42
41
|
|
43
|
-
|
42
|
+
```ruby
|
43
|
+
Http.get "https://github.com/tarcieri/http/commit/HEAD.json"
|
44
|
+
```
|
44
45
|
|
45
46
|
The Github API happens to support this approach, but really this is a bit of a
|
46
47
|
hack that makes it easy for people typing URLs into the address bars of
|
@@ -48,8 +49,10 @@ browsers to perform the act of content negotiation. Since we have access to
|
|
48
49
|
the full, raw power of HTTP, we can perform content negotiation the way HTTP
|
49
50
|
intends us to, by using the Accept header:
|
50
51
|
|
51
|
-
|
52
|
-
|
52
|
+
```ruby
|
53
|
+
Http.with_headers(:accept => 'application/json').
|
54
|
+
get("https://github.com/tarcieri/http/commit/HEAD")
|
55
|
+
```
|
53
56
|
|
54
57
|
This requests JSON from Github. Github is smart enough to understand our
|
55
58
|
request and returns a response with Content-Type: application/json. If you
|
@@ -58,8 +61,10 @@ JSON.parse, the Http library will attempt to parse the JSON response.
|
|
58
61
|
|
59
62
|
A shorter alias exists for HTTP.with_headers:
|
60
63
|
|
61
|
-
|
62
|
-
|
64
|
+
```ruby
|
65
|
+
Http.with(:accept => 'application/json').
|
66
|
+
get("https://github.com/tarcieri/http/commit/HEAD")
|
67
|
+
```
|
63
68
|
|
64
69
|
Content Negotiation
|
65
70
|
-------------------
|
@@ -68,11 +73,26 @@ As important a concept as content negotiation is HTTP, it sure should be easy,
|
|
68
73
|
right? But usually it's not, and so we end up adding ".json" onto the ends of
|
69
74
|
our URLs because the existing mechanisms make it too hard. It should be easy:
|
70
75
|
|
71
|
-
|
76
|
+
```ruby
|
77
|
+
Http.accept(:json).get("https://github.com/tarcieri/http/commit/HEAD")
|
78
|
+
```
|
72
79
|
|
73
80
|
This adds the appropriate Accept header for retrieving a JSON response for the
|
74
81
|
given resource.
|
75
82
|
|
83
|
+
Curb Compatibility
|
84
|
+
------------------
|
85
|
+
|
86
|
+
The Http gem provides partial compatibility with the Curb::Easy API. This is
|
87
|
+
great if you're transitioning to JRuby and need a drop-in API-compatible
|
88
|
+
replacement for Curb.
|
89
|
+
|
90
|
+
To use the Curb compatibility, do:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
require 'http/compat/curb'
|
94
|
+
```
|
95
|
+
|
76
96
|
Contributing to Http
|
77
97
|
--------------------
|
78
98
|
|
data/lib/http.rb
CHANGED
@@ -5,7 +5,6 @@ require 'http/mime_type'
|
|
5
5
|
require 'http/parameters'
|
6
6
|
require 'http/response'
|
7
7
|
|
8
|
-
|
9
8
|
# THIS IS ENTIRELY TEMPORARY, I ASSURE YOU
|
10
9
|
require 'net/https'
|
11
10
|
require 'uri'
|
@@ -14,7 +13,3 @@ require 'uri'
|
|
14
13
|
module Http
|
15
14
|
extend Chainable
|
16
15
|
end
|
17
|
-
|
18
|
-
# TIMTOWTDI!
|
19
|
-
HTTP = Http
|
20
|
-
HttpClient = Http::Client
|
data/lib/http/client.rb
CHANGED
@@ -7,54 +7,54 @@ module Http
|
|
7
7
|
@uri = uri
|
8
8
|
else
|
9
9
|
# Why the FUCK can't Net::HTTP do this?
|
10
|
-
@uri = URI(uri)
|
10
|
+
@uri = URI(uri.to_s)
|
11
11
|
end
|
12
12
|
|
13
13
|
@options = {:response => :object}.merge(options)
|
14
14
|
end
|
15
15
|
|
16
16
|
# Request a get sans response body
|
17
|
-
def head(
|
17
|
+
def head(options = {})
|
18
18
|
request :head, options
|
19
19
|
end
|
20
20
|
|
21
21
|
# Get a resource
|
22
|
-
def get(
|
22
|
+
def get(options = {})
|
23
23
|
request :get, options
|
24
24
|
end
|
25
25
|
|
26
26
|
# Post to a resource
|
27
|
-
def post(
|
27
|
+
def post(options = {})
|
28
28
|
request :post, options
|
29
29
|
end
|
30
30
|
|
31
31
|
# Put to a resource
|
32
|
-
def put(
|
32
|
+
def put(options = {})
|
33
33
|
request :put, options
|
34
34
|
end
|
35
35
|
|
36
36
|
# Delete a resource
|
37
|
-
def delete(
|
37
|
+
def delete(options = {})
|
38
38
|
request :delete, options
|
39
39
|
end
|
40
40
|
|
41
41
|
# Echo the request back to the client
|
42
|
-
def trace(
|
42
|
+
def trace(options = {})
|
43
43
|
request :trace, options
|
44
44
|
end
|
45
45
|
|
46
46
|
# Return the methods supported on the given URI
|
47
|
-
def options(
|
47
|
+
def options(options = {})
|
48
48
|
request :options, options
|
49
49
|
end
|
50
50
|
|
51
51
|
# Convert to a transparent TCP/IP tunnel
|
52
|
-
def connect(
|
52
|
+
def connect(options = {})
|
53
53
|
request :connect, options
|
54
54
|
end
|
55
55
|
|
56
56
|
# Apply partial modifications to a resource
|
57
|
-
def patch(
|
57
|
+
def patch(options = {})
|
58
58
|
request :patch, options
|
59
59
|
end
|
60
60
|
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'http'
|
2
|
+
|
3
|
+
# Compatibility with the Curb gem
|
4
|
+
module Curl
|
5
|
+
module Err
|
6
|
+
class CurlError < RuntimeError; end
|
7
|
+
class ConnectionFailedError < CurlError; end
|
8
|
+
class HostResolutionError < CurlError; end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Easy
|
12
|
+
attr_accessor :headers, :encoding
|
13
|
+
attr_reader :response_code, :body_str
|
14
|
+
|
15
|
+
def self.http_post(url, request_body = nil)
|
16
|
+
Easy.new(url).tap { |e| e.http_post(request_body) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.http_get(url, request_body = nil)
|
20
|
+
Easy.new(url).tap { |e| e.http_get(request_body) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(url = nil, method = nil, request_body = nil, headers = {})
|
24
|
+
@url = url
|
25
|
+
@method = method
|
26
|
+
@request_body = request_body
|
27
|
+
@headers = headers
|
28
|
+
@response_code = @body_str = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def perform
|
32
|
+
client = Http::Client.new @url, :headers => @headers
|
33
|
+
response = client.send @method
|
34
|
+
@response_code, @body_str = response.code, response.body
|
35
|
+
true
|
36
|
+
rescue SocketError => ex
|
37
|
+
if ex.message['getaddrinfo'] || ex.message['ame or service not known']
|
38
|
+
raise Err::HostResolutionError, ex.message
|
39
|
+
else
|
40
|
+
raise Err::ConnectionFailedError, ex.message
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def http_get(request_body = nil)
|
45
|
+
@method, @request_body = :get, request_body
|
46
|
+
perform
|
47
|
+
end
|
48
|
+
|
49
|
+
def http_put(request_body = nil)
|
50
|
+
@method, @request_body = :put, request_body
|
51
|
+
perform
|
52
|
+
end
|
53
|
+
|
54
|
+
def http_post(request_body = nil)
|
55
|
+
@method, @request_body = :post, request_body
|
56
|
+
perform
|
57
|
+
end
|
58
|
+
|
59
|
+
def http_delete
|
60
|
+
@method = :delete
|
61
|
+
perform
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Multi
|
66
|
+
def initialize
|
67
|
+
@clients = []
|
68
|
+
@done = false
|
69
|
+
end
|
70
|
+
|
71
|
+
def add(client)
|
72
|
+
@clients << client
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def perform
|
77
|
+
return if @done
|
78
|
+
|
79
|
+
@clients.map do |client|
|
80
|
+
Thread.new { client.perform }
|
81
|
+
end.each(&:join)
|
82
|
+
|
83
|
+
@done = true
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/http/version.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'http/compat/curb'
|
4
|
+
|
5
|
+
describe Curl do
|
6
|
+
let(:test_endpoint) { "http://127.0.0.1:#{TEST_SERVER_PORT}/" }
|
7
|
+
|
8
|
+
describe Curl::Easy do
|
9
|
+
it "gets resources" do
|
10
|
+
response = Curl::Easy.http_get test_endpoint
|
11
|
+
response.body_str.should match(/<!doctype html>/)
|
12
|
+
end
|
13
|
+
|
14
|
+
context :errors do
|
15
|
+
it "raises Curl::Err::HostResolutionError if asked to connect to a nonexistent domain" do
|
16
|
+
expect {
|
17
|
+
Curl::Easy.http_get "http://totallynonexistentdomain.com"
|
18
|
+
}.to raise_exception(Curl::Err::HostResolutionError)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe Curl::Multi do
|
24
|
+
it "gets resources" do
|
25
|
+
requests = [test_endpoint]
|
26
|
+
responses = []
|
27
|
+
|
28
|
+
multi = Curl::Multi.new
|
29
|
+
|
30
|
+
requests.each do |url|
|
31
|
+
response = Curl::Easy.new url, :get
|
32
|
+
multi.add response
|
33
|
+
responses << response
|
34
|
+
end
|
35
|
+
|
36
|
+
multi.perform
|
37
|
+
responses.first.body_str.should match(/<!doctype html>/)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/spec/http_spec.rb
CHANGED
@@ -2,35 +2,32 @@ require 'spec_helper'
|
|
2
2
|
require 'json'
|
3
3
|
|
4
4
|
describe Http do
|
5
|
+
let(:test_endpoint) { "http://127.0.0.1:#{TEST_SERVER_PORT}/" }
|
6
|
+
|
5
7
|
context "getting resources" do
|
6
8
|
it "should be easy" do
|
7
|
-
|
8
|
-
response = Http.get "http://www.google.com"
|
9
|
+
response = Http.get test_endpoint
|
9
10
|
response.should match(/<!doctype html>/)
|
10
11
|
end
|
11
12
|
|
12
13
|
context "with headers" do
|
13
14
|
it "should be easy" do
|
14
|
-
response = Http.accept(:json).get
|
15
|
-
|
16
|
-
# Congratulations first committer, your prize is to break the build!
|
17
|
-
response['commit']['author']['name'].should == "Tony Arcieri"
|
15
|
+
response = Http.accept(:json).get test_endpoint
|
16
|
+
response['json'].should be_true
|
18
17
|
end
|
19
18
|
end
|
20
19
|
end
|
21
20
|
|
22
21
|
context "posting to resources" do
|
23
22
|
it "should be easy" do
|
24
|
-
|
25
|
-
response
|
26
|
-
|
27
|
-
response.should match(/HTML5/)
|
23
|
+
response = Http.post test_endpoint, :form => {:example => 'testing'}
|
24
|
+
response.should == "passed :)"
|
28
25
|
end
|
29
26
|
end
|
30
27
|
|
31
28
|
context "head requests" do
|
32
29
|
it "should be easy" do
|
33
|
-
response = Http.head
|
30
|
+
response = Http.head test_endpoint
|
34
31
|
response.status.should == 200
|
35
32
|
response['content-type'].should match(/html/)
|
36
33
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'webrick'
|
2
|
+
|
3
|
+
TEST_SERVER_PORT = 65432
|
4
|
+
|
5
|
+
class MockService < WEBrick::HTTPServlet::AbstractServlet
|
6
|
+
class << self; attr_accessor :post_handler; end
|
7
|
+
|
8
|
+
def do_GET(request, response)
|
9
|
+
case request.path
|
10
|
+
when "/"
|
11
|
+
response.status = 200
|
12
|
+
|
13
|
+
case request['Accept']
|
14
|
+
when 'application/json'
|
15
|
+
response['Content-Type'] = 'application/json'
|
16
|
+
response.body = '{"json": true}'
|
17
|
+
else
|
18
|
+
response['Content-Type'] = 'text/html'
|
19
|
+
response.body = "<!doctype html>"
|
20
|
+
end
|
21
|
+
else
|
22
|
+
response.status = 404
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def do_POST(request, response)
|
27
|
+
case request.path
|
28
|
+
when "/"
|
29
|
+
if request.query['example'] == 'testing'
|
30
|
+
response.status = 200
|
31
|
+
response.body = "passed :)"
|
32
|
+
else
|
33
|
+
response.status = 400
|
34
|
+
response.vody = "invalid! >:E"
|
35
|
+
end
|
36
|
+
else
|
37
|
+
response.status = 404
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def do_HEAD(request, response)
|
42
|
+
case request.path
|
43
|
+
when "/"
|
44
|
+
response.status = 200
|
45
|
+
response['Content-Type'] = 'text/html'
|
46
|
+
else
|
47
|
+
response.status = 404
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
MockServer = WEBrick::HTTPServer.new(:Port => TEST_SERVER_PORT, :AccessLog => [])
|
53
|
+
MockServer.mount "/", MockService
|
54
|
+
|
55
|
+
Thread.new { MockServer.start }
|
56
|
+
trap("INT") { MockServer.shutdown; exit }
|
57
|
+
|
58
|
+
# hax: wait for webrick to start
|
59
|
+
sleep 0.1
|
metadata
CHANGED
@@ -1,58 +1,78 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: http
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- Tony Arcieri
|
9
14
|
- Carl Lerche
|
10
15
|
autorequire:
|
11
16
|
bindir: bin
|
12
17
|
cert_chain: []
|
13
|
-
|
14
|
-
|
15
|
-
|
18
|
+
|
19
|
+
date: 2012-01-26 00:00:00 -08:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
16
23
|
name: rake
|
17
|
-
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
18
26
|
none: false
|
19
|
-
requirements:
|
20
|
-
- -
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 3
|
31
|
+
segments:
|
32
|
+
- 0
|
33
|
+
version: "0"
|
23
34
|
type: :development
|
24
|
-
|
25
|
-
|
26
|
-
- !ruby/object:Gem::Dependency
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
27
37
|
name: rspec
|
28
|
-
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
29
40
|
none: false
|
30
|
-
requirements:
|
31
|
-
- -
|
32
|
-
- !ruby/object:Gem::Version
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 23
|
45
|
+
segments:
|
46
|
+
- 2
|
47
|
+
- 6
|
48
|
+
- 0
|
33
49
|
version: 2.6.0
|
34
50
|
type: :development
|
35
|
-
|
36
|
-
|
37
|
-
- !ruby/object:Gem::Dependency
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
38
53
|
name: json
|
39
|
-
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
40
56
|
none: false
|
41
|
-
requirements:
|
42
|
-
- -
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
45
64
|
type: :development
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
zoo
|
50
|
-
email:
|
65
|
+
version_requirements: *id003
|
66
|
+
description: HTTP so awesome it will lure Catherine Zeta Jones into your unicorn petting zoo
|
67
|
+
email:
|
51
68
|
- tony.arcieri@gmail.com
|
52
69
|
executables: []
|
70
|
+
|
53
71
|
extensions: []
|
72
|
+
|
54
73
|
extra_rdoc_files: []
|
55
|
-
|
74
|
+
|
75
|
+
files:
|
56
76
|
- .gitignore
|
57
77
|
- .rspec
|
58
78
|
- .travis.yml
|
@@ -65,6 +85,7 @@ files:
|
|
65
85
|
- lib/http.rb
|
66
86
|
- lib/http/chainable.rb
|
67
87
|
- lib/http/client.rb
|
88
|
+
- lib/http/compat/curb.rb
|
68
89
|
- lib/http/mime_type.rb
|
69
90
|
- lib/http/mime_types/json.rb
|
70
91
|
- lib/http/parameters.rb
|
@@ -73,33 +94,46 @@ files:
|
|
73
94
|
- parser/common.rl
|
74
95
|
- parser/http.rl
|
75
96
|
- parser/multipart.rl
|
97
|
+
- spec/http/compat/curb_spec.rb
|
76
98
|
- spec/http_spec.rb
|
77
99
|
- spec/spec_helper.rb
|
100
|
+
- spec/support/mock_server.rb
|
101
|
+
has_rdoc: true
|
78
102
|
homepage: https://github.com/tarcieri/http
|
79
103
|
licenses: []
|
104
|
+
|
80
105
|
post_install_message:
|
81
106
|
rdoc_options: []
|
82
|
-
|
107
|
+
|
108
|
+
require_paths:
|
83
109
|
- lib
|
84
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
111
|
none: false
|
86
|
-
requirements:
|
87
|
-
- -
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
|
90
|
-
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
hash: 3
|
116
|
+
segments:
|
117
|
+
- 0
|
118
|
+
version: "0"
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
120
|
none: false
|
92
|
-
requirements:
|
93
|
-
- -
|
94
|
-
- !ruby/object:Gem::Version
|
95
|
-
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
hash: 3
|
125
|
+
segments:
|
126
|
+
- 0
|
127
|
+
version: "0"
|
96
128
|
requirements: []
|
129
|
+
|
97
130
|
rubyforge_project:
|
98
|
-
rubygems_version: 1.
|
131
|
+
rubygems_version: 1.5.2
|
99
132
|
signing_key:
|
100
133
|
specification_version: 3
|
101
134
|
summary: Made entirely of Ruby (and Ragel and C and Java)
|
102
|
-
test_files:
|
135
|
+
test_files:
|
136
|
+
- spec/http/compat/curb_spec.rb
|
103
137
|
- spec/http_spec.rb
|
104
138
|
- spec/spec_helper.rb
|
105
|
-
|
139
|
+
- spec/support/mock_server.rb
|