hatetepe 0.6.0.pre → 0.6.0.pre.1

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/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
- The HTTP toolkit
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.finished.then do
13
- puts "#{request.http_method.upcase} #{request.uri}"
14
- response = build_response
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: 10
3
- total_score: 160
2
+ threshold: 8
3
+ total_score: 147
data/config/reek.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  Attribute:
3
- enabled: true
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: 1
22
+ max_calls: 2
23
23
  allow_calls: []
24
24
  FeatureEnvy:
25
- enabled: true
25
+ enabled: false
26
26
  exclude: []
27
27
  IrresponsibleModule:
28
- enabled: true
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: 3
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: 1
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: 3
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: 2
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: true
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
@@ -13,67 +13,67 @@ module Hatetepe
13
13
 
14
14
  setup_connection
15
15
  setup_handlers
16
- notify_handlers(:post_init)
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.finished.fulfill
22
- perform(request)
23
- request.served.sync
21
+ perform(request).sync
24
22
  end
25
23
 
26
24
  def perform(request)
27
- @requests << request
25
+ served = Promise.new
26
+ @requests << [request, served]
28
27
 
29
- fiber = Fiber.new do
30
- notify_handlers(:perform, request)
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
- unless (request = correlate(response))
42
- raise ClientError, 'Unable to correlate with request'
43
- end
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
- while (request = @requests.shift)
51
- if (response = request.served.value)
52
- response.finished.reject(reason)
53
- else
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
- CONNECTION = 'Connection'.freeze
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
- previous_request = current_request
18
- @requests << request
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, client, connection)
7
- @config, @client = config, client
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 post_init
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 = nil)
9
+ def initialize(callback = proc {})
10
10
  @parser = HTTP::Parser.new(self)
11
- callback.call(self) if callback
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.finished.reject(error) if @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 << chunk
53
- @message.finished.progress(chunk)
52
+ @message.body.closed.progress(chunk)
54
53
  end
55
54
 
56
55
  def on_message_complete
57
- @message.finished.fulfill
56
+ @message.close
58
57
  end
59
58
  end
60
59
  end