httpx 0.0.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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +191 -0
  3. data/README.md +119 -0
  4. data/lib/httpx.rb +50 -0
  5. data/lib/httpx/buffer.rb +34 -0
  6. data/lib/httpx/callbacks.rb +32 -0
  7. data/lib/httpx/chainable.rb +51 -0
  8. data/lib/httpx/channel.rb +222 -0
  9. data/lib/httpx/channel/http1.rb +220 -0
  10. data/lib/httpx/channel/http2.rb +224 -0
  11. data/lib/httpx/client.rb +173 -0
  12. data/lib/httpx/connection.rb +74 -0
  13. data/lib/httpx/errors.rb +7 -0
  14. data/lib/httpx/extensions.rb +52 -0
  15. data/lib/httpx/headers.rb +152 -0
  16. data/lib/httpx/io.rb +240 -0
  17. data/lib/httpx/loggable.rb +11 -0
  18. data/lib/httpx/options.rb +138 -0
  19. data/lib/httpx/plugins/authentication.rb +14 -0
  20. data/lib/httpx/plugins/basic_authentication.rb +20 -0
  21. data/lib/httpx/plugins/compression.rb +123 -0
  22. data/lib/httpx/plugins/compression/brotli.rb +55 -0
  23. data/lib/httpx/plugins/compression/deflate.rb +50 -0
  24. data/lib/httpx/plugins/compression/gzip.rb +59 -0
  25. data/lib/httpx/plugins/cookies.rb +63 -0
  26. data/lib/httpx/plugins/digest_authentication.rb +141 -0
  27. data/lib/httpx/plugins/follow_redirects.rb +72 -0
  28. data/lib/httpx/plugins/h2c.rb +85 -0
  29. data/lib/httpx/plugins/proxy.rb +108 -0
  30. data/lib/httpx/plugins/proxy/http.rb +115 -0
  31. data/lib/httpx/plugins/proxy/socks4.rb +110 -0
  32. data/lib/httpx/plugins/proxy/socks5.rb +152 -0
  33. data/lib/httpx/plugins/push_promise.rb +67 -0
  34. data/lib/httpx/plugins/stream.rb +33 -0
  35. data/lib/httpx/registry.rb +88 -0
  36. data/lib/httpx/request.rb +222 -0
  37. data/lib/httpx/response.rb +225 -0
  38. data/lib/httpx/selector.rb +155 -0
  39. data/lib/httpx/timeout.rb +68 -0
  40. data/lib/httpx/transcoder.rb +12 -0
  41. data/lib/httpx/transcoder/body.rb +56 -0
  42. data/lib/httpx/transcoder/chunker.rb +38 -0
  43. data/lib/httpx/transcoder/form.rb +41 -0
  44. data/lib/httpx/transcoder/json.rb +36 -0
  45. data/lib/httpx/version.rb +5 -0
  46. metadata +150 -0
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ class HTTPX::Selector
4
+ #
5
+ # I/O monitor
6
+ #
7
+ class Monitor
8
+ attr_accessor :value, :interests, :readiness
9
+
10
+ def initialize(io, interests, reactor)
11
+ @io = io
12
+ @interests = interests
13
+ @reactor = reactor
14
+ @closed = false
15
+ end
16
+
17
+ def readable?
18
+ @interests == :rw || @interests == :r
19
+ end
20
+
21
+ def writable?
22
+ @interests == :rw || @interests == :w
23
+ end
24
+
25
+ # closes +@io+, deregisters from reactor (unless +deregister+ is false)
26
+ def close(deregister = true)
27
+ return if @closed
28
+ @closed = true
29
+ @reactor.deregister(@io) if deregister
30
+ end
31
+
32
+ def closed?
33
+ @closed
34
+ end
35
+
36
+ # :nocov:
37
+ def to_s
38
+ "#<#{self.class}: #{@io}(closed:#{@closed}) #{@interests} #{object_id.to_s(16)}>"
39
+ end
40
+ # :nocov:
41
+ end
42
+
43
+ def initialize
44
+ @readers = {}
45
+ @writers = {}
46
+ @lock = Mutex.new
47
+ @__r__, @__w__ = IO.pipe
48
+ @closed = false
49
+ end
50
+
51
+ def empty?
52
+ @readers.empty? && @writers.empty?
53
+ end
54
+
55
+ # deregisters +io+ from selectables.
56
+ def deregister(io)
57
+ @lock.synchronize do
58
+ rmonitor = @readers.delete(io)
59
+ wmonitor = @writers.delete(io)
60
+ monitor = rmonitor || wmonitor
61
+ monitor.close(false) if monitor
62
+ end
63
+ end
64
+
65
+ # register +io+ for +interests+ events.
66
+ def register(io, interests)
67
+ readable = interests == :r || interests == :rw
68
+ writable = interests == :w || interests == :rw
69
+ @lock.synchronize do
70
+ if readable
71
+ monitor = @readers[io]
72
+ if monitor
73
+ monitor.interests = interests
74
+ else
75
+ monitor = Monitor.new(io, interests, self)
76
+ end
77
+ @readers[io] = monitor
78
+ @writers.delete(io) unless writable
79
+ end
80
+ if writable
81
+ monitor = @writers[io]
82
+ if monitor
83
+ monitor.interests = interests
84
+ else
85
+ # reuse object
86
+ monitor = readable ? @readers[io] : Monitor.new(io, interests, self)
87
+ end
88
+ @writers[io] = monitor
89
+ @readers.delete(io) unless readable
90
+ end
91
+ monitor
92
+ end
93
+ end
94
+
95
+ # waits for read/write events for +interval+. Yields for monitors of
96
+ # selected IO objects.
97
+ #
98
+ def select(interval)
99
+ begin
100
+ r = nil
101
+ w = nil
102
+ @lock.synchronize do
103
+ r = @readers.keys
104
+ w = @writers.keys
105
+ end
106
+ r.unshift(@__r__)
107
+
108
+ readers, writers = IO.select(r, w, nil, interval)
109
+
110
+ raise HTTPX::TimeoutError, "timed out while waiting on select" if readers.nil? && writers.nil?
111
+ rescue IOError, SystemCallError
112
+ @lock.synchronize do
113
+ @readers.reject! { |io, _| io.closed? }
114
+ @writers.reject! { |io, _| io.closed? }
115
+ end
116
+ retry
117
+ end
118
+
119
+ readers.each do |io|
120
+ if io == @__r__
121
+ # clean up wakeups
122
+ @__r__.read(@__r__.stat.size)
123
+ else
124
+ monitor = io.closed? ? @readers.delete(io) : @readers[io]
125
+ next unless monitor
126
+ monitor.readiness = writers.delete(io) ? :rw : :r
127
+ yield monitor
128
+ end
129
+ end if readers
130
+
131
+ writers.each do |io|
132
+ monitor = io.closed? ? @writers.delete(io) : @writers[io]
133
+ next unless monitor
134
+ # don't double run this, the last iteration might have run this task already
135
+ monitor.readiness = :w
136
+ yield monitor
137
+ end if writers
138
+ end
139
+
140
+ # Closes the selector.
141
+ #
142
+ def close
143
+ return if @closed
144
+ @__r__.close
145
+ @__w__.close
146
+ rescue IOError
147
+ ensure
148
+ @closed = true
149
+ end
150
+
151
+ # interrupts the select call.
152
+ def wakeup
153
+ @__w__.write_nonblock("\0", exception: false)
154
+ end
155
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "timeout"
4
+
5
+ module HTTPX
6
+ class Timeout
7
+ LOOP_TIMEOUT = 5
8
+
9
+ def self.new(opts = {})
10
+ return opts if opts.is_a?(Timeout)
11
+ super
12
+ end
13
+
14
+ def initialize(loop_timeout: 5, total_timeout: nil)
15
+ @loop_timeout = loop_timeout
16
+ @total_timeout = total_timeout
17
+ reset_counter
18
+ end
19
+
20
+ def timeout
21
+ @loop_timeout || @total_timeout
22
+ ensure
23
+ log_time
24
+ end
25
+
26
+ def ==(other)
27
+ if other.is_a?(Timeout)
28
+ @loop_timeout == other.instance_variable_get(:@loop_timeout) &&
29
+ @total_timeout == other.instance_variable_get(:@total_timeout)
30
+ else
31
+ super
32
+ end
33
+ end
34
+
35
+ def merge(other)
36
+ case other
37
+ when Hash
38
+ timeout = Timeout.new(other)
39
+ merge(timeout)
40
+ when Timeout
41
+ loop_timeout = other.instance_variable_get(:@loop_timeout) || @loop_timeout
42
+ total_timeout = other.instance_variable_get(:@total_timeout) || @total_timeout
43
+ Timeout.new(loop_timeout: loop_timeout, total_timeout: total_timeout)
44
+ else
45
+ raise ArgumentError, "can't merge with #{other.class}"
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def reset_counter
52
+ @time_left = @total_timeout
53
+ end
54
+
55
+ def reset_timer
56
+ @started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
57
+ end
58
+
59
+ def log_time
60
+ return unless @time_left
61
+ return reset_timer unless @started
62
+ @time_left -= (Process.clock_gettime(Process::CLOCK_MONOTONIC) - @started)
63
+ raise TimeoutError, "Timed out after #{@total_timeout} seconds" if @time_left <= 0
64
+
65
+ reset_timer
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Transcoder
5
+ extend Registry
6
+ end
7
+ end
8
+
9
+ require "httpx/transcoder/body"
10
+ require "httpx/transcoder/form"
11
+ require "httpx/transcoder/json"
12
+ require "httpx/transcoder/chunker"
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module HTTPX::Transcoder
6
+ module Body
7
+ module_function
8
+
9
+ class Encoder
10
+ extend Forwardable
11
+
12
+ def_delegator :@raw, :to_s
13
+
14
+ def initialize(body)
15
+ @raw = body
16
+ end
17
+
18
+ def bytesize
19
+ if @raw.respond_to?(:bytesize)
20
+ @raw.bytesize
21
+ elsif @raw.respond_to?(:to_ary)
22
+ @raw.map(&:bytesize).reduce(0, :+)
23
+ elsif @raw.respond_to?(:size)
24
+ @raw.size || Float::INFINITY
25
+ elsif @raw.respond_to?(:each)
26
+ Float::INFINITY
27
+ else
28
+ raise Error, "cannot determine size of body: #{@raw.inspect}"
29
+ end
30
+ end
31
+
32
+ def content_type
33
+ "application/octet-stream"
34
+ end
35
+
36
+ private
37
+
38
+ def respond_to_missing?(meth, *args)
39
+ @raw.respond_to?(meth, *args) || super
40
+ end
41
+
42
+ def method_missing(meth, *args, &block)
43
+ if @raw.respond_to?(meth)
44
+ @raw.__send__(meth, *args, &block)
45
+ else
46
+ super
47
+ end
48
+ end
49
+ end
50
+
51
+ def encode(body)
52
+ Encoder.new(body)
53
+ end
54
+ end
55
+ register "body", Body
56
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module HTTPX::Transcoder
6
+ module Chunker
7
+ module_function
8
+
9
+ class Encoder
10
+ extend Forwardable
11
+
12
+ def_delegator :@raw, :readpartial
13
+
14
+ CRLF = "\r\n"
15
+
16
+ def initialize(body)
17
+ @raw = body
18
+ end
19
+
20
+ def each
21
+ return enum_for(__method__) unless block_given?
22
+ @raw.each do |chunk|
23
+ yield "#{chunk.bytesize.to_s(16)}#{CRLF}#{chunk}#{CRLF}"
24
+ end
25
+ yield "0#{CRLF}#{CRLF}"
26
+ end
27
+
28
+ def respond_to_missing?(meth, *args)
29
+ @raw.respond_to?(meth, *args) || super
30
+ end
31
+ end
32
+
33
+ def encode(chunks)
34
+ Encoder.new(chunks)
35
+ end
36
+ end
37
+ register "chunker", Chunker
38
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+ require "http/form_data"
5
+
6
+ module HTTPX::Transcoder
7
+ module Form
8
+ module_function
9
+
10
+ class Encoder
11
+ extend Forwardable
12
+
13
+ def_delegator :@raw, :content_type
14
+
15
+ def_delegator :@raw, :to_s
16
+
17
+ def_delegator :@raw, :read
18
+
19
+ def initialize(form)
20
+ @raw = HTTP::FormData.create(form)
21
+ end
22
+
23
+ def bytesize
24
+ @raw.content_length
25
+ end
26
+
27
+ def force_encoding(*args)
28
+ @raw.to_s.force_encoding(*args)
29
+ end
30
+
31
+ def to_str
32
+ @raw.to_s
33
+ end
34
+ end
35
+
36
+ def encode(form)
37
+ Encoder.new(form)
38
+ end
39
+ end
40
+ register "form", Form
41
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+ require "json"
5
+
6
+ module HTTPX::Transcoder
7
+ module JSON
8
+ module_function
9
+
10
+ class Encoder
11
+ extend Forwardable
12
+
13
+ def_delegator :@raw, :to_str
14
+
15
+ def_delegator :@raw, :to_s
16
+
17
+ def_delegator :@raw, :bytesize
18
+
19
+ def_delegator :@raw, :force_encoding
20
+
21
+ def initialize(json)
22
+ @raw = ::JSON.dump(json)
23
+ @charset = @raw.encoding.name.downcase
24
+ end
25
+
26
+ def content_type
27
+ "application/json; charset=#{@charset}"
28
+ end
29
+ end
30
+
31
+ def encode(json)
32
+ Encoder.new(json)
33
+ end
34
+ end
35
+ register "json", JSON
36
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ VERSION = "0.0.1"
5
+ end
metadata ADDED
@@ -0,0 +1,150 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: httpx
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tiago Cardoso
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-02-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: http-2
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.9.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.9.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: http-form_data
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.0
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '3'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.0.0
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '3'
47
+ - !ruby/object:Gem::Dependency
48
+ name: http_parser.rb
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.6.0
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 0.6.0
61
+ - !ruby/object:Gem::Dependency
62
+ name: http-cookie
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1.0'
75
+ description: A client library for making HTTP requests from Ruby.
76
+ email:
77
+ - cardoso_tiago@hotmail.com
78
+ executables: []
79
+ extensions: []
80
+ extra_rdoc_files: []
81
+ files:
82
+ - LICENSE.txt
83
+ - README.md
84
+ - lib/httpx.rb
85
+ - lib/httpx/buffer.rb
86
+ - lib/httpx/callbacks.rb
87
+ - lib/httpx/chainable.rb
88
+ - lib/httpx/channel.rb
89
+ - lib/httpx/channel/http1.rb
90
+ - lib/httpx/channel/http2.rb
91
+ - lib/httpx/client.rb
92
+ - lib/httpx/connection.rb
93
+ - lib/httpx/errors.rb
94
+ - lib/httpx/extensions.rb
95
+ - lib/httpx/headers.rb
96
+ - lib/httpx/io.rb
97
+ - lib/httpx/loggable.rb
98
+ - lib/httpx/options.rb
99
+ - lib/httpx/plugins/authentication.rb
100
+ - lib/httpx/plugins/basic_authentication.rb
101
+ - lib/httpx/plugins/compression.rb
102
+ - lib/httpx/plugins/compression/brotli.rb
103
+ - lib/httpx/plugins/compression/deflate.rb
104
+ - lib/httpx/plugins/compression/gzip.rb
105
+ - lib/httpx/plugins/cookies.rb
106
+ - lib/httpx/plugins/digest_authentication.rb
107
+ - lib/httpx/plugins/follow_redirects.rb
108
+ - lib/httpx/plugins/h2c.rb
109
+ - lib/httpx/plugins/proxy.rb
110
+ - lib/httpx/plugins/proxy/http.rb
111
+ - lib/httpx/plugins/proxy/socks4.rb
112
+ - lib/httpx/plugins/proxy/socks5.rb
113
+ - lib/httpx/plugins/push_promise.rb
114
+ - lib/httpx/plugins/stream.rb
115
+ - lib/httpx/registry.rb
116
+ - lib/httpx/request.rb
117
+ - lib/httpx/response.rb
118
+ - lib/httpx/selector.rb
119
+ - lib/httpx/timeout.rb
120
+ - lib/httpx/transcoder.rb
121
+ - lib/httpx/transcoder/body.rb
122
+ - lib/httpx/transcoder/chunker.rb
123
+ - lib/httpx/transcoder/form.rb
124
+ - lib/httpx/transcoder/json.rb
125
+ - lib/httpx/version.rb
126
+ homepage: https://gitlab.com/honeyryderchuck/httpx
127
+ licenses:
128
+ - Apache 2.0
129
+ metadata: {}
130
+ post_install_message:
131
+ rdoc_options: []
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ required_rubygems_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ requirements: []
145
+ rubyforge_project:
146
+ rubygems_version: 2.7.6
147
+ signing_key:
148
+ specification_version: 4
149
+ summary: HTTPX, to the future, and beyond
150
+ test_files: []