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,235 @@
1
+ require 'cgi'
2
+ module Tilia
3
+ module Http
4
+ # The Request class represents a single HTTP request.
5
+ #
6
+ # You can either simply construct the object from scratch, or if you need
7
+ # access to the current HTTP request, use Sapi::getRequest.
8
+ class Request
9
+ include Tilia::Http::Message
10
+ include Tilia::Http::RequestInterface
11
+
12
+ protected
13
+
14
+ # HTTP Method
15
+ #
16
+ # @return [String]
17
+ attr_accessor :method
18
+
19
+ # Request Url
20
+ #
21
+ # @return [String]
22
+ attr_accessor :url
23
+
24
+ public
25
+
26
+ # Creates the request object
27
+ #
28
+ # @param [String] method
29
+ # @param [String] url
30
+ # @param array headers
31
+ # @param resource body
32
+ def initialize(method = nil, url = nil, headers = nil, body = nil)
33
+ initialize_message
34
+ @base_url = '/' # RUBY
35
+ @post_data = {}
36
+ @raw_server_data = {}
37
+
38
+ fail ArgumentError, 'The first argument for this constructor should be a string or null, not an array. Did you upgrade from sabre/http 1.0 to 2.0?' if method.is_a?(Array)
39
+
40
+ self.method = method if method
41
+ self.url = url if url
42
+ update_headers(headers) if headers
43
+ self.body = body if body
44
+ end
45
+
46
+ # Returns the current HTTP method
47
+ #
48
+ # @return [String]
49
+ attr_reader :method
50
+
51
+ # Sets the HTTP method
52
+ #
53
+ # @param [String] method
54
+ # @return [void]
55
+ attr_writer :method
56
+
57
+ # Returns the request url.
58
+ #
59
+ # @return [String]
60
+ attr_reader :url
61
+
62
+ # Sets the request url.
63
+ #
64
+ # @param [String] url
65
+ # @return [void]
66
+ attr_writer :url
67
+
68
+ # Returns the list of query parameters.
69
+ #
70
+ # This is equivalent to PHP's $_GET superglobal.
71
+ #
72
+ # @return array
73
+ def query_parameters
74
+ url = self.url
75
+
76
+ if !(index = url.index('?'))
77
+ {}
78
+ else
79
+ query_params = CGI.parse(url[index + 1..-1])
80
+ query_params.keys.each do |key|
81
+ query_params[key] = query_params[key][0] if query_params[key].size == 1
82
+ query_params[key] = nil if query_params[key].size == 0
83
+ end
84
+ query_params
85
+ end
86
+ end
87
+
88
+ # Sets the absolute url.
89
+ #
90
+ # @param [String] url
91
+ # @return [void]
92
+ attr_writer :absolute_url
93
+
94
+ # Returns the absolute url.
95
+ #
96
+ # @return [String]
97
+ attr_reader :absolute_url
98
+
99
+ protected
100
+
101
+ # Base url
102
+ #
103
+ # @return [String]
104
+ attr_accessor :base_url
105
+
106
+ public
107
+
108
+ # Sets a base url.
109
+ #
110
+ # This url is used for relative path calculations.
111
+ #
112
+ # @param [String] url
113
+ # @return [void]
114
+ attr_writer :base_url
115
+
116
+ # Returns the current base url.
117
+ #
118
+ # @return [String]
119
+ attr_reader :base_url
120
+
121
+ # Returns the relative path.
122
+ #
123
+ # This is being calculated using the base url. This path will not start
124
+ # with a slash, so it will always return something like
125
+ # 'example/path.html'.
126
+ #
127
+ # If the full path is equal to the base url, this method will return an
128
+ # empty string.
129
+ #
130
+ # This method will also urldecode the path, and if the url was incoded as
131
+ # ISO-8859-1, it will convert it to UTF-8.
132
+ #
133
+ # If the path is outside of the base url, a LogicException will be thrown.
134
+ #
135
+ # @return [String]
136
+ def path
137
+ # Removing duplicated slashes.
138
+ uri = url.gsub('//', '/')
139
+
140
+ uri = Tilia::Uri.normalize(uri)
141
+ base_uri = Tilia::Uri.normalize(base_url)
142
+
143
+ if uri.index(base_uri) == 0
144
+ # We're not interested in the query part (everything after the ?).
145
+ uri = uri.split('?').first
146
+ return Tilia::Http::UrlUtil.decode_path(uri[base_uri.size..-1]).gsub(%r{^/+|/+$}, '')
147
+ elsif uri + '/' == base_uri
148
+ # A special case, if the baseUri was accessed without a trailing
149
+ # slash, we'll accept it as well.
150
+ return ''
151
+ end
152
+
153
+ fail "Requested uri (#{url}) is out of base uri (#{base_url})"
154
+ end
155
+
156
+ protected
157
+
158
+ # Equivalent of PHP's $_POST.
159
+ #
160
+ # @return array
161
+ attr_accessor :post_data
162
+
163
+ public
164
+
165
+ # Sets the post data.
166
+ #
167
+ # This is equivalent to PHP's $_POST superglobal.
168
+ #
169
+ # This would not have been needed, if POST data was accessible as
170
+ # php://input, but unfortunately we need to special case it.
171
+ #
172
+ # @param array post_data
173
+ # @return [void]
174
+ attr_writer :post_data
175
+
176
+ # Returns the POST data.
177
+ #
178
+ # This is equivalent to PHP's $_POST superglobal.
179
+ #
180
+ # @return array
181
+ attr_reader :post_data
182
+
183
+ protected
184
+
185
+ # An array containing the raw _SERVER array.
186
+ #
187
+ # @return array
188
+ attr_accessor :raw_server_data
189
+
190
+ public
191
+
192
+ # Returns an item from the _SERVER array.
193
+ #
194
+ # If the value does not exist in the array, null is returned.
195
+ #
196
+ # @param [String] value_name
197
+ # @return [String, nil]
198
+ def raw_server_value(value_name)
199
+ @raw_server_data[value_name]
200
+ end
201
+
202
+ # Sets the _SERVER array.
203
+ #
204
+ # @param array data
205
+ # @return [void]
206
+ def raw_server_data=(data)
207
+ @raw_server_data = data.dup
208
+ end
209
+
210
+ # Serializes the request object as a string.
211
+ #
212
+ # This is useful for debugging purposes.
213
+ #
214
+ # @return [String]
215
+ def to_s
216
+ out = "#{method} #{url} HTTP/#{http_version}\r\n"
217
+
218
+ headers.each do |key, value|
219
+ value.each do |v|
220
+ if key == 'Authorization'
221
+ v = v.split(' ').first
222
+ v << ' REDACTED'
223
+ end
224
+ out << "#{key}: #{v}\r\n"
225
+ end
226
+ end
227
+
228
+ out << "\r\n"
229
+ out << body_as_string
230
+
231
+ out
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,160 @@
1
+ module Tilia
2
+ module Http
3
+ # Request Decorator
4
+ #
5
+ # This helper class allows you to easily create decorators for the Request
6
+ # object.
7
+ class RequestDecorator
8
+ include Tilia::Http::RequestInterface
9
+ include Tilia::Http::MessageDecoratorTrait
10
+
11
+ # Constructor.
12
+ #
13
+ # @param RequestInterface inner
14
+ def initialize(inner)
15
+ @inner = inner
16
+ end
17
+
18
+ # Returns the current HTTP method
19
+ #
20
+ # @return [String]
21
+ def method
22
+ @inner.method
23
+ end
24
+
25
+ # Sets the HTTP method
26
+ #
27
+ # @param [String] method
28
+ # @return [void]
29
+ def method=(method)
30
+ @inner.method = method
31
+ end
32
+
33
+ # Returns the request url.
34
+ #
35
+ # @return [String]
36
+ def url
37
+ @inner.url
38
+ end
39
+
40
+ # Sets the request url.
41
+ #
42
+ # @param [String] url
43
+ # @return [void]
44
+ def url=(url)
45
+ @inner.url = url
46
+ end
47
+
48
+ # Returns the absolute url.
49
+ #
50
+ # @return [String]
51
+ def absolute_url
52
+ @inner.absolute_url
53
+ end
54
+
55
+ # Sets the absolute url.
56
+ #
57
+ # @param [String] url
58
+ # @return [void]
59
+ def absolute_url=(url)
60
+ @inner.absolute_url = url
61
+ end
62
+
63
+ # Returns the current base url.
64
+ #
65
+ # @return [String]
66
+ def base_url
67
+ @inner.base_url
68
+ end
69
+
70
+ # Sets a base url.
71
+ #
72
+ # This url is used for relative path calculations.
73
+ #
74
+ # The base url should default to /
75
+ #
76
+ # @param [String] url
77
+ # @return [void]
78
+ def base_url=(url)
79
+ @inner.base_url = url
80
+ end
81
+
82
+ # Returns the relative path.
83
+ #
84
+ # This is being calculated using the base url. This path will not start
85
+ # with a slash, so it will always return something like
86
+ # 'example/path.html'.
87
+ #
88
+ # If the full path is equal to the base url, this method will return an
89
+ # empty string.
90
+ #
91
+ # This method will also urldecode the path, and if the url was incoded as
92
+ # ISO-8859-1, it will convert it to UTF-8.
93
+ #
94
+ # If the path is outside of the base url, a LogicException will be thrown.
95
+ #
96
+ # @return [String]
97
+ def path
98
+ @inner.path
99
+ end
100
+
101
+ # Returns the list of query parameters.
102
+ #
103
+ # This is equivalent to PHP's $_GET superglobal.
104
+ #
105
+ # @return array
106
+ def query_parameters
107
+ @inner.query_parameters
108
+ end
109
+
110
+ # Returns the POST data.
111
+ #
112
+ # This is equivalent to PHP's $_POST superglobal.
113
+ #
114
+ # @return array
115
+ def post_data
116
+ @inner.post_data
117
+ end
118
+
119
+ # Sets the post data.
120
+ #
121
+ # This is equivalent to PHP's $_POST superglobal.
122
+ #
123
+ # This would not have been needed, if POST data was accessible as
124
+ # php://input, but unfortunately we need to special case it.
125
+ #
126
+ # @param array post_data
127
+ # @return [void]
128
+ def post_data=(post_data)
129
+ @inner.post_data = post_data
130
+ end
131
+
132
+ # Returns an item from the _SERVER array.
133
+ #
134
+ # If the value does not exist in the array, null is returned.
135
+ #
136
+ # @param [String] value_name
137
+ # @return [String, nil]
138
+ def raw_server_value(value_name)
139
+ @inner.raw_server_value(value_name)
140
+ end
141
+
142
+ # Sets the _SERVER array.
143
+ #
144
+ # @param array data
145
+ # @return [void]
146
+ def raw_server_data=(data)
147
+ @inner.raw_server_data = data
148
+ end
149
+
150
+ # Serializes the request object as a string.
151
+ #
152
+ # This is useful for debugging purposes.
153
+ #
154
+ # @return [String]
155
+ def to_s
156
+ @inner.to_s
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,126 @@
1
+ module Tilia
2
+ module Http
3
+ # The RequestInterface represents a HTTP request.
4
+ module RequestInterface
5
+ include Tilia::Http::MessageInterface
6
+
7
+ # Returns the current HTTP method
8
+ #
9
+ # @return [String]
10
+ def method
11
+ end
12
+
13
+ # Sets the HTTP method
14
+ #
15
+ # @param [String] method
16
+ # @return [void]
17
+ def method=(_method)
18
+ end
19
+
20
+ # Returns the request url.
21
+ #
22
+ # @return [String]
23
+ def url
24
+ end
25
+
26
+ # Sets the request url.
27
+ #
28
+ # @param [String] url
29
+ # @return [void]
30
+ def url=(_url)
31
+ end
32
+
33
+ # Returns the absolute url.
34
+ #
35
+ # @return [String]
36
+ def absolute_url
37
+ end
38
+
39
+ # Sets the absolute url.
40
+ #
41
+ # @param [String] url
42
+ # @return [void]
43
+ def absolute_url=(_url)
44
+ end
45
+
46
+ # Returns the current base url.
47
+ #
48
+ # @return [String]
49
+ def base_url
50
+ end
51
+
52
+ # Sets a base url.
53
+ #
54
+ # This url is used for relative path calculations.
55
+ #
56
+ # The base url should default to /
57
+ #
58
+ # @param [String] url
59
+ # @return [void]
60
+ def base_url=(_url)
61
+ end
62
+
63
+ # Returns the relative path.
64
+ #
65
+ # This is being calculated using the base url. This path will not start
66
+ # with a slash, so it will always return something like
67
+ # 'example/path.html'.
68
+ #
69
+ # If the full path is equal to the base url, this method will return an
70
+ # empty string.
71
+ #
72
+ # This method will also urldecode the path, and if the url was incoded as
73
+ # ISO-8859-1, it will convert it to UTF-8.
74
+ #
75
+ # If the path is outside of the base url, a LogicException will be thrown.
76
+ #
77
+ # @return [String]
78
+ def path
79
+ end
80
+
81
+ # Returns the list of query parameters.
82
+ #
83
+ # This is equivalent to PHP's $_GET superglobal.
84
+ #
85
+ # @return array
86
+ def query_parameters
87
+ end
88
+
89
+ # Returns the POST data.
90
+ #
91
+ # This is equivalent to PHP's $_POST superglobal.
92
+ #
93
+ # @return array
94
+ def post_data
95
+ end
96
+
97
+ # Sets the post data.
98
+ #
99
+ # This is equivalent to PHP's $_POST superglobal.
100
+ #
101
+ # This would not have been needed, if POST data was accessible as
102
+ # php://input, but unfortunately we need to special case it.
103
+ #
104
+ # @param array post_data
105
+ # @return [void]
106
+ def post_data=(_post_data)
107
+ end
108
+
109
+ # Returns an item from the _SERVER array.
110
+ #
111
+ # If the value does not exist in the array, null is returned.
112
+ #
113
+ # @param [String] value_name
114
+ # @return [String, nil]
115
+ def raw_server_value(_value_name)
116
+ end
117
+
118
+ # Sets the _SERVER array.
119
+ #
120
+ # @param array data
121
+ # @return [void]
122
+ def raw_server_data=(_data)
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,164 @@
1
+ module Tilia
2
+ module Http
3
+ # This class represents a single HTTP response.
4
+ class Response
5
+ include Tilia::Http::Message
6
+ include Tilia::Http::ResponseInterface
7
+
8
+ # This is the list of currently registered HTTP status codes.
9
+ #
10
+ # @return array
11
+ def self.status_codes
12
+ {
13
+ 100 => 'Continue',
14
+ 101 => 'Switching Protocols',
15
+ 102 => 'Processing',
16
+ 200 => 'OK',
17
+ 201 => 'Created',
18
+ 202 => 'Accepted',
19
+ 203 => 'Non-Authorative Information',
20
+ 204 => 'No Content',
21
+ 205 => 'Reset Content',
22
+ 206 => 'Partial Content',
23
+ 207 => 'Multi-Status', # RFC 4918
24
+ 208 => 'Already Reported', # RFC 5842
25
+ 226 => 'IM Used', # RFC 3229
26
+ 300 => 'Multiple Choices',
27
+ 301 => 'Moved Permanently',
28
+ 302 => 'Found',
29
+ 303 => 'See Other',
30
+ 304 => 'Not Modified',
31
+ 305 => 'Use Proxy',
32
+ 307 => 'Temporary Redirect',
33
+ 308 => 'Permanent Redirect',
34
+ 400 => 'Bad Request',
35
+ 401 => 'Unauthorized',
36
+ 402 => 'Payment Required',
37
+ 403 => 'Forbidden',
38
+ 404 => 'Not Found',
39
+ 405 => 'Method Not Allowed',
40
+ 406 => 'Not Acceptable',
41
+ 407 => 'Proxy Authentication Required',
42
+ 408 => 'Request Timeout',
43
+ 409 => 'Conflict',
44
+ 410 => 'Gone',
45
+ 411 => 'Length Required',
46
+ 412 => 'Precondition failed',
47
+ 413 => 'Request Entity Too Large',
48
+ 414 => 'Request-URI Too Long',
49
+ 415 => 'Unsupported Media Type',
50
+ 416 => 'Requested Range Not Satisfiable',
51
+ 417 => 'Expectation Failed',
52
+ 418 => 'I\'m a teapot', # RFC 2324
53
+ 421 => 'Misdirected Request', # RFC7540 (HTTP/2)
54
+ 422 => 'Unprocessable Entity', # RFC 4918
55
+ 423 => 'Locked', # RFC 4918
56
+ 424 => 'Failed Dependency', # RFC 4918
57
+ 426 => 'Upgrade Required',
58
+ 428 => 'Precondition Required', # RFC 6585
59
+ 429 => 'Too Many Requests', # RFC 6585
60
+ 431 => 'Request Header Fields Too Large', # RFC 6585
61
+ 451 => 'Unavailable For Legal Reasons', # draft-tbray-http-legally-restricted-status
62
+ 500 => 'Internal Server Error',
63
+ 501 => 'Not Implemented',
64
+ 502 => 'Bad Gateway',
65
+ 503 => 'Service Unavailable',
66
+ 504 => 'Gateway Timeout',
67
+ 505 => 'HTTP Version not supported',
68
+ 506 => 'Variant Also Negotiates',
69
+ 507 => 'Insufficient Storage', # RFC 4918
70
+ 508 => 'Loop Detected', # RFC 5842
71
+ 509 => 'Bandwidth Limit Exceeded', # non-standard
72
+ 510 => 'Not extended',
73
+ 511 => 'Network Authentication Required' # RFC 6585
74
+ }
75
+ end
76
+
77
+ protected
78
+
79
+ # HTTP status code
80
+ #
81
+ # @return int
82
+ attr_accessor :status
83
+
84
+ # HTTP status text
85
+ #
86
+ # @return [String]
87
+ attr_accessor :status_text
88
+
89
+ public
90
+
91
+ # Creates the response object
92
+ #
93
+ # @param [String, Fixnum] status
94
+ # @param array headers
95
+ # @param resource body
96
+ # @return [void]
97
+ def initialize(status = nil, headers = nil, body = nil)
98
+ initialize_message # RUBY
99
+
100
+ self.status = status if status
101
+ update_headers(headers) if headers
102
+ self.body = body if body
103
+ end
104
+
105
+ # Returns the current HTTP status code.
106
+ #
107
+ # @return int
108
+ attr_reader :status
109
+
110
+ # Returns the human-readable status string.
111
+ #
112
+ # In the case of a 200, this may for example be 'OK'.
113
+ #
114
+ # @return [String]
115
+ attr_reader :status_text
116
+
117
+ # Sets the HTTP status code.
118
+ #
119
+ # This can be either the full HTTP status code with human readable string,
120
+ # for example: "403 I can't let you do that, Dave".
121
+ #
122
+ # Or just the code, in which case the appropriate default message will be
123
+ # added.
124
+ #
125
+ # @param [String, Fixnum] status
126
+ # @throws \InvalidArgumentExeption
127
+ # @return [void]
128
+ def status=(status)
129
+ if status.is_a?(Fixnum) || status =~ /^\d+$/
130
+ status_code = status
131
+ status_text = self.class.status_codes.key?(status.to_i) ? self.class.status_codes[status.to_i] : 'Unkown'
132
+ else
133
+ (
134
+ status_code,
135
+ status_text
136
+ ) = status.split(' ', 2)
137
+ end
138
+
139
+ fail ArgumentError, 'The HTTP status code must be exactly 3 digits' if status_code.to_i < 100 || status_code.to_i > 999
140
+
141
+ @status = status_code.to_i
142
+ @status_text = status_text
143
+ end
144
+
145
+ # Serializes the response object as a string.
146
+ #
147
+ # This is useful for debugging purposes.
148
+ #
149
+ # @return [String]
150
+ def to_s
151
+ str = "HTTP/#{http_version} #{status} #{status_text}\r\n"
152
+ headers.each do |key, value|
153
+ value.each do |v|
154
+ str << "#{key}: #{v}\r\n"
155
+ end
156
+ end
157
+
158
+ str << "\r\n"
159
+ str << body_as_string
160
+ str
161
+ end
162
+ end
163
+ end
164
+ end