net-http2 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ba9816c229aab7bb320437a77c3c91ed60752609
4
- data.tar.gz: 63836c93155c8d717f68542519b145f87e5e6bd1
3
+ metadata.gz: 06fbac929ced0fe89503612541dd469842084882
4
+ data.tar.gz: b1a1119b920b3e2aaa2445b4f3810a9438ee32b2
5
5
  SHA512:
6
- metadata.gz: bb589fa1c74d8fb02f79161660756667c3420baaa3810007a47506df85c5b996cfa46524d98bf6a11e2099dedc9fd2bba7922fd4ed171201dce5b59472e5012a
7
- data.tar.gz: 749b07f13a864be5a935ee7fbeef1bc912e1a4e75170f9c1334de8799aeeb0f318e43a6cc209bcc8733b6687121bee756165a759e17273e47ddc50fbeaee63ba
6
+ metadata.gz: 7a49a1d2f16b33d0260464584880d74e55076d4b7c1f59545f9fabb6af2861c8c357596f1fd16ab30b4212450be0ea242f2bcf2ea76c6a966d8d9f38762f73f4
7
+ data.tar.gz: 1d7492113575170952373c2326c13bab44018f1ae941f71a1ff56eeb6e4426ef9721499c6b88a12febda67fb73c1b93d65eb76ec7bada382b0650fa7b2aa49d4
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  [![Build Status](https://travis-ci.org/ostinelli/net-http2.svg?branch=master)](https://travis-ci.org/ostinelli/net-http2)
2
+ [![Code Climate](https://codeclimate.com/github/ostinelli/net-http2/badges/gpa.svg)](https://codeclimate.com/github/ostinelli/net-http2)
2
3
 
3
4
  # NetHttp2
4
5
 
5
6
  NetHttp2 is an HTTP/2 client for Ruby.
6
7
 
7
-
8
8
  ## Installation
9
9
  Just install the gem:
10
10
 
@@ -19,7 +19,9 @@ gem 'net-http2'
19
19
  ```
20
20
 
21
21
  ## Usage
22
+ NetHttp2 can perform sync and async calls. Sync calls are very similar to the HTTP/1 calls, while async calls take advantage of the streaming properties of HTTP/2.
22
23
 
24
+ To perform a sync call:
23
25
  ```ruby
24
26
  require 'net-http2'
25
27
 
@@ -27,7 +29,7 @@ require 'net-http2'
27
29
  client = NetHttp2::Client.new("http://106.186.112.116")
28
30
 
29
31
  # send request
30
- response = client.get('/')
32
+ response = client.call(:get, '/')
31
33
 
32
34
  # read the response
33
35
  response.ok? # => true
@@ -39,16 +41,34 @@ response.body # => "A body"
39
41
  client.close
40
42
  ```
41
43
 
44
+ To perform an async call:
45
+ ```ruby
46
+ require 'net-http2'
42
47
 
43
- ## Objects
48
+ # create a client
49
+ client = NetHttp2::Client.new("http://106.186.112.116")
44
50
 
45
- ### `NetHttp2::Client`
46
- To create a new client:
51
+ # prepare request
52
+ request = client.prepare_request(:get, '/')
53
+ request.on(:headers) { |headers| p headers }
54
+ request.on(:body_chunk) { |chunk| p chunk }
55
+ request.on(:close) { puts "request completed!" }
47
56
 
48
- ```ruby
49
- NetHttp2::Client.new(url)
57
+ # send
58
+ client.call_async(request)
59
+
60
+ # We need to wait for the callbacks to be triggered, so here's a quick and dirty fix for this example.
61
+ # In real life, if you are using async requests so you probably have a running loop that keeps your program alive.
62
+ sleep 5
63
+
64
+ # close the connection
65
+ client.close
50
66
  ```
51
67
 
68
+ ## Objects
69
+
70
+ ### `NetHttp2::Client`
71
+
52
72
  #### Methods
53
73
 
54
74
  * **new(url, options={})** → **`NetHttp2::Client`**
@@ -56,16 +76,20 @@ NetHttp2::Client.new(url)
56
76
  Returns a new client. `url` is a `string` such as https://localhost:443.
57
77
  The only current option is `:ssl_context`, in case the url has an https scheme and you want your SSL client to use a custom context.
58
78
 
59
- For instance:
79
+ To create a new client:
80
+ ```ruby
81
+ NetHttp2::Client.new("http://106.186.112.116")
82
+ ```
60
83
 
61
- ```ruby
62
- certificate = File.read("cert.pem")
63
- ctx = OpenSSL::SSL::SSLContext.new
64
- ctx.key = OpenSSL::PKey::RSA.new(certificate, "cert_password")
65
- ctx.cert = OpenSSL::X509::Certificate.new(certificate)
84
+ To create a new client with a custom SSL context:
85
+ ```ruby
86
+ certificate = File.read("cert.pem")
87
+ ctx = OpenSSL::SSL::SSLContext.new
88
+ ctx.key = OpenSSL::PKey::RSA.new(certificate, "cert_password")
89
+ ctx.cert = OpenSSL::X509::Certificate.new(certificate)
66
90
 
67
- NetHttp2::Client.new(url, ssl_context: ctx)
68
- ```
91
+ NetHttp2::Client.new("http://106.186.112.116", ssl_context: ctx)
92
+ ```
69
93
 
70
94
  * **uri** → **`URI`**
71
95
 
@@ -80,18 +104,68 @@ These behave similarly to HTTP/1 calls.
80
104
 
81
105
  `method` is a symbol that specifies the `:method` header (`:get`, `:post`, `:put`, `:patch`, `:delete`, `:options`). The body and the headers of the request can be specified in the options, together with the timeout.
82
106
 
83
- For example:
84
-
85
- ```ruby
86
- response_1 = client.call(:get, '/path1')
87
- response_2 = client.call(:get, '/path2', headers: { 'x-custom' => 'custom' })
88
- response_3 = client.call(:post '/path3', body: "the request body", timeout: 1)
89
- ```
107
+ ```ruby
108
+ response_1 = client.call(:get, '/path1')
109
+ response_2 = client.call(:get, '/path2', headers: { 'x-custom' => 'custom' })
110
+ response_3 = client.call(:post '/path3', body: "the request body", timeout: 1)
111
+ ```
90
112
 
91
113
 
92
114
  ##### Non-blocking calls
115
+ The real benefit of HTTP/2 is being able to receive body and header streams. Instead of buffering the whole response, you might want to react immediately upon receiving those streams. This is what non-blocking calls are for.
116
+
117
+ * **prepare_request(method, path, options={})** → **`NetHttp2::Request`**
118
+
119
+ Prepares an async request. Arguments are the same as the `call` method, with the difference that the `:timeout` option will be ignored. In an async call, you will need to write your own logic for timeouts.
93
120
 
94
- > The real benefit of HTTP/2 is being able to receive body and header streams. The non-blocking API calls are currently being developed.
121
+ ```ruby
122
+ request = client.prepare_request(:get, '/path', headers: { 'x-custom-header' => 'custom' })
123
+ ```
124
+
125
+ * **on(event, &block)**
126
+
127
+ Allows to set a callback for the request. Available events are:
128
+
129
+ * `:headers`: triggered when headers are received (called once).
130
+ * `:body_chunk`: triggered when body chunks are received (may be called multiple times).
131
+ * `:close`: triggered when the request has been completed (called once).
132
+
133
+ Even if NetHttp2 is thread-safe, the async callbacks will be executed in a different thread, so ensure that your code in the callbacks is thread-safe.
134
+
135
+ ```ruby
136
+ request.on(:headers) { |headers| p headers }
137
+ request.on(:body_chunk) { |chunk| p chunk }
138
+ request.on(:close) { puts "request completed!" }
139
+ ```
140
+
141
+ * **call_async(request)**
142
+
143
+ Calls the server with the async request.
144
+
145
+
146
+ ### `NetHttp2::Request`
147
+
148
+ #### Methods
149
+
150
+ * **method** → **`symbol`**
151
+
152
+ The request's method.
153
+
154
+ * **uri** → **`URI`**
155
+
156
+ The request's URI.
157
+
158
+ * **path** → **`string`**
159
+
160
+ The request's path.
161
+
162
+ * **body** → **`string`**
163
+
164
+ The request's body.
165
+
166
+ * **timeout** → **`integer`**
167
+
168
+ The request's timeout.
95
169
 
96
170
 
97
171
  ### `NetHttp2::Response`
@@ -115,6 +189,9 @@ These behave similarly to HTTP/1 calls.
115
189
  Returns the RAW body of the response.
116
190
 
117
191
 
192
+ ## Thread-Safety
193
+ NetHttp2 is thread-safe.
194
+
118
195
  ## Contributing
119
196
  So you want to contribute? That's great! Please follow the guidelines below. It will make it easier to get merged in.
120
197
 
@@ -22,11 +22,20 @@ module NetHttp2
22
22
  end
23
23
 
24
24
  def call(method, path, options={})
25
- request = NetHttp2::Request.new(method, @uri, path, options)
25
+ request = prepare_request(method, path, options)
26
26
  ensure_open
27
27
  new_stream.call_with request
28
28
  end
29
29
 
30
+ def call_async(request)
31
+ ensure_open
32
+ new_stream.async_call_with request
33
+ end
34
+
35
+ def prepare_request(method, path, options={})
36
+ NetHttp2::Request.new(method, @uri, path, options)
37
+ end
38
+
30
39
  def ssl?
31
40
  @is_ssl
32
41
  end
@@ -42,27 +51,25 @@ module NetHttp2
42
51
 
43
52
  private
44
53
 
45
- def async_call_with(request, &block)
46
- ensure_open
47
- new_stream.async_call_with request, &block
48
- end
49
-
50
54
  def new_stream
51
55
  NetHttp2::Stream.new(uri: @uri, h2_stream: h2.new_stream)
52
56
  end
53
57
 
54
58
  def ensure_open
55
- return if @socket_thread
59
+ @mutex.synchronize do
56
60
 
57
- socket = new_socket
61
+ return if @socket_thread
58
62
 
59
- @socket_thread = Thread.new do
63
+ socket = new_socket
60
64
 
61
- begin
62
- thread_loop(socket)
63
- ensure
64
- socket.close unless socket.closed?
65
- @socket_thread = nil
65
+ @socket_thread = Thread.new do
66
+
67
+ begin
68
+ thread_loop(socket)
69
+ ensure
70
+ socket.close unless socket.closed?
71
+ @socket_thread = nil
72
+ end
66
73
  end
67
74
  end
68
75
  end
@@ -83,7 +90,7 @@ module NetHttp2
83
90
  end
84
91
 
85
92
  if ready[0].include?(@pipe_r)
86
- data_to_send = @pipe_r.read_nonblock(1024)
93
+ data_to_send = @pipe_r.readpartial(1024)
87
94
  socket.write(data_to_send)
88
95
  end
89
96
  end
@@ -146,7 +153,7 @@ module NetHttp2
146
153
  end
147
154
 
148
155
  def exit_thread(thread)
149
- return unless thread && thread.alive?
156
+ return unless thread
150
157
  thread.exit
151
158
  thread.join
152
159
  end
@@ -13,6 +13,8 @@ module NetHttp2
13
13
  @body = options[:body]
14
14
  @headers = options[:headers] || {}
15
15
  @timeout = options[:timeout] || DEFAULT_TIMEOUT
16
+
17
+ @events = {}
16
18
  end
17
19
 
18
20
  def headers
@@ -30,8 +32,19 @@ module NetHttp2
30
32
  @headers.delete('content-length')
31
33
  end
32
34
 
33
-
34
35
  @headers
35
36
  end
37
+
38
+ def on(event, &block)
39
+ raise ArgumentError, 'on event must provide a block' unless block_given?
40
+
41
+ @events[event] ||= []
42
+ @events[event] << block
43
+ end
44
+
45
+ def emit(event, arg)
46
+ return unless @events[event]
47
+ @events[event].each { |b| b.call(arg) }
48
+ end
36
49
  end
37
50
  end
@@ -4,30 +4,72 @@ module NetHttp2
4
4
 
5
5
  def initialize(options={})
6
6
  @h2_stream = options[:h2_stream]
7
- @uri = options[:uri]
8
7
  @headers = {}
9
8
  @data = ''
9
+ @request = nil
10
10
  @completed = false
11
- @block = nil
11
+ @async = false
12
12
 
13
- @h2_stream.on(:headers) do |hs|
14
- hs.each { |k, v| @headers[k] = v }
15
- end
16
-
17
- @h2_stream.on(:data) { |d| @data << d }
18
- @h2_stream.on(:close) { @completed = true }
13
+ listen_for_headers
14
+ listen_for_data
15
+ listen_for_close
19
16
  end
20
17
 
21
18
  def call_with(request)
22
- send_data_of request
23
- sync_respond(request.timeout)
19
+ @request = request
20
+ send_request_data
21
+ sync_respond
22
+ end
23
+
24
+ def async_call_with(request)
25
+ @request = request
26
+ @async = true
27
+ send_request_data
28
+ end
29
+
30
+ def completed?
31
+ @completed
32
+ end
33
+
34
+ def async?
35
+ @async
24
36
  end
25
37
 
26
38
  private
27
39
 
28
- def send_data_of(request)
29
- headers = request.headers
30
- body = request.body
40
+ def listen_for_headers
41
+ @h2_stream.on(:headers) do |hs_array|
42
+ hs = Hash[*hs_array.flatten]
43
+
44
+ if async?
45
+ @request.emit(:headers, hs)
46
+ else
47
+ @headers.merge!(hs)
48
+ end
49
+ end
50
+ end
51
+
52
+ def listen_for_data
53
+ @h2_stream.on(:data) do |data|
54
+ if async?
55
+ @request.emit(:body_chunk, data)
56
+ else
57
+ @data << data
58
+ end
59
+ end
60
+ end
61
+
62
+ def listen_for_close
63
+ @h2_stream.on(:close) do |data|
64
+ @completed = true
65
+
66
+ @request.emit(:close, data) if async?
67
+ end
68
+ end
69
+
70
+ def send_request_data
71
+ headers = @request.headers
72
+ body = @request.body
31
73
 
32
74
  if body
33
75
  @h2_stream.headers(headers, end_stream: false)
@@ -37,24 +79,18 @@ module NetHttp2
37
79
  end
38
80
  end
39
81
 
40
- def sync_respond(timeout)
41
- wait(timeout)
42
- response if @completed
82
+ def sync_respond
83
+ wait_for_completed
84
+
85
+ NetHttp2::Response.new(headers: @headers, body: @data) if @completed
43
86
  end
44
87
 
45
- def wait(timeout)
46
- cutoff_time = Time.now + timeout
88
+ def wait_for_completed
89
+ cutoff_time = Time.now + @request.timeout
47
90
 
48
91
  while !@completed && Time.now < cutoff_time
49
92
  sleep 0.1
50
93
  end
51
94
  end
52
-
53
- def response
54
- NetHttp2::Response.new(
55
- headers: @headers,
56
- body: @data
57
- )
58
- end
59
95
  end
60
96
  end
@@ -1,3 +1,3 @@
1
1
  module NetHttp2
2
- VERSION = "0.8.0"
2
+ VERSION = "0.9.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-http2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roberto Ostinelli
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-04-29 00:00:00.000000000 Z
11
+ date: 2016-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http-2