tilia-http 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
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