httpx 0.0.3 → 0.0.4

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: 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