http-protocol 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a248a6b8419639fb567474da1539e38772b452b706d21ef12c52c02adb60352d
4
+ data.tar.gz: 302d1563c4c6516d88d2487ef63eeac1fa9277739194d8bf60716b0a40e52f9b
5
+ SHA512:
6
+ metadata.gz: a69be0f9448ab54d044326705a3fe70b7b37fc673aef27d04e4c8ac3d2669374a1e4ad25a3385ef227ce2884c7ce80a27bf2b86f140735479e1df48b2cb3bbb9
7
+ data.tar.gz: 7d09ffea378ea59cd1e9465669226f6e8b40f1e8e16530a74ec77050aeb159cecf60f4dd9d65c36b059b5405a7cc7c12850e14c5dc428169a542914e059a4203
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --warnings
3
+ --require spec_helper
@@ -0,0 +1,23 @@
1
+ language: ruby
2
+ sudo: required
3
+ dist: xenial
4
+ cache: bundler
5
+
6
+ before_script:
7
+ - gem update --system
8
+ - gem install bundler
9
+
10
+ matrix:
11
+ include:
12
+ - rvm: 2.3
13
+ - rvm: 2.4
14
+ - rvm: 2.5
15
+ - rvm: 2.6
16
+ - rvm: jruby-head
17
+ env: JRUBY_OPTS="--debug -X+O"
18
+ - rvm: ruby-head
19
+ - rvm: rbx-3
20
+ allow_failures:
21
+ - rvm: ruby-head
22
+ - rvm: jruby-head
23
+ - rvm: rbx-3
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in http-protocol.gemspec
6
+ gemspec
7
+
8
+ group :test do
9
+ gem 'pry'
10
+ gem 'covered', require: 'covered/rspec' if RUBY_VERSION >= "2.6.0"
11
+ end
@@ -0,0 +1,64 @@
1
+ # HTTP::Protocol
2
+
3
+ Provides abstractions for working with the HTTP protocol with a focus on on HTTP/2.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'http-protocol'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install http-protocol
20
+
21
+ ## Usage
22
+
23
+ ### HTTP2
24
+
25
+ ```ruby
26
+ framer = HTTP::Protocol::HTTP2::Framer.new(io)
27
+
28
+ frame = framer.read_frame
29
+
30
+ frame.write(io)
31
+ ```
32
+
33
+ ## Contributing
34
+
35
+ 1. Fork it
36
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
37
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
38
+ 4. Push to the branch (`git push origin my-new-feature`)
39
+ 5. Create new Pull Request
40
+
41
+ ## License
42
+
43
+ Released under the MIT license.
44
+
45
+ Copyright, 2018, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
46
+ Copyright, 2013, by Ilya Grigorik.
47
+
48
+ Permission is hereby granted, free of charge, to any person obtaining a copy
49
+ of this software and associated documentation files (the "Software"), to deal
50
+ in the Software without restriction, including without limitation the rights
51
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
52
+ copies of the Software, and to permit persons to whom the Software is
53
+ furnished to do so, subject to the following conditions:
54
+
55
+ The above copyright notice and this permission notice shall be included in
56
+ all copies or substantial portions of the Software.
57
+
58
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
61
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
62
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
63
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
64
+ THE SOFTWARE.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,24 @@
1
+
2
+ require_relative "lib/http/protocol/version"
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "http-protocol"
6
+ spec.version = HTTP::Protocol::VERSION
7
+ spec.authors = ["Samuel Williams"]
8
+ spec.email = ["samuel.williams@oriontransfer.co.nz"]
9
+
10
+ spec.summary = "Provides abstractions to handle HTTP1 and HTTP2 protocols."
11
+ spec.homepage = "https://github.com/socketry/http-protocol"
12
+
13
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
14
+ f.match(%r{^(test|spec|features)/})
15
+ end
16
+
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_dependency "http-hpack", "~> 0.1.0"
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.16"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.0"
24
+ end
@@ -0,0 +1,21 @@
1
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require "http/protocol/version"
@@ -0,0 +1,80 @@
1
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ # Copyright, 2013, by Ilya Grigorik.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+
22
+ module HTTP
23
+ module Protocol
24
+ class Error < StandardError
25
+ end
26
+
27
+ # Raised if connection header is missing or invalid indicating that
28
+ # this is an invalid HTTP 2.0 request - no frames are emitted and the
29
+ # connection must be aborted.
30
+ class HandshakeError < Error
31
+ end
32
+
33
+ # Raised by stream or connection handlers, results in GOAWAY frame
34
+ # which signals termination of the current connection. You *cannot*
35
+ # recover from this exception, or any exceptions subclassed from it.
36
+ class ProtocolError < Error
37
+ def initialize(message, code = nil)
38
+ super(message)
39
+
40
+ @code = code
41
+ end
42
+
43
+ attr :code
44
+ end
45
+
46
+ # When the frame payload does not match expectations.
47
+ #
48
+ # @see ProtocolError
49
+ class FrameSizeError < ProtocolError
50
+ end
51
+
52
+ # Raised on invalid flow control frame or command.
53
+ #
54
+ # @see ProtocolError
55
+ class FlowControlError < ProtocolError
56
+ end
57
+
58
+ # Raised on invalid stream processing: invalid frame type received or
59
+ # sent, or invalid command issued.
60
+ class InternalError < ProtocolError
61
+ end
62
+
63
+ #
64
+ # -- Recoverable errors -------------------------------------------------
65
+ #
66
+
67
+ # Raised if stream has been closed and new frames cannot be sent.
68
+ class StreamClosed < Error
69
+ end
70
+
71
+ # Raised if connection has been closed (or draining) and new stream
72
+ # cannot be opened.
73
+ class ConnectionClosed < Error
74
+ end
75
+
76
+ # Raised if stream limit has been reached and new stream cannot be opened.
77
+ class StreamLimitExceeded < Error
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,203 @@
1
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module HTTP
22
+ module Protocol
23
+ class Headers
24
+ class Split < Array
25
+ COMMA = /\s*,\s*/
26
+
27
+ def initialize(value)
28
+ super(value.split(COMMA))
29
+ end
30
+
31
+ def << value
32
+ super value.split(COMMA)
33
+ end
34
+
35
+ def to_s
36
+ join(", ")
37
+ end
38
+ end
39
+
40
+ class Multiple < Array
41
+ def initialize(value)
42
+ super()
43
+
44
+ self << value
45
+ end
46
+
47
+ def to_s
48
+ join("\n")
49
+ end
50
+ end
51
+
52
+ def self.[] hash
53
+ self.new(hash.to_a)
54
+ end
55
+
56
+ def initialize(fields = [])
57
+ @fields = fields
58
+ @indexed = to_h
59
+ end
60
+
61
+ attr :fields
62
+
63
+ def freeze
64
+ return if frozen?
65
+
66
+ @indexed = to_h
67
+
68
+ super
69
+ end
70
+
71
+ def empty?
72
+ @fields.empty?
73
+ end
74
+
75
+ def each(&block)
76
+ @fields.each(&block)
77
+ end
78
+
79
+ def include? key
80
+ self[key] != nil
81
+ end
82
+
83
+ # Delete all headers with the given key, and return the value of the last one, if any.
84
+ def delete(key)
85
+ values, @fields = @fields.partition do |field|
86
+ field.first.downcase == key
87
+ end
88
+
89
+ if @indexed
90
+ @indexed.delete(key)
91
+ end
92
+
93
+ if field = values.last
94
+ return field.last
95
+ end
96
+ end
97
+
98
+ def slice!(keys)
99
+ values, @fields = @fields.partition do |field|
100
+ keys.include?(field.first.downcase)
101
+ end
102
+
103
+ if @indexed
104
+ keys.each do |key|
105
+ @indexed.delete(key)
106
+ end
107
+ end
108
+ end
109
+
110
+ def add(key, value)
111
+ self[key] = value
112
+ end
113
+
114
+ def []= key, value
115
+ @fields << [key, value]
116
+
117
+ if @indexed
118
+ # It would be good to do some kind of validation here.
119
+ merge(@indexed, key.downcase, value)
120
+ end
121
+ end
122
+
123
+ MERGE_POLICY = {
124
+ # Headers which may only be specified once.
125
+ 'content-type' => false,
126
+ 'content-disposition' => false,
127
+ 'content-length' => false,
128
+ 'user-agent' => false,
129
+ 'referer' => false,
130
+ 'host' => false,
131
+ 'authorization' => false,
132
+ 'proxy-authorization' => false,
133
+ 'if-modified-since' => false,
134
+ 'if-unmodified-since' => false,
135
+ 'from' => false,
136
+ 'location' => false,
137
+ 'max-forwards' => false,
138
+
139
+ 'connection' => Split,
140
+
141
+ # Headers specifically for proxies:
142
+ 'via' => Split,
143
+ 'x-forwarded-for' => Split,
144
+
145
+ # Headers which may be specified multiple times, but which can't be concatenated.
146
+ 'set-cookie' => Multiple,
147
+ 'www-authenticate' => Multiple,
148
+ 'proxy-authenticate' => Multiple
149
+ }.tap{|hash| hash.default = Split}
150
+
151
+ def merge(hash, key, value)
152
+ if policy = MERGE_POLICY[key]
153
+ if current_value = hash[key]
154
+ current_value << value
155
+ else
156
+ hash[key] = policy.new(value)
157
+ end
158
+ else
159
+ raise ArgumentError, "Header #{key} can only be set once!" if hash.include?(key)
160
+
161
+ # We can't merge these, we only expose the last one set.
162
+ hash[key] = value
163
+ end
164
+ end
165
+
166
+ def [] key
167
+ @indexed ||= to_h
168
+
169
+ @indexed[key]
170
+ end
171
+
172
+ def to_h
173
+ @fields.inject({}) do |hash, (key, value)|
174
+ merge(hash, key.downcase, value)
175
+
176
+ hash
177
+ end
178
+ end
179
+
180
+ def == other
181
+ if other.is_a? Hash
182
+ to_h == other
183
+ else
184
+ @fields == other.fields
185
+ end
186
+ end
187
+
188
+ class Merged
189
+ def initialize(*all)
190
+ @all = all
191
+ end
192
+
193
+ def each(&block)
194
+ @all.each do |headers|
195
+ headers.each do |key, value|
196
+ yield key, value.to_s
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end