http-protocol 0.1.0

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.
@@ -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