hatetepe 0.6.0.pre → 0.6.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +6 -4
- data/bin/hatetepe +4 -8
- data/config/flay.yml +2 -2
- data/config/reek.yml +15 -11
- data/lib/hatetepe.rb +1 -1
- data/lib/hatetepe/body.rb +62 -0
- data/lib/hatetepe/client.rb +34 -34
- data/lib/hatetepe/client/keep_alive.rb +4 -49
- data/lib/hatetepe/client/timeouts.rb +3 -3
- data/lib/hatetepe/connection/eventmachine.rb +7 -8
- data/lib/hatetepe/promise.rb +7 -2
- data/lib/hatetepe/request.rb +8 -10
- data/lib/hatetepe/response.rb +5 -7
- data/lib/hatetepe/serializer.rb +2 -3
- data/lib/hatetepe/serializer/encoding.rb +8 -16
- data/lib/hatetepe/server.rb +15 -10
- data/lib/hatetepe/server/keep_alive.rb +26 -31
- data/lib/hatetepe/server/timeouts.rb +3 -3
- data/lib/hatetepe/support/message.rb +25 -3
- data/lib/hatetepe/version.rb +1 -1
- data/spec/integration/keep_alive_spec.rb +18 -24
- data/spec/integration/smoke_spec.rb +2 -2
- data/spec/integration/streaming_spec.rb +9 -13
- data/spec/support/handler.rb +12 -16
- data/spec/support/helper.rb +8 -8
- data/spec/unit/client_spec.rb +25 -24
- data/spec/unit/connection/eventmachine_spec.rb +9 -8
- data/spec/unit/request_spec.rb +6 -6
- data/spec/unit/response_spec.rb +3 -3
- data/spec/unit/server/keep_alive_spec.rb +67 -0
- data/spec/unit/server_spec.rb +13 -9
- data/spec/unit/support/message_spec.rb +5 -2
- metadata +5 -5
- data/lib/hatetepe/support/keep_alive.rb +0 -14
- data/spec/unit/support/keep_alive_spec.rb +0 -52
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
-
# Hatetepe
|
1
|
+
# Hatetepe, the HTTP toolkit [![Build Status](https://travis-ci.org/lgierth/hatetepe.png?branch=master)](https://travis-ci.org/lgierth/hatetepe) [![Code Climate](https://codeclimate.com/github/lgierth/hatetepe.png)](https://codeclimate.com/github/lgierth/hatetepe)
|
2
2
|
|
3
|
-
|
3
|
+
General purpose toolkit for HTTP clients and servers.
|
4
|
+
|
5
|
+
Note: The `master` branch contains the latest development effort. Look at the
|
6
|
+
`0.5.x` branch for stable releases.
|
4
7
|
|
5
8
|
## Installation
|
6
9
|
|
@@ -27,9 +30,8 @@ TODO: Write usage instructions here
|
|
27
30
|
- Unit tests
|
28
31
|
- Method/Status semantics (GET/HEAD/204/etc.)
|
29
32
|
- Host header
|
33
|
+
- Date header
|
30
34
|
- Documentation
|
31
|
-
- Replace into hatetepe-dev
|
32
|
-
- Deprecate hatetepe-0.5
|
33
35
|
- Future
|
34
36
|
- SSL support
|
35
37
|
- Webmachine adapter
|
data/bin/hatetepe
CHANGED
@@ -1,20 +1,16 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# encoding: utf-8
|
3
3
|
|
4
|
-
require 'ap'
|
5
4
|
require 'hatetepe'
|
6
5
|
|
7
6
|
class RequestHandler
|
8
7
|
def initialize(config, server, connection)
|
9
8
|
end
|
10
9
|
|
11
|
-
def serve(request)
|
12
|
-
request.
|
13
|
-
|
14
|
-
|
15
|
-
request.served.fulfill(response)
|
16
|
-
response.finished.fulfill
|
17
|
-
end
|
10
|
+
def serve(request, served)
|
11
|
+
puts "#{request.http_method.upcase} #{request.uri}"
|
12
|
+
response = build_response
|
13
|
+
served.fulfill(response)
|
18
14
|
end
|
19
15
|
|
20
16
|
def build_response
|
data/config/flay.yml
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
---
|
2
|
-
threshold:
|
3
|
-
total_score:
|
2
|
+
threshold: 8
|
3
|
+
total_score: 147
|
data/config/reek.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
---
|
2
2
|
Attribute:
|
3
|
-
enabled:
|
3
|
+
enabled: false
|
4
4
|
exclude: []
|
5
5
|
BooleanParameter:
|
6
6
|
enabled: true
|
@@ -19,13 +19,13 @@ DataClump:
|
|
19
19
|
DuplicateMethodCall:
|
20
20
|
enabled: true
|
21
21
|
exclude: []
|
22
|
-
max_calls:
|
22
|
+
max_calls: 2
|
23
23
|
allow_calls: []
|
24
24
|
FeatureEnvy:
|
25
|
-
enabled:
|
25
|
+
enabled: false
|
26
26
|
exclude: []
|
27
27
|
IrresponsibleModule:
|
28
|
-
enabled:
|
28
|
+
enabled: false
|
29
29
|
exclude: []
|
30
30
|
LongParameterList:
|
31
31
|
enabled: true
|
@@ -33,7 +33,7 @@ LongParameterList:
|
|
33
33
|
max_params: 2
|
34
34
|
overrides:
|
35
35
|
initialize:
|
36
|
-
max_params:
|
36
|
+
max_params: 4
|
37
37
|
LongYieldList:
|
38
38
|
enabled: true
|
39
39
|
exclude: []
|
@@ -41,7 +41,7 @@ LongYieldList:
|
|
41
41
|
NestedIterators:
|
42
42
|
enabled: true
|
43
43
|
exclude: []
|
44
|
-
max_allowed_nesting:
|
44
|
+
max_allowed_nesting: 2
|
45
45
|
ignore_iterators: []
|
46
46
|
NilCheck:
|
47
47
|
enabled: true
|
@@ -53,7 +53,7 @@ RepeatedConditional:
|
|
53
53
|
TooManyInstanceVariables:
|
54
54
|
enabled: true
|
55
55
|
exclude: []
|
56
|
-
max_instance_variables:
|
56
|
+
max_instance_variables: 4
|
57
57
|
TooManyMethods:
|
58
58
|
enabled: true
|
59
59
|
exclude: []
|
@@ -62,7 +62,7 @@ TooManyStatements:
|
|
62
62
|
enabled: true
|
63
63
|
exclude:
|
64
64
|
- each
|
65
|
-
max_statements:
|
65
|
+
max_statements: 5
|
66
66
|
UncommunicativeMethodName:
|
67
67
|
enabled: true
|
68
68
|
exclude: []
|
@@ -85,7 +85,8 @@ UncommunicativeParameterName:
|
|
85
85
|
- !ruby/regexp /^.$/
|
86
86
|
- !ruby/regexp /[0-9]$/
|
87
87
|
- !ruby/regexp /[A-Z]/
|
88
|
-
accept:
|
88
|
+
accept:
|
89
|
+
- _
|
89
90
|
UncommunicativeVariableName:
|
90
91
|
enabled: true
|
91
92
|
exclude: []
|
@@ -93,11 +94,14 @@ UncommunicativeVariableName:
|
|
93
94
|
- !ruby/regexp /^.$/
|
94
95
|
- !ruby/regexp /[0-9]$/
|
95
96
|
- !ruby/regexp /[A-Z]/
|
96
|
-
accept:
|
97
|
+
accept:
|
98
|
+
- a
|
99
|
+
- e
|
100
|
+
- _
|
97
101
|
UnusedParameters:
|
98
102
|
enabled: true
|
99
103
|
exclude: []
|
100
104
|
UtilityFunction:
|
101
|
-
enabled:
|
105
|
+
enabled: false
|
102
106
|
exclude: []
|
103
107
|
max_helper_calls: 0
|
data/lib/hatetepe.rb
CHANGED
@@ -9,10 +9,10 @@ require 'hatetepe/version'
|
|
9
9
|
|
10
10
|
require 'hatetepe/errors'
|
11
11
|
require 'hatetepe/support/handlers'
|
12
|
-
require 'hatetepe/support/keep_alive'
|
13
12
|
require 'hatetepe/support/message'
|
14
13
|
|
15
14
|
require 'hatetepe/promise'
|
15
|
+
require 'hatetepe/body'
|
16
16
|
require 'hatetepe/request'
|
17
17
|
require 'hatetepe/response'
|
18
18
|
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Hatetepe
|
4
|
+
class Body
|
5
|
+
include Promise::Attribute
|
6
|
+
promise :closed
|
7
|
+
|
8
|
+
def self.build(arg)
|
9
|
+
if arg.is_a?(Body)
|
10
|
+
arg
|
11
|
+
else
|
12
|
+
body = new(arg.to_str)
|
13
|
+
body.closed.fulfill
|
14
|
+
body
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(string = '')
|
19
|
+
@io = StringIO.new(string)
|
20
|
+
|
21
|
+
closed.on_progress { |str| @io.write(str) }
|
22
|
+
end
|
23
|
+
|
24
|
+
# don't optimize the sync call, we don't wanna search in @io.string
|
25
|
+
# because search time increases with its size.
|
26
|
+
def gets(*args)
|
27
|
+
closed.sync
|
28
|
+
@io.gets(*args)
|
29
|
+
end
|
30
|
+
|
31
|
+
def read(length = nil, buffer = nil)
|
32
|
+
closed.sync unless optimized_read?(length)
|
33
|
+
@io.read(length, buffer)
|
34
|
+
end
|
35
|
+
|
36
|
+
def rewind
|
37
|
+
closed.sync
|
38
|
+
@io.rewind
|
39
|
+
end
|
40
|
+
|
41
|
+
def each(&block)
|
42
|
+
yield_string(&block)
|
43
|
+
closed.on_progress(&block)
|
44
|
+
closed.sync
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
closed.sync
|
49
|
+
@io.string
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def optimized_read?(length)
|
55
|
+
length && length < (@io.length - pos)
|
56
|
+
end
|
57
|
+
|
58
|
+
def yield_string(&block)
|
59
|
+
block.call(@io.string) unless @io.string.empty?
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/hatetepe/client.rb
CHANGED
@@ -13,67 +13,67 @@ module Hatetepe
|
|
13
13
|
|
14
14
|
setup_connection
|
15
15
|
setup_handlers
|
16
|
-
notify_handlers(:
|
16
|
+
notify_handlers(:setup)
|
17
17
|
end
|
18
18
|
|
19
19
|
def request(http_method, uri)
|
20
20
|
request = Request.new(http_method, uri)
|
21
|
-
request.
|
22
|
-
perform(request)
|
23
|
-
request.served.sync
|
21
|
+
perform(request).sync
|
24
22
|
end
|
25
23
|
|
26
24
|
def perform(request)
|
27
|
-
|
25
|
+
served = Promise.new
|
26
|
+
@requests << [request, served]
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
@connection.serialize(request)
|
32
|
-
end
|
33
|
-
fiber.resume
|
34
|
-
end
|
35
|
-
|
36
|
-
def close
|
37
|
-
@connection.close
|
28
|
+
Fiber.new { perform!(request) }.resume
|
29
|
+
served
|
38
30
|
end
|
39
31
|
|
40
32
|
def receive(response)
|
41
|
-
|
42
|
-
|
43
|
-
|
33
|
+
request, served = correlate
|
34
|
+
|
35
|
+
served.fulfill(response)
|
36
|
+
response.closed { finish(request) }
|
44
37
|
|
45
|
-
setup_cleanup(request, response)
|
46
38
|
notify_handlers(:receive, request, response)
|
47
39
|
end
|
48
40
|
|
41
|
+
def finish(request)
|
42
|
+
@requests.delete_if { |(req, _)| req == request }
|
43
|
+
notify_handlers(:finish, request)
|
44
|
+
end
|
45
|
+
|
46
|
+
def close
|
47
|
+
@connection.close
|
48
|
+
end
|
49
|
+
|
49
50
|
def teardown(reason)
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
request.served.reject(reason)
|
51
|
+
@requests.each do |(_, served)|
|
52
|
+
served.reject(reason)
|
53
|
+
if (response = served.value)
|
54
|
+
response.reject_closed(reason)
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
59
|
private
|
60
60
|
|
61
|
+
def perform!(request)
|
62
|
+
notify_handlers(:perform, request)
|
63
|
+
@connection.serialize(request)
|
64
|
+
end
|
65
|
+
|
66
|
+
def correlate
|
67
|
+
tuple = @requests.detect { |(_, served)| served.pending? }
|
68
|
+
raise ClientError, 'Unable to correlate with request' unless tuple
|
69
|
+
tuple
|
70
|
+
end
|
71
|
+
|
61
72
|
def setup_connection
|
62
73
|
@connection =
|
63
74
|
EM.connect(@config[:address], @config[:port], Connection::EventMachine)
|
64
75
|
@connection.parse(method(:receive))
|
65
76
|
@connection.closed.then(method(:teardown))
|
66
77
|
end
|
67
|
-
|
68
|
-
def correlate(response)
|
69
|
-
@requests
|
70
|
-
.detect { |request| request.served.pending? }
|
71
|
-
.tap { |request| request.served.fulfill(response) if request }
|
72
|
-
end
|
73
|
-
|
74
|
-
def setup_cleanup(request, response)
|
75
|
-
callback = proc { @requests.delete(request) }
|
76
|
-
response.finished.then(callback, callback)
|
77
|
-
end
|
78
78
|
end
|
79
79
|
end
|
@@ -2,58 +2,13 @@
|
|
2
2
|
|
3
3
|
module Hatetepe
|
4
4
|
class Client::KeepAlive
|
5
|
-
|
6
|
-
|
7
|
-
include Support::KeepAlive
|
8
|
-
|
9
|
-
def initialize(config, client, connection)
|
10
|
-
@config, @client = config, client
|
11
|
-
@connection = connection
|
12
|
-
@close = nil
|
13
|
-
@requests = []
|
5
|
+
def initialize(*_)
|
6
|
+
@previous = nil
|
14
7
|
end
|
15
8
|
|
16
9
|
def perform(request)
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
if @close.nil?
|
21
|
-
await_connection_header(request)
|
22
|
-
elsif !@close
|
23
|
-
await_previous_response(previous_request) if previous_request
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def receive(request, response)
|
28
|
-
@requests.delete(request)
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def current_request
|
34
|
-
@requests.first
|
35
|
-
end
|
36
|
-
|
37
|
-
def await_connection_header(request)
|
38
|
-
if current_request == request
|
39
|
-
# first response has the relevant Connection header
|
40
|
-
assume_connection_header
|
41
|
-
else
|
42
|
-
# subsequent requests wait for first response
|
43
|
-
current_request.served.sync
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def assume_connection_header
|
48
|
-
current_request.served.then do |response|
|
49
|
-
@close = close_connection?(response.http_version,
|
50
|
-
response.headers[CONNECTION])
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def await_previous_response(request)
|
55
|
-
response = request.served.sync
|
56
|
-
response.finished.sync
|
10
|
+
@previous, previous = request, @previous
|
11
|
+
previous.closed! if previous
|
57
12
|
end
|
58
13
|
end
|
59
14
|
end
|
@@ -3,15 +3,15 @@
|
|
3
3
|
module Hatetepe
|
4
4
|
# @see EM.heartbeat_interval
|
5
5
|
class Client::Timeouts
|
6
|
-
def initialize(config,
|
7
|
-
@config
|
6
|
+
def initialize(config, _, connection)
|
7
|
+
@config = config
|
8
8
|
@connection = connection
|
9
9
|
|
10
10
|
@config[:timeout] ||= 2.0
|
11
11
|
@config[:connect_timeout] ||= 2.0
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
14
|
+
def setup
|
15
15
|
@connection.comm_inactivity_timeout = @config[:timeout]
|
16
16
|
@connection.pending_connect_timeout = @config[:connect_timeout]
|
17
17
|
end
|
@@ -6,9 +6,9 @@ module Hatetepe
|
|
6
6
|
include Promise::Attribute
|
7
7
|
promise :closed
|
8
8
|
|
9
|
-
def initialize(callback =
|
9
|
+
def initialize(callback = proc {})
|
10
10
|
@parser = HTTP::Parser.new(self)
|
11
|
-
callback.call(self)
|
11
|
+
callback.call(self)
|
12
12
|
end
|
13
13
|
|
14
14
|
def parse(block)
|
@@ -21,16 +21,16 @@ module Hatetepe
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def receive_data(data)
|
24
|
-
# ap data
|
24
|
+
# ap [__id__, :receive, data]
|
25
25
|
@parser << data
|
26
26
|
# rescue => error
|
27
27
|
# $stderr.puts(error.to_s)
|
28
|
-
# @message.
|
28
|
+
# @message.reject_close(error) if @message
|
29
29
|
# close_connection
|
30
30
|
end
|
31
31
|
|
32
32
|
# def send_data(data)
|
33
|
-
# ap data
|
33
|
+
# ap [__id__, :send, data]
|
34
34
|
# super
|
35
35
|
# end
|
36
36
|
|
@@ -49,12 +49,11 @@ module Hatetepe
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def on_body(chunk)
|
52
|
-
@message.body
|
53
|
-
@message.finished.progress(chunk)
|
52
|
+
@message.body.closed.progress(chunk)
|
54
53
|
end
|
55
54
|
|
56
55
|
def on_message_complete
|
57
|
-
@message.
|
56
|
+
@message.close
|
58
57
|
end
|
59
58
|
end
|
60
59
|
end
|