hatetepe 0.5.1 → 0.5.2

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.
data/Gemfile CHANGED
@@ -1,4 +1,6 @@
1
- source :rubygems
1
+ source "https://rubygems.org"
2
+
3
+ ruby "1.9.3"
2
4
 
3
5
  gemspec
4
6
 
@@ -0,0 +1,46 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ hatetepe (0.5.1)
5
+ em-synchrony (~> 1.0)
6
+ eventmachine (~> 1.0.0.beta.4)
7
+ http_parser.rb (~> 0.6.0.beta.2)
8
+ rack
9
+ thor
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ awesome_print (1.1.0)
15
+ diff-lcs (1.2.4)
16
+ em-synchrony (1.0.3)
17
+ eventmachine (>= 1.0.0.beta.1)
18
+ eventmachine (1.0.3)
19
+ eventmachine (1.0.3-java)
20
+ http_parser.rb (0.6.0.beta.2)
21
+ http_parser.rb (0.6.0.beta.2-java)
22
+ kramdown (1.1.0)
23
+ rack (1.5.2)
24
+ rake (10.1.0)
25
+ rspec (2.14.1)
26
+ rspec-core (~> 2.14.0)
27
+ rspec-expectations (~> 2.14.0)
28
+ rspec-mocks (~> 2.14.0)
29
+ rspec-core (2.14.4)
30
+ rspec-expectations (2.14.1)
31
+ diff-lcs (>= 1.1.3, < 2.0)
32
+ rspec-mocks (2.14.3)
33
+ thor (0.18.1)
34
+ yard (0.8.7)
35
+
36
+ PLATFORMS
37
+ java
38
+ ruby
39
+
40
+ DEPENDENCIES
41
+ awesome_print
42
+ hatetepe!
43
+ kramdown
44
+ rake
45
+ rspec
46
+ yard
@@ -0,0 +1 @@
1
+ web: bundle exec hatetepe -p $PORT -b 0.0.0.0
data/README.md CHANGED
@@ -182,13 +182,15 @@ Hatetepe is subject to an MIT-style license (see LICENSE file).
182
182
  Roadmap
183
183
  -------
184
184
 
185
- - 0.5.0
186
- - Direct IO via EM.enable_proxy
185
+ - 0.6.0
186
+ - Direct recv<->send IO via EM.enable_proxy
187
+ - SSL/TLS
188
+ - HTTP proxying (in client and server)
189
+ - later
187
190
  - Encoding support (ref. [github.com/tmm1/http_parser.rb#1](https://github.com/tmm1/http_parser.rb/pull/1))
188
191
  - Optimize for performance
189
192
  - Propagate connection errors to the app
190
193
 
191
-
192
194
  Ideas
193
195
  -----
194
196
 
@@ -0,0 +1,7 @@
1
+ Hatetepe::Server::CONFIG_DEFAULTS[:app] = [ Hatetepe::Server::KeepAlive, Hatetepe::Server::RackApp ]
2
+
3
+ use Rack::ContentLength
4
+
5
+ run proc {|_|
6
+ [200, {'Content-Type' => 'text/html'}, ['hello, world']]
7
+ }
@@ -1,6 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  $:.push File.expand_path("../lib", __FILE__)
3
3
  require "hatetepe/version"
4
+ require "date"
4
5
 
5
6
  Gem::Specification.new do |s|
6
7
  s.name = "hatetepe"
@@ -13,7 +14,7 @@ Gem::Specification.new do |s|
13
14
  s.summary = %q{The HTTP toolkit}
14
15
  #s.description = %q{TODO: write description}
15
16
 
16
- s.add_dependency "http_parser.rb", "~> 0.5.3"
17
+ s.add_dependency "http_parser.rb", "~> 0.6.0.beta.2"
17
18
  s.add_dependency "eventmachine", "~> 1.0.0.beta.4"
18
19
  s.add_dependency "em-synchrony", "~> 1.0"
19
20
  s.add_dependency "rack"
@@ -7,6 +7,13 @@ require "hatetepe/parser"
7
7
  require "hatetepe/request"
8
8
  require "hatetepe/version"
9
9
 
10
+ module Hatetepe
11
+ HatetepeError = Class.new(StandardError)
12
+ RequestError = Class.new(HatetepeError)
13
+ ClientError = Class.new(RequestError)
14
+ ServerError = Class.new(RequestError)
15
+ end
16
+
10
17
  module Hatetepe::Client
11
18
  include Hatetepe::Connection
12
19
 
@@ -39,12 +46,11 @@ module Hatetepe::Client
39
46
  # @api semipublic
40
47
  def initialize(config)
41
48
  @config = CONFIG_DEFAULTS.merge(config)
49
+ @ssl_handshake_completed = EM::DefaultDeferrable.new
42
50
  end
43
51
 
44
52
  # Initializes the parser, request queue, and middleware pipe.
45
53
  #
46
- # TODO: Use +Rack::Builder+ for building the app pipe.
47
- #
48
54
  # @see EM::Connection#post_init
49
55
  #
50
56
  # @api semipublic
@@ -60,6 +66,12 @@ module Hatetepe::Client
60
66
 
61
67
  self.comm_inactivity_timeout = config[:timeout]
62
68
  self.pending_connect_timeout = config[:connect_timeout]
69
+
70
+ start_tls if config[:ssl]
71
+ end
72
+
73
+ def ssl_handshake_completed
74
+ EM::Synchrony.next_tick { @ssl_handshake_completed.succeed }
63
75
  end
64
76
 
65
77
  # Feeds response data into the parser.
@@ -101,6 +113,8 @@ module Hatetepe::Client
101
113
  # @api public
102
114
  def <<(request)
103
115
  Fiber.new do
116
+ EM::Synchrony.sync(@ssl_handshake_completed) if config[:ssl]
117
+
104
118
  response = @app.call(request)
105
119
 
106
120
  if response && (request.verb == "HEAD" || response.status == 204)
@@ -108,7 +122,7 @@ module Hatetepe::Client
108
122
  end
109
123
 
110
124
  if !response
111
- request.fail(nil, self)
125
+ request.fail
112
126
  elsif response.failure?
113
127
  request.fail(response)
114
128
  else
@@ -133,11 +147,52 @@ module Hatetepe::Client
133
147
  #
134
148
  # @api public
135
149
  def request(verb, uri, headers = {}, body = [])
136
- request = Hatetepe::Request.new(verb, URI(uri), headers, body)
150
+ uri = URI(uri)
151
+ uri.scheme ||= @config[:ssl] ? 'http' : 'https'
152
+ uri.host ||= @config[:host]
153
+ uri.port ||= @config[:port]
154
+
155
+ headers['Host'] ||= "#{uri.host}:#{uri.port}"
156
+
157
+ request = Hatetepe::Request.new(verb, URI(uri.to_s), headers, body)
137
158
  self << request
138
159
  EM::Synchrony.sync(request)
139
160
  end
140
161
 
162
+ # Like +#request+, but raises errors for 4xx and 5xx responses.
163
+ #
164
+ # @param [Symbol, String] verb
165
+ # The HTTP method verb, e.g. +:get+ or +"PUT"+.
166
+ # @param [String, URI] uri
167
+ # The request URI.
168
+ # @param [Hash] headers (optional)
169
+ # The request headers.
170
+ # @param [#each] body (optional)
171
+ # A request body object whose +#each+ method yields objects that respond
172
+ # to +#to_s+.
173
+ #
174
+ # @return [Hatetepe]::Response, nil]
175
+ #
176
+ # @raise [Hatetepe::ClientError]
177
+ # If the server responded with a 4xx status code.
178
+ # @raise [Hatetepe::ServerError]
179
+ # If the server responded with a 5xx status code.
180
+ # @raise [Hatetepe::RequestError]
181
+ # If the client failed to receive any response at all.
182
+ def request!(verb, uri, headers = {}, body = [])
183
+ response = request(verb, uri, headers, body)
184
+
185
+ if response.nil?
186
+ raise Hatetepe::RequestError
187
+ elsif response.status >= 500
188
+ raise Hatetepe::ServerError
189
+ elsif response.status >= 400
190
+ raise Hatetepe::ClientError
191
+ end
192
+
193
+ response
194
+ end
195
+
141
196
  # Gracefully stops the client.
142
197
  #
143
198
  # Waits for all requests to finish and then stops the client.
@@ -188,10 +243,8 @@ module Hatetepe::Client
188
243
  # @api public
189
244
  def self.request(verb, uri, headers = {}, body = [])
190
245
  uri = URI(uri)
191
- client = start(host: uri.host, port: uri.port)
246
+ client = start(host: uri.host, port: uri.port, ssl: uri.scheme == 'https')
192
247
  client.request(verb, uri, headers, body)
193
- ensure
194
- client.stop
195
248
  end
196
249
 
197
250
  # Feeds the request into the builder and blocks while waiting for the
@@ -1,3 +1,3 @@
1
1
  module Hatetepe
2
- VERSION = "0.5.1"
2
+ VERSION = "0.5.2"
3
3
  end
@@ -26,7 +26,7 @@ describe Hatetepe::Body do
26
26
  end
27
27
 
28
28
  context "#sync" do
29
- let(:conn) { stub "conn", :paused? => true }
29
+ let(:conn) { double "conn", :paused? => true }
30
30
 
31
31
  it "resumes the source connection if any" do
32
32
  body.source = conn
@@ -43,7 +43,7 @@ describe Hatetepe::Body do
43
43
  end
44
44
 
45
45
  context "#length" do
46
- let(:length) { stub "length" }
46
+ let(:length) { double "length" }
47
47
 
48
48
  it "forwards to io#length" do
49
49
  body.stub :sync
@@ -79,7 +79,7 @@ describe Hatetepe::Body do
79
79
  end
80
80
 
81
81
  context "#pos" do
82
- let(:pos) { stub "pos" }
82
+ let(:pos) { double "pos" }
83
83
 
84
84
  it "forwards to io#pos" do
85
85
  body.io.stub :pos => pos
@@ -109,7 +109,7 @@ describe Hatetepe::Body do
109
109
 
110
110
  context "#closed_write?" do
111
111
  it "forwards to io#closed_write?" do
112
- ret = stub("return")
112
+ ret = double("return")
113
113
  body.io.should_receive(:closed_write?) { ret }
114
114
 
115
115
  body.closed_write?.should equal(ret)
@@ -143,7 +143,7 @@ describe Hatetepe::Body do
143
143
  end
144
144
 
145
145
  describe "without a block" do
146
- let(:enumerator) { stub }
146
+ let(:enumerator) { double }
147
147
 
148
148
  before { body.stub(:to_enum).with(:each) { enumerator } }
149
149
 
@@ -167,7 +167,7 @@ describe Hatetepe::Body do
167
167
 
168
168
  it "forwards to io#read" do
169
169
  body.succeed
170
- args, ret = [stub("arg#1"), stub("arg#2")], stub("ret")
170
+ args, ret = [double("arg#1"), double("arg#2")], double("ret")
171
171
 
172
172
  body.io.should_receive(:read).with(*args) { ret }
173
173
  body.read(*args).should equal(ret)
@@ -187,7 +187,7 @@ describe Hatetepe::Body do
187
187
 
188
188
  it "forwards to io#gets" do
189
189
  body.succeed
190
- ret = stub("ret")
190
+ ret = double("ret")
191
191
 
192
192
  body.io.should_receive(:gets) { ret }
193
193
  body.gets.should equal(ret)
@@ -196,7 +196,7 @@ describe Hatetepe::Body do
196
196
 
197
197
  context "#write(chunk)" do
198
198
  it "forwards to io#write" do
199
- arg, ret = stub("arg"), stub("ret")
199
+ arg, ret = double("arg"), double("ret")
200
200
  body.io.should_receive(:write).with(arg) { ret }
201
201
 
202
202
  body.write(arg).should equal(ret)
@@ -73,7 +73,7 @@ describe Hatetepe::Builder do
73
73
  describe "#chunked?"
74
74
 
75
75
  describe "#request(array)" do
76
- let(:req) { [:get, "/foo", {"Key" => "value"}, stub("body")] }
76
+ let(:req) { [:get, "/foo", {"Key" => "value"}, double("body")] }
77
77
 
78
78
  before { builder.send :initialize }
79
79
 
@@ -137,7 +137,7 @@ describe Hatetepe::Builder do
137
137
  end
138
138
 
139
139
  describe "#response(array)" do
140
- let(:res) { [201, {"Key" => "value"}, stub("body")] }
140
+ let(:res) { [201, {"Key" => "value"}, double("body")] }
141
141
 
142
142
  before { builder.send :initialize }
143
143
 
@@ -230,7 +230,7 @@ describe Hatetepe::Builder do
230
230
  end
231
231
 
232
232
  describe "#body(#each)" do
233
- let(:body) { [stub("chunk#1"), stub("chunk#2")] }
233
+ let(:body) { [double("chunk#1"), double("chunk#2")] }
234
234
 
235
235
  it "calls #body_chunk for each element" do
236
236
  builder.should_receive(:body_chunk).with body[0]
@@ -318,7 +318,7 @@ describe Hatetepe::Builder do
318
318
  builder.complete
319
319
  end
320
320
 
321
- let(:hook) { stub "hook", :call => nil }
321
+ let(:hook) { double "hook", :call => nil }
322
322
 
323
323
  it "calls the on_complete hooks" do
324
324
  builder.on_complete << hook
@@ -333,8 +333,8 @@ describe Hatetepe::Builder do
333
333
  end
334
334
 
335
335
  describe "#write(data)" do
336
- let(:hook) { stub "hook" }
337
- let(:data) { stub "data" }
336
+ let(:hook) { double "hook" }
337
+ let(:data) { double "data" }
338
338
 
339
339
  before { builder.send :initialize }
340
340
 
@@ -346,10 +346,10 @@ describe Hatetepe::Builder do
346
346
  end
347
347
 
348
348
  describe "#error(message)" do
349
- let(:hook1) { stub "hook#1" }
350
- let(:hook2) { stub "hook#2" }
349
+ let(:hook1) { double "hook#1" }
350
+ let(:hook2) { double "hook#2" }
351
351
  let(:message) { "error! error!" }
352
- let(:exception) { stub "exception" }
352
+ let(:exception) { double "exception" }
353
353
 
354
354
  before do
355
355
  builder.send :initialize
@@ -72,10 +72,10 @@ describe Hatetepe::Client do
72
72
  end
73
73
 
74
74
  describe ".request" do
75
- let(:client) { stub("client", request: res, stop: nil) }
76
- let(:headers) { stub("headers") }
77
- let(:body) { stub("body") }
78
- let(:res) { stub("response") }
75
+ let(:client) { double("client", request: res, stop: nil) }
76
+ let(:headers) { double("headers") }
77
+ let(:body) { double("body") }
78
+ let(:res) { double("response") }
79
79
  let(:response) { Hatetepe::Client.request(:put, "/test", headers, body) }
80
80
 
81
81
  before { Hatetepe::Client.stub(start: client) }
@@ -84,18 +84,13 @@ describe Hatetepe::Client do
84
84
  client.should_receive(:request).with(:put, URI("/test"), headers, body)
85
85
  response.should equal(res)
86
86
  end
87
-
88
- it "stops the client afterwards" do
89
- client.should_receive(:stop)
90
- response
91
- end
92
87
  end
93
88
 
94
89
  describe "#request" do
95
90
  let(:body) { [ "Hello,", " world!" ] }
96
91
  let(:headers) { { "Content-Type" => "text/plain" } }
97
- let(:request) { stub("request") }
98
- let(:response) { stub("response") }
92
+ let(:request) { double("request") }
93
+ let(:response) { double("response") }
99
94
 
100
95
  before do
101
96
  client.stub(:<<)
@@ -125,6 +120,46 @@ describe Hatetepe::Client do
125
120
  end
126
121
  end
127
122
 
123
+ describe "#request!" do
124
+ subject do
125
+ proc { client.request!(:get, "/") }
126
+ end
127
+
128
+ let(:status) { 200 }
129
+ let(:response) { double("response", :status => status) }
130
+
131
+ before do
132
+ client.stub(:request).with(:get, "/", {}, []) { response }
133
+ end
134
+
135
+ it "forwards to #request" do
136
+ subject.call.should eq(response)
137
+ end
138
+
139
+ describe "for a 4xx response" do
140
+ let(:status) { 404 }
141
+
142
+ it "raises a ClientError" do
143
+ subject.should raise_error(Hatetepe::ClientError)
144
+ end
145
+ end
146
+
147
+ describe "for a 5xx response" do
148
+ let(:status) { 502 }
149
+
150
+ it "raises a ServerError" do
151
+ subject.should raise_error(Hatetepe::ServerError)
152
+ end
153
+ end
154
+
155
+ describe "if no response could be received" do
156
+ it "raises a ServerError" do
157
+ client.stub(:request) { nil }
158
+ subject.should raise_error(Hatetepe::RequestError)
159
+ end
160
+ end
161
+ end
162
+
128
163
  describe "#<<" do
129
164
  let :request do
130
165
  Hatetepe::Request.new :head, "/test"
@@ -152,7 +187,7 @@ describe Hatetepe::Client do
152
187
  let(:response) { nil }
153
188
 
154
189
  it "fails the request" do
155
- request.should_receive(:fail).with(nil, client)
190
+ request.should_receive(:fail)
156
191
  client << request
157
192
  end
158
193
  end
@@ -68,7 +68,7 @@ describe Hatetepe::Parser do
68
68
  end
69
69
 
70
70
  let(:block) {
71
- stub("block").tap {|blk|
71
+ double("block").tap {|blk|
72
72
  blk.stub :to_proc => proc {|*args| blk.call *args }
73
73
  }
74
74
  }
@@ -2,14 +2,14 @@ require "spec_helper"
2
2
  require "rack/handler/hatetepe"
3
3
 
4
4
  describe Rack::Handler::Hatetepe do
5
- let(:app) { stub "app" }
5
+ let(:app) { double "app" }
6
6
  let(:options) {
7
7
  {
8
- :Host => stub("host"),
9
- :Port => stub("port")
8
+ :Host => double("host"),
9
+ :Port => double("port")
10
10
  }
11
11
  }
12
- let(:server) { stub "server" }
12
+ let(:server) { double "server" }
13
13
 
14
14
  describe ".run(app, options) {|server| ... }" do
15
15
  before {
@@ -39,7 +39,7 @@ describe Hatetepe::Server, "(public API)" do
39
39
  end
40
40
 
41
41
  let :app do
42
- stub("app", :call => nil)
42
+ double("app", :call => nil)
43
43
  end
44
44
 
45
45
  let :http_request do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hatetepe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-06 00:00:00.000000000 Z
12
+ date: 2013-09-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: http_parser.rb
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 0.5.3
21
+ version: 0.6.0.beta.2
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: 0.5.3
29
+ version: 0.6.0.beta.2
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: eventmachine
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -148,10 +148,13 @@ extensions: []
148
148
  extra_rdoc_files: []
149
149
  files:
150
150
  - Gemfile
151
+ - Gemfile.lock
151
152
  - LICENSE
153
+ - Procfile
152
154
  - README.md
153
155
  - Rakefile
154
156
  - bin/hatetepe
157
+ - config.ru
155
158
  - examples/parallel_requests.rb
156
159
  - hatetepe.gemspec
157
160
  - lib/hatetepe.rb
@@ -205,7 +208,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
205
208
  version: '0'
206
209
  requirements: []
207
210
  rubyforge_project:
208
- rubygems_version: 1.8.24
211
+ rubygems_version: 1.8.23
209
212
  signing_key:
210
213
  specification_version: 3
211
214
  summary: The HTTP toolkit