tilia-http 4.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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +35 -0
  5. data/.simplecov +4 -0
  6. data/.travis.yml +3 -0
  7. data/CHANGELOG.sabre.md +235 -0
  8. data/CONTRIBUTING.md +25 -0
  9. data/Gemfile +18 -0
  10. data/Gemfile.lock +69 -0
  11. data/LICENSE +27 -0
  12. data/LICENSE.sabre +27 -0
  13. data/README.md +68 -0
  14. data/Rakefile +17 -0
  15. data/examples/asyncclient.rb +45 -0
  16. data/examples/basicauth.rb +39 -0
  17. data/examples/client.rb +20 -0
  18. data/examples/reverseproxy.rb +39 -0
  19. data/examples/stringify.rb +37 -0
  20. data/lib/tilia/http/auth/abstract_auth.rb +51 -0
  21. data/lib/tilia/http/auth/aws.rb +191 -0
  22. data/lib/tilia/http/auth/basic.rb +43 -0
  23. data/lib/tilia/http/auth/bearer.rb +37 -0
  24. data/lib/tilia/http/auth/digest.rb +187 -0
  25. data/lib/tilia/http/auth.rb +12 -0
  26. data/lib/tilia/http/client.rb +452 -0
  27. data/lib/tilia/http/client_exception.rb +15 -0
  28. data/lib/tilia/http/client_http_exception.rb +37 -0
  29. data/lib/tilia/http/http_exception.rb +21 -0
  30. data/lib/tilia/http/message.rb +241 -0
  31. data/lib/tilia/http/message_decorator_trait.rb +183 -0
  32. data/lib/tilia/http/message_interface.rb +154 -0
  33. data/lib/tilia/http/request.rb +235 -0
  34. data/lib/tilia/http/request_decorator.rb +160 -0
  35. data/lib/tilia/http/request_interface.rb +126 -0
  36. data/lib/tilia/http/response.rb +164 -0
  37. data/lib/tilia/http/response_decorator.rb +58 -0
  38. data/lib/tilia/http/response_interface.rb +36 -0
  39. data/lib/tilia/http/sapi.rb +165 -0
  40. data/lib/tilia/http/url_util.rb +70 -0
  41. data/lib/tilia/http/util.rb +51 -0
  42. data/lib/tilia/http/version.rb +9 -0
  43. data/lib/tilia/http.rb +416 -0
  44. data/test/http/auth/aws_test.rb +189 -0
  45. data/test/http/auth/basic_test.rb +60 -0
  46. data/test/http/auth/bearer_test.rb +47 -0
  47. data/test/http/auth/digest_test.rb +141 -0
  48. data/test/http/client_mock.rb +101 -0
  49. data/test/http/client_test.rb +331 -0
  50. data/test/http/message_decorator_test.rb +67 -0
  51. data/test/http/message_test.rb +163 -0
  52. data/test/http/request_decorator_test.rb +87 -0
  53. data/test/http/request_test.rb +132 -0
  54. data/test/http/response_decorator_test.rb +28 -0
  55. data/test/http/response_test.rb +38 -0
  56. data/test/http/sapi_mock.rb +12 -0
  57. data/test/http/sapi_test.rb +133 -0
  58. data/test/http/url_util_test.rb +155 -0
  59. data/test/http/util_test.rb +186 -0
  60. data/test/http_test.rb +102 -0
  61. data/test/test_helper.rb +6 -0
  62. data/tilia-http.gemspec +18 -0
  63. metadata +192 -0
@@ -0,0 +1,241 @@
1
+ require 'stringio'
2
+ module Tilia
3
+ module Http
4
+ # This is the abstract base class for both the Request and Response objects.
5
+ #
6
+ # This object contains a few simple methods that are shared by both.
7
+ module Message
8
+ include Tilia::Http::MessageInterface
9
+
10
+ protected
11
+
12
+ # Request body
13
+ #
14
+ # This should be a stream resource
15
+ #
16
+ # @return resource
17
+ attr_accessor :body
18
+
19
+ # Contains the list of HTTP headers
20
+ #
21
+ # @return array
22
+ attr_accessor :headers
23
+
24
+ # HTTP message version (1.0 or 1.1)
25
+ #
26
+ # @return [String]
27
+ attr_accessor :http_version
28
+
29
+ public
30
+
31
+ # Returns the body as a readable stream resource.
32
+ #
33
+ # Note that the stream may not be rewindable, and therefore may only be
34
+ # read once.
35
+ #
36
+ # @return resource
37
+ def body_as_stream
38
+ body = self.body
39
+ if body.is_a?(String) || body.nil?
40
+ stream = StringIO.new
41
+ stream.write body
42
+ stream.rewind
43
+ return stream
44
+ end
45
+ body
46
+ end
47
+
48
+ # Returns the body as a string.
49
+ #
50
+ # Note that because the underlying data may be based on a stream, this
51
+ # method could only work correctly the first time.
52
+ #
53
+ # @return [String]
54
+ def body_as_string
55
+ body = self.body
56
+ if body.is_a?(String)
57
+ body
58
+ elsif body.nil?
59
+ ''
60
+ else
61
+ body.readlines.join("\n")
62
+ end
63
+ end
64
+
65
+ # Returns the message body, as it's internal representation.
66
+ #
67
+ # This could be either a string or a stream.
68
+ #
69
+ # @return resource|string
70
+ def body
71
+ @body
72
+ end
73
+
74
+ # Replaces the body resource with a new stream or string.
75
+ #
76
+ # @param resource|string body
77
+ def body=(body)
78
+ @body = body
79
+ end
80
+
81
+ # Returns all the HTTP headers as an array.
82
+ #
83
+ # Every header is returned as an array, with one or more values.
84
+ #
85
+ # @return array
86
+ def headers
87
+ result = {}
88
+ @headers.values.each do |header_info|
89
+ result[header_info[0]] = header_info[1]
90
+ end
91
+ result
92
+ end
93
+
94
+ # Will return true or false, depending on if a HTTP header exists.
95
+ #
96
+ # @param [String] name
97
+ # @return bool
98
+ def header?(name)
99
+ @headers.key? name.downcase
100
+ end
101
+
102
+ # Returns a specific HTTP header, based on it's name.
103
+ #
104
+ # The name must be treated as case-insensitive.
105
+ # If the header does not exist, this method must return null.
106
+ #
107
+ # If a header appeared more than once in a HTTP request, this method will
108
+ # concatenate all the values with a comma.
109
+ #
110
+ # Note that this not make sense for all headers. Some, such as
111
+ # `Set-Cookie` cannot be logically combined with a comma. In those cases
112
+ # you *should* use header_as_array.
113
+ #
114
+ # @param [String] name
115
+ # @return [String, nil]
116
+ def header(name)
117
+ name = name.downcase
118
+
119
+ return @headers[name][1].join(',') if @headers.key?(name)
120
+
121
+ nil
122
+ end
123
+
124
+ # Returns a HTTP header as an array.
125
+ #
126
+ # For every time the HTTP header appeared in the request or response, an
127
+ # item will appear in the array.
128
+ #
129
+ # If the header did not exists, this method will return an empty array.
130
+ #
131
+ # @param [String] name
132
+ # @return [String][]
133
+ def header_as_array(name)
134
+ name = name.downcase
135
+
136
+ return @headers[name][1] if @headers.key?(name)
137
+
138
+ []
139
+ end
140
+
141
+ # Updates a HTTP header.
142
+ #
143
+ # The case-sensitity of the name value must be retained as-is.
144
+ #
145
+ # If the header already existed, it will be overwritten.
146
+ #
147
+ # @param [String] name
148
+ # @param [String, Array<String>] value
149
+ # @return [void]
150
+ def update_header(name, value)
151
+ value = [value] unless value.is_a?(Array)
152
+ @headers[name.downcase] = [name, value]
153
+ end
154
+
155
+ # Sets a new set of HTTP headers.
156
+ #
157
+ # The headers array should contain headernames for keys, and their value
158
+ # should be specified as either a string or an array.
159
+ #
160
+ # Any header that already existed will be overwritten.
161
+ #
162
+ # @param array headers
163
+ # @return [void]
164
+ def update_headers(headers)
165
+ headers.each do |name, value|
166
+ update_header(name, value)
167
+ end
168
+ end
169
+
170
+ # Adds a HTTP header.
171
+ #
172
+ # This method will not overwrite any existing HTTP header, but instead add
173
+ # another value. Individual values can be retrieved with
174
+ # getHeadersAsArray.
175
+ #
176
+ # @param [String] name
177
+ # @param [String] value
178
+ # @return [void]
179
+ def add_header(name, value)
180
+ l_name = name.downcase
181
+ value = [value] unless value.is_a?(Array)
182
+
183
+ if @headers.key?(l_name)
184
+ @headers[l_name][1].concat value
185
+ else
186
+ @headers[l_name] = [name, value]
187
+ end
188
+ end
189
+
190
+ # Adds a new set of HTTP headers.
191
+ #
192
+ # Any existing headers will not be overwritten.
193
+ #
194
+ # @param array headers
195
+ # @return [void]
196
+ def add_headers(headers)
197
+ headers.each do |name, value|
198
+ add_header(name, value)
199
+ end
200
+ end
201
+
202
+ # Removes a HTTP header.
203
+ #
204
+ # The specified header name must be treated as case-insenstive.
205
+ # This method should return true if the header was successfully deleted,
206
+ # and false if the header did not exist.
207
+ #
208
+ # @return bool
209
+ def remove_header(name)
210
+ name = name.downcase
211
+ return false unless @headers.key?(name)
212
+ @headers.delete name
213
+ true
214
+ end
215
+
216
+ # Sets the HTTP version.
217
+ #
218
+ # Should be 1.0 or 1.1.
219
+ #
220
+ # @param [String] version
221
+ # @return [void]
222
+ def http_version=(version)
223
+ @http_version = version
224
+ end
225
+
226
+ # Returns the HTTP version.
227
+ #
228
+ # @return [String]
229
+ def http_version
230
+ @http_version
231
+ end
232
+
233
+ # TODO: document
234
+ def initialize_message
235
+ @body = nil
236
+ @headers = {}
237
+ @http_version = '1.1'
238
+ end
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,183 @@
1
+ module Tilia
2
+ module Http
3
+ # This trait contains a bunch of methods, shared by both the RequestDecorator
4
+ # and the ResponseDecorator.
5
+ #
6
+ # Didn't seem needed to create a full class for this, so we're just
7
+ # implementing it as a trait.
8
+ module MessageDecoratorTrait
9
+ protected
10
+
11
+ # The inner request object.
12
+ #
13
+ # All method calls will be forwarded here.
14
+ #
15
+ # @return MessageInterface
16
+ attr_accessor :inner
17
+
18
+ public
19
+
20
+ # Returns the body as a readable stream resource.
21
+ #
22
+ # Note that the stream may not be rewindable, and therefore may only be
23
+ # read once.
24
+ #
25
+ # @return resource
26
+ def body_as_stream
27
+ @inner.body_as_stream
28
+ end
29
+
30
+ # Returns the body as a string.
31
+ #
32
+ # Note that because the underlying data may be based on a stream, this
33
+ # method could only work correctly the first time.
34
+ #
35
+ # @return [String]
36
+ def body_as_string
37
+ @inner.body_as_string
38
+ end
39
+
40
+ # Returns the message body, as it's internal representation.
41
+ #
42
+ # This could be either a string or a stream.
43
+ #
44
+ # @return resource|string
45
+ def body
46
+ @inner.body
47
+ end
48
+
49
+ # Updates the body resource with a new stream.
50
+ #
51
+ # @param resource body
52
+ # @return [void]
53
+ def body=(body)
54
+ @inner.body = body
55
+ end
56
+
57
+ # Returns all the HTTP headers as an array.
58
+ #
59
+ # Every header is returned as an array, with one or more values.
60
+ #
61
+ # @return array
62
+ def headers
63
+ @inner.headers
64
+ end
65
+
66
+ # Will return true or false, depending on if a HTTP header exists.
67
+ #
68
+ # @param [String] name
69
+ # @return bool
70
+ def header?(name)
71
+ @inner.header?(name)
72
+ end
73
+
74
+ # Returns a specific HTTP header, based on it's name.
75
+ #
76
+ # The name must be treated as case-insensitive.
77
+ # If the header does not exist, this method must return null.
78
+ #
79
+ # If a header appeared more than once in a HTTP request, this method will
80
+ # concatenate all the values with a comma.
81
+ #
82
+ # Note that this not make sense for all headers. Some, such as
83
+ # `Set-Cookie` cannot be logically combined with a comma. In those cases
84
+ # you *should* use header_as_array.
85
+ #
86
+ # @param [String] name
87
+ # @return [String, nil]
88
+ def header(name)
89
+ @inner.header(name)
90
+ end
91
+
92
+ # Returns a HTTP header as an array.
93
+ #
94
+ # For every time the HTTP header appeared in the request or response, an
95
+ # item will appear in the array.
96
+ #
97
+ # If the header did not exists, this method will return an empty array.
98
+ #
99
+ # @param [String] name
100
+ # @return [String][]
101
+ def header_as_array(name)
102
+ @inner.header_as_array(name)
103
+ end
104
+
105
+ # Updates a HTTP header.
106
+ #
107
+ # The case-sensitity of the name value must be retained as-is.
108
+ #
109
+ # If the header already existed, it will be overwritten.
110
+ #
111
+ # @param [String] name
112
+ # @param [String, Array<String>] value
113
+ # @return [void]
114
+ def update_header(name, value)
115
+ @inner.update_header(name, value)
116
+ end
117
+
118
+ # Sets a new set of HTTP headers.
119
+ #
120
+ # The headers array should contain headernames for keys, and their value
121
+ # should be specified as either a string or an array.
122
+ #
123
+ # Any header that already existed will be overwritten.
124
+ #
125
+ # @param array headers
126
+ # @return [void]
127
+ def update_headers(headers)
128
+ @inner.update_headers(headers)
129
+ end
130
+
131
+ # Adds a HTTP header.
132
+ #
133
+ # This method will not overwrite any existing HTTP header, but instead add
134
+ # another value. Individual values can be retrieved with
135
+ # getHeadersAsArray.
136
+ #
137
+ # @param [String] name
138
+ # @param [String] value
139
+ # @return [void]
140
+ def add_header(name, value)
141
+ @inner.add_header(name, value)
142
+ end
143
+
144
+ # Adds a new set of HTTP headers.
145
+ #
146
+ # Any existing headers will not be overwritten.
147
+ #
148
+ # @param array headers
149
+ # @return [void]
150
+ def add_headers(headers)
151
+ @inner.add_headers(headers)
152
+ end
153
+
154
+ # Removes a HTTP header.
155
+ #
156
+ # The specified header name must be treated as case-insenstive.
157
+ # This method should return true if the header was successfully deleted,
158
+ # and false if the header did not exist.
159
+ #
160
+ # @return bool
161
+ def remove_header(name)
162
+ @inner.remove_header(name)
163
+ end
164
+
165
+ # Sets the HTTP version.
166
+ #
167
+ # Should be 1.0 or 1.1.
168
+ #
169
+ # @param [String] version
170
+ # @return [void]
171
+ def http_version=(version)
172
+ @inner.http_version = version
173
+ end
174
+
175
+ # Returns the HTTP version.
176
+ #
177
+ # @return [String]
178
+ def http_version
179
+ @inner.http_version
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,154 @@
1
+ module Tilia
2
+ module Http
3
+ # The MessageInterface is the base interface that's used by both
4
+ # the RequestInterface and ResponseInterface.
5
+ module MessageInterface
6
+ # Returns the body as a readable stream resource.
7
+ #
8
+ # Note that the stream may not be rewindable, and therefore may only be
9
+ # read once.
10
+ #
11
+ # @return resource
12
+ def body_as_stream
13
+ end
14
+
15
+ # Returns the body as a string.
16
+ #
17
+ # Note that because the underlying data may be based on a stream, this
18
+ # method could only work correctly the first time.
19
+ #
20
+ # @return [String]
21
+ def body_as_string
22
+ end
23
+
24
+ # Returns the message body, as it's internal representation.
25
+ #
26
+ # This could be either a string or a stream.
27
+ #
28
+ # @return resource|string
29
+ def body
30
+ end
31
+
32
+ # Updates the body resource with a new stream.
33
+ #
34
+ # @param resource body
35
+ # @return [void]
36
+ def body=(_body)
37
+ end
38
+
39
+ # Returns all the HTTP headers as an array.
40
+ #
41
+ # Every header is returned as an array, with one or more values.
42
+ #
43
+ # @return array
44
+ def headers
45
+ end
46
+
47
+ # Will return true or false, depending on if a HTTP header exists.
48
+ #
49
+ # @param [String] name
50
+ # @return bool
51
+ def header?(_name)
52
+ end
53
+
54
+ # Returns a specific HTTP header, based on it's name.
55
+ #
56
+ # The name must be treated as case-insensitive.
57
+ # If the header does not exist, this method must return null.
58
+ #
59
+ # If a header appeared more than once in a HTTP request, this method will
60
+ # concatenate all the values with a comma.
61
+ #
62
+ # Note that this not make sense for all headers. Some, such as
63
+ # `Set-Cookie` cannot be logically combined with a comma. In those cases
64
+ # you *should* use header_as_array.
65
+ #
66
+ # @param [String] name
67
+ # @return [String, nil]
68
+ def header(_name)
69
+ end
70
+
71
+ # Returns a HTTP header as an array.
72
+ #
73
+ # For every time the HTTP header appeared in the request or response, an
74
+ # item will appear in the array.
75
+ #
76
+ # If the header did not exists, this method will return an empty array.
77
+ #
78
+ # @param [String] name
79
+ # @return [String][]
80
+ def header_as_array(_name)
81
+ end
82
+
83
+ # Updates a HTTP header.
84
+ #
85
+ # The case-sensitity of the name value must be retained as-is.
86
+ #
87
+ # If the header already existed, it will be overwritten.
88
+ #
89
+ # @param [String] name
90
+ # @param [String, Array<String>] value
91
+ # @return [void]
92
+ def update_header(_name, _value)
93
+ end
94
+
95
+ # Sets a new set of HTTP headers.
96
+ #
97
+ # The headers array should contain headernames for keys, and their value
98
+ # should be specified as either a string or an array.
99
+ #
100
+ # Any header that already existed will be overwritten.
101
+ #
102
+ # @param array headers
103
+ # @return [void]
104
+ def update_headers(_headers)
105
+ end
106
+
107
+ # Adds a HTTP header.
108
+ #
109
+ # This method will not overwrite any existing HTTP header, but instead add
110
+ # another value. Individual values can be retrieved with
111
+ # getHeadersAsArray.
112
+ #
113
+ # @param [String] name
114
+ # @param [String] value
115
+ # @return [void]
116
+ def add_header(_name, _value)
117
+ end
118
+
119
+ # Adds a new set of HTTP headers.
120
+ #
121
+ # Any existing headers will not be overwritten.
122
+ #
123
+ # @param array headers
124
+ # @return [void]
125
+ def add_headers(_headers)
126
+ end
127
+
128
+ # Removes a HTTP header.
129
+ #
130
+ # The specified header name must be treated as case-insenstive.
131
+ # This method should return true if the header was successfully deleted,
132
+ # and false if the header did not exist.
133
+ #
134
+ # @return bool
135
+ def remove_header(_name)
136
+ end
137
+
138
+ # Sets the HTTP version.
139
+ #
140
+ # Should be 1.0 or 1.1.
141
+ #
142
+ # @param [String] version
143
+ # @return [void]
144
+ def http_version=(_version)
145
+ end
146
+
147
+ # Returns the HTTP version.
148
+ #
149
+ # @return [String]
150
+ def http_version
151
+ end
152
+ end
153
+ end
154
+ end