httpx 0.0.3 → 0.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 79f53c62644d9e645ab5aeb91d897e0537c3160c
4
- data.tar.gz: 7a9cb5c745c6b02ada9d0d76930372201150dba3
3
+ metadata.gz: 6dac1e1e43e2c1935f8fd0c1807d1d29bf782f27
4
+ data.tar.gz: e5786862d65ee9807f31e9ca847d8ca9125dce29
5
5
  SHA512:
6
- metadata.gz: c623d4faf4e590b757f5e883a62c337364d76da0d01dfb170f47580331edd915abddee82b867a92f86b59d0ea7eefb5c7405554ee7269cc01e12e144a47ec849
7
- data.tar.gz: 40ce7074c72b32de7255e5af941e6a8e38c2f026aa3d1e367fb09ca0d63186a5d71c9b5011ced31ff7e28b446a41508f68ea578ab339872baa0ac5de6b82be53
6
+ metadata.gz: 5a5f4106511049f7dc22c6a44aee01cf560827b553b05aede40e5ff11fb20d52213d754488cec4419a895c9a7eef9c77517242f4553ee0d1b797cfe1cbc4726c
7
+ data.tar.gz: a2390e810c8d6738a9d13fce36bc0cbfcd9e4a7eeb88e5d7a9919a8311dc61404cb3328569836eff86cbabe33b63daef9107d9fe3938136af06b6c78b57f953d
data/README.md CHANGED
@@ -92,14 +92,15 @@ It also means that it ships with the minimum amount of dependencies.
92
92
 
93
93
  ## Easy to test
94
94
 
95
- The test suite runs against [httpbin proxied over nghttp2](https://nghttp2.org/httpbin/), so there is no mocking/stubbing going on. The test suite uses [minitest](https://github.com/seattlerb/minitest), but its matchers usage is limit to assert (assert is all you need).
95
+ The test suite runs against [httpbin proxied over nghttp2](https://nghttp2.org/httpbin/), so there are no mocking/stubbing false positives. The test suite uses [minitest](https://github.com/seattlerb/minitest), but its matchers usage is (almost) limited to `#assert` (`assert` is all you need).
96
96
 
97
97
  ## Supported Rubies
98
98
 
99
99
  All Rubies greater or equal to 2.1, and always latest JRuby.
100
100
 
101
101
  ## Resources
102
-
102
+ | | |
103
+ | ------------- | --------------------------------------------------- |
103
104
  | Website | https://honeyryderchuck.gitlab.io/httpx/ |
104
105
  | Documentation | https://honeyryderchuck.gitlab.io/httpx/rdoc/ |
105
106
  | Wiki | https://gitlab.com/honeyryderchuck/httpx/wikis/home |
data/lib/httpx/channel.rb CHANGED
@@ -196,6 +196,14 @@ module HTTPX
196
196
  parser.on(:close) do
197
197
  transition(:closing)
198
198
  end
199
+ parser.on(:reset) do
200
+ transition(:closing)
201
+ unless parser.empty?
202
+ transition(:closed)
203
+ transition(:idle)
204
+ transition(:open)
205
+ end
206
+ end
199
207
  parser
200
208
  end
201
209
 
@@ -218,7 +226,10 @@ module HTTPX
218
226
  end
219
227
  @state = nextstate
220
228
  rescue Errno::ECONNREFUSED,
221
- Errno::EADDRNOTAVAIL => e
229
+ Errno::ENETUNREACH,
230
+ Errno::EADDRNOTAVAIL,
231
+ OpenSSL::SSL::SSLError => e
232
+ # connect errors, exit gracefully
222
233
  emit_error(e)
223
234
  @state = :closed
224
235
  emit(:close)
@@ -226,7 +237,7 @@ module HTTPX
226
237
 
227
238
  def emit_error(e)
228
239
  response = ErrorResponse.new(e, 0, @options)
229
- @pending.each do |request, _| # rubocop:disable Performance/HashEachMethods
240
+ @pending.each do |request, _|
230
241
  emit(:response, request, response)
231
242
  end
232
243
  end
@@ -71,7 +71,7 @@ module HTTPX
71
71
  # must be public methods, or else they won't be reachable
72
72
 
73
73
  def on_message_begin
74
- log(2) { "parsing begins" }
74
+ log(level: 2) { "parsing begins" }
75
75
  end
76
76
 
77
77
  def on_headers_complete(h)
@@ -81,14 +81,14 @@ module HTTPX
81
81
  request = @requests.first
82
82
  return if request.response
83
83
 
84
- log(2) { "headers received" }
84
+ log(level: 2) { "headers received" }
85
85
  headers = @options.headers_class.new(h)
86
86
  response = @options.response_class.new(@requests.last,
87
87
  @parser.status_code,
88
88
  @parser.http_version.join("."),
89
89
  headers, @options)
90
- log { "-> HEADLINE: #{response.status} HTTP/#{@parser.http_version.join(".")}" }
91
- log { response.headers.each.map { |f, v| "-> HEADER: #{f}: #{v}" }.join("\n") }
90
+ log(color: :yellow) { "-> HEADLINE: #{response.status} HTTP/#{@parser.http_version.join(".")}" }
91
+ log(color: :yellow) { response.headers.each.map { |f, v| "-> HEADER: #{f}: #{v}" }.join("\n") }
92
92
 
93
93
  request.response = response
94
94
 
@@ -96,8 +96,8 @@ module HTTPX
96
96
  end
97
97
 
98
98
  def on_body(chunk)
99
- log { "-> DATA: #{chunk.bytesize} bytes..." }
100
- log(2) { "-> #{chunk.inspect}" }
99
+ log(color: :green) { "-> DATA: #{chunk.bytesize} bytes..." }
100
+ log(level: 2, color: :green) { "-> #{chunk.inspect}" }
101
101
  response = @requests.first.response
102
102
 
103
103
  response << chunk
@@ -106,7 +106,7 @@ module HTTPX
106
106
  end
107
107
 
108
108
  def on_message_complete
109
- log(2) { "parsing complete" }
109
+ log(level: 2) { "parsing complete" }
110
110
  request = @requests.first
111
111
  response = request.response
112
112
 
@@ -144,18 +144,21 @@ module HTTPX
144
144
  close
145
145
  send(@pending.shift) unless @pending.empty?
146
146
  return unless response.headers["connection"] == "close"
147
- unless @requests.empty?
148
- @requests.map { |r| r.transition(:idle) }
149
- # server doesn't handle pipelining, and probably
150
- # doesn't support keep-alive. Fallback to send only
151
- # 1 keep alive request.
152
- @max_concurrent_requests = 1
153
- end
154
- emit(:close)
147
+ disable_concurrency
148
+ emit(:reset)
155
149
  end
156
150
 
157
151
  private
158
152
 
153
+ def disable_concurrency
154
+ return if @requests.empty?
155
+ @requests.each { |r| r.transition(:idle) }
156
+ # server doesn't handle pipelining, and probably
157
+ # doesn't support keep-alive. Fallback to send only
158
+ # 1 keep alive request.
159
+ @max_concurrent_requests = 1
160
+ end
161
+
159
162
  def set_request_headers(request)
160
163
  request.headers["host"] ||= request.authority
161
164
  request.headers["connection"] ||= "keep-alive"
@@ -184,12 +187,12 @@ module HTTPX
184
187
  def join_headers(request)
185
188
  buffer = +""
186
189
  buffer << "#{request.verb.to_s.upcase} #{headline_uri(request)} HTTP/#{@version.join(".")}" << CRLF
187
- log { "<- HEADLINE: #{buffer.chomp.inspect}" }
190
+ log(color: :yellow) { "<- HEADLINE: #{buffer.chomp.inspect}" }
188
191
  @buffer << buffer
189
192
  buffer.clear
190
193
  request.headers.each do |field, value|
191
194
  buffer << "#{capitalized(field)}: #{value}" << CRLF
192
- log { "<- HEADER: #{buffer.chomp}" }
195
+ log(color: :yellow) { "<- HEADER: #{buffer.chomp}" }
193
196
  @buffer << buffer
194
197
  buffer.clear
195
198
  end
@@ -200,8 +203,8 @@ module HTTPX
200
203
  def join_body(request)
201
204
  return if request.empty?
202
205
  while (chunk = request.drain_body)
203
- log { "<- DATA: #{chunk.bytesize} bytes..." }
204
- log(2) { "<- #{chunk.inspect}" }
206
+ log(color: :green) { "<- DATA: #{chunk.bytesize} bytes..." }
207
+ log(level: 2, color: :green) { "<- #{chunk.inspect}" }
205
208
  @buffer << chunk
206
209
  throw(:buffer_full, request) if @buffer.full?
207
210
  end
@@ -92,7 +92,7 @@ module HTTPX
92
92
  def handle_stream(stream, request)
93
93
  stream.on(:close, &method(:on_stream_close).curry[stream, request])
94
94
  stream.on(:half_close) do
95
- log(2, "#{stream.id}: ") { "waiting for response..." }
95
+ log(level: 2, label: "#{stream.id}: ") { "waiting for response..." }
96
96
  end
97
97
  # stream.on(:altsvc)
98
98
  stream.on(:headers, &method(:on_stream_headers).curry[stream, request])
@@ -107,7 +107,7 @@ module HTTPX
107
107
  headers[":path"] = headline_uri(request)
108
108
  headers[":authority"] = request.authority
109
109
  headers = headers.merge(request.headers)
110
- log(1, "#{stream.id}: ") do
110
+ log(level: 1, label: "#{stream.id}: ", color: :yellow) do
111
111
  headers.map { |k, v| "-> HEADER: #{k}: #{v}" }.join("\n")
112
112
  end
113
113
  stream.headers(headers, end_stream: request.empty?)
@@ -117,8 +117,8 @@ module HTTPX
117
117
  chunk = @drains.delete(request) || request.drain_body
118
118
  while chunk
119
119
  next_chunk = request.drain_body
120
- log(1, "#{stream.id}: ") { "-> DATA: #{chunk.bytesize} bytes..." }
121
- log(2, "#{stream.id}: ") { "-> #{chunk.inspect}" }
120
+ log(level: 1, label: "#{stream.id}: ", color: :green) { "-> DATA: #{chunk.bytesize} bytes..." }
121
+ log(level: 2, label: "#{stream.id}: ", color: :green) { "-> #{chunk.inspect}" }
122
122
  stream.data(chunk, end_stream: !next_chunk)
123
123
  if next_chunk && @buffer.full?
124
124
  @drains[request] = next_chunk
@@ -133,7 +133,7 @@ module HTTPX
133
133
  ######
134
134
 
135
135
  def on_stream_headers(stream, request, h)
136
- log(stream.id) do
136
+ log(label: "#{stream.id}:", color: :yellow) do
137
137
  h.map { |k, v| "<- HEADER: #{k}: #{v}" }.join("\n")
138
138
  end
139
139
  _, status = h.shift
@@ -144,8 +144,8 @@ module HTTPX
144
144
  end
145
145
 
146
146
  def on_stream_data(stream, request, data)
147
- log(1, "#{stream.id}: ") { "<- DATA: #{data.bytesize} bytes..." }
148
- log(2, "#{stream.id}: ") { "<- #{data.inspect}" }
147
+ log(level: 1, label: "#{stream.id}: ", color: :green) { "<- DATA: #{data.bytesize} bytes..." }
148
+ log(level: 2, label: "#{stream.id}: ", color: :green) { "<- #{data.inspect}" }
149
149
  request.response << data
150
150
  end
151
151
 
@@ -153,7 +153,7 @@ module HTTPX
153
153
  return handle(request, stream) if request.expects?
154
154
  response = request.response || ErrorResponse.new(Error.new(error), @retries, @options)
155
155
  emit(:response, request, response)
156
- log(2, "#{stream.id}: ") { "closing stream" }
156
+ log(level: 2, label: "#{stream.id}: ") { "closing stream" }
157
157
 
158
158
  @streams.delete(request)
159
159
  send(@pending.shift) unless @pending.empty?
@@ -174,13 +174,11 @@ module HTTPX
174
174
  end
175
175
 
176
176
  def on_frame_sent(frame)
177
- log(2, "#{frame[:stream]}: ") { "frame was sent!" }
178
- log(2, "#{frame[:stream]}: ") do
177
+ log(level: 2, label: "#{frame[:stream]}: ") { "frame was sent!" }
178
+ log(level: 2, label: "#{frame[:stream]}: ", color: :blue) do
179
179
  case frame[:type]
180
180
  when :data
181
181
  frame.merge(payload: frame[:payload].bytesize).inspect
182
- when :headers
183
- "\e[33m#{frame.inspect}\e[0m"
184
182
  else
185
183
  frame.inspect
186
184
  end
@@ -188,8 +186,8 @@ module HTTPX
188
186
  end
189
187
 
190
188
  def on_frame_received(frame)
191
- log(2, "#{frame[:stream]}: ") { "frame was received!" }
192
- log(2, "#{frame[:stream]}: ") do
189
+ log(level: 2, label: "#{frame[:stream]}: ") { "frame was received!" }
190
+ log(level: 2, label: "#{frame[:stream]}: ", color: :magenta) do
193
191
  case frame[:type]
194
192
  when :data
195
193
  frame.merge(payload: frame[:payload].bytesize).inspect
@@ -200,8 +198,8 @@ module HTTPX
200
198
  end
201
199
 
202
200
  def on_altsvc(frame)
203
- log(2, "#{frame[:stream]}: ") { "altsvc frame was received" }
204
- log(2, "#{frame[:stream]}: ") { frame.inspect }
201
+ log(level: 2, label: "#{frame[:stream]}: ") { "altsvc frame was received" }
202
+ log(level: 2, label: "#{frame[:stream]}: ") { frame.inspect }
205
203
  end
206
204
 
207
205
  def on_promise(stream)
data/lib/httpx/client.rb CHANGED
@@ -9,16 +9,18 @@ module HTTPX
9
9
  @options = self.class.default_options.merge(options)
10
10
  @connection = Connection.new(@options)
11
11
  @responses = {}
12
+ @keep_open = false
12
13
  wrap(&blk) if block_given?
13
14
  end
14
15
 
15
16
  def wrap
16
17
  return unless block_given?
17
18
  begin
19
+ prev_keep_open = @keep_open
18
20
  @keep_open = true
19
21
  yield self
20
22
  ensure
21
- @keep_open = false
23
+ @keep_open = prev_keep_open
22
24
  close
23
25
  end
24
26
  end
@@ -43,7 +45,7 @@ module HTTPX
43
45
  end
44
46
 
45
47
  def on_promise(_, stream)
46
- log(2, "#{stream.id}: ") { "refusing stream!" }
48
+ log(level: 2, label: "#{stream.id}: ") { "refusing stream!" }
47
49
  stream.refuse
48
50
  # TODO: policy for handling promises
49
51
  end
data/lib/httpx/errors.rb CHANGED
@@ -6,11 +6,15 @@ module HTTPX
6
6
  TimeoutError = Class.new(Error)
7
7
 
8
8
  HTTPError = Class.new(Error) do
9
- attr_reader :status
9
+ attr_reader :response
10
10
 
11
- def initialize(status)
12
- @status = status
13
- super("HTTP Error: #{status}")
11
+ def initialize(response)
12
+ @response = response
13
+ super("HTTP Error: #{@response.status}")
14
+ end
15
+
16
+ def status
17
+ @response.status
14
18
  end
15
19
  end
16
20
  end
data/lib/httpx/headers.rb CHANGED
@@ -5,16 +5,16 @@ module HTTPX
5
5
  EMPTY = [].freeze # :nodoc:
6
6
 
7
7
  class << self
8
- def new(h = nil)
9
- return h if h.is_a?(self)
8
+ def new(headers = nil)
9
+ return headers if headers.is_a?(self)
10
10
  super
11
11
  end
12
12
  end
13
13
 
14
- def initialize(h = nil)
14
+ def initialize(headers = nil)
15
15
  @headers = {}
16
- return unless h
17
- h.each do |field, value|
16
+ return unless headers
17
+ headers.each do |field, value|
18
18
  array_value(value).each do |v|
19
19
  add(downcased(field), v)
20
20
  end
data/lib/httpx/io.rb CHANGED
@@ -137,7 +137,7 @@ module HTTPX
137
137
  when :closed
138
138
  return unless @state == :connected
139
139
  end
140
- log(1, "#{inspect}: ") { nextstate.to_s }
140
+ log(level: 1, label: "#{inspect}: ") { nextstate.to_s }
141
141
  @state = nextstate
142
142
  end
143
143
  end
@@ -2,10 +2,23 @@
2
2
 
3
3
  module HTTPX
4
4
  module Loggable
5
- def log(level = @options.debug_level, label = "", &msg)
5
+ COLORS = {
6
+ black: 30,
7
+ red: 31,
8
+ green: 32,
9
+ yellow: 33,
10
+ blue: 34,
11
+ magenta: 35,
12
+ cyan: 36,
13
+ white: 37,
14
+ }.freeze
15
+
16
+ def log(level: @options.debug_level, label: "", color: nil, &msg)
6
17
  return unless @options.debug
7
18
  return unless @options.debug_level >= level
8
- @options.debug << (+label << msg.call << "\n")
19
+ message = (+label << msg.call << "\n")
20
+ message = "\e[#{COLORS[color]}m#{message}\e[0m" if color && @options.debug.isatty
21
+ @options.debug << message
9
22
  end
10
23
  end
11
24
  end
@@ -37,9 +37,9 @@ module HTTPX
37
37
 
38
38
  module ResponseMethods
39
39
  def cookie_jar
40
- return @cookies if defined?(@cookies)
40
+ return @cookie_jar if defined?(@cookie_jar)
41
41
  return nil unless headers.key?("set-cookie")
42
- @cookies ||= begin
42
+ @cookie_jar ||= begin
43
43
  jar = HTTP::CookieJar.new
44
44
  jar.parse(headers["set-cookie"], @request.uri)
45
45
  jar
@@ -53,7 +53,7 @@ module HTTPX
53
53
  return unless @state == :connecting
54
54
  @parser = nil
55
55
  end
56
- log(1, "SOCKS4: ") { "#{nextstate}: #{@write_buffer.to_s.inspect}" }
56
+ log(level: 1, label: "SOCKS4: ") { "#{nextstate}: #{@write_buffer.to_s.inspect}" }
57
57
  super
58
58
  end
59
59
  end
@@ -86,7 +86,7 @@ module HTTPX
86
86
  return unless @state == :negotiating
87
87
  @parser = nil
88
88
  end
89
- log(1, "SOCKS5: ") { "#{nextstate}: #{@write_buffer.to_s.inspect}" }
89
+ log(level: 1, label: "SOCKS5: ") { "#{nextstate}: #{@write_buffer.to_s.inspect}" }
90
90
  super
91
91
  end
92
92
 
@@ -34,7 +34,7 @@ module HTTPX
34
34
  end
35
35
 
36
36
  def __on_promise_request(parser, stream, h)
37
- log(1, "#{stream.id}: ") do
37
+ log(level: 1, label: "#{stream.id}: ") do
38
38
  h.map { |k, v| "-> PROMISE HEADER: #{k}: #{v}" }.join("\n")
39
39
  end
40
40
  headers = @options.headers_class.new(h)
@@ -62,7 +62,7 @@ module HTTPX
62
62
 
63
63
  def raise_for_status
64
64
  return if @status < 400
65
- raise HTTPError, @status
65
+ raise HTTPError, self
66
66
  end
67
67
 
68
68
  class Body
data/lib/httpx/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- VERSION = "0.0.3"
4
+ VERSION = "0.0.4"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: httpx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Cardoso
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-26 00:00:00.000000000 Z
11
+ date: 2018-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http-2