restify 1.9.0.rc1 → 1.13.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +119 -48
- data/README.md +10 -12
- data/lib/restify/adapter/em.rb +2 -2
- data/lib/restify/adapter/pooled_em.rb +5 -5
- data/lib/restify/adapter/typhoeus.rb +20 -11
- data/lib/restify/context.rb +2 -15
- data/lib/restify/error.rb +56 -0
- data/lib/restify/global.rb +2 -2
- data/lib/restify/link.rb +4 -4
- data/lib/restify/promise.rb +8 -0
- data/lib/restify/relation.rb +1 -0
- data/lib/restify/timeout.rb +1 -0
- data/lib/restify/version.rb +2 -2
- data/spec/restify/context_spec.rb +2 -2
- data/spec/restify/error_spec.rb +70 -0
- data/spec/restify/features/response_errors.rb +79 -0
- data/spec/restify/global_spec.rb +1 -1
- data/spec/restify/promise_spec.rb +7 -0
- data/spec/restify_spec.rb +0 -2
- data/spec/spec_helper.rb +10 -6
- metadata +21 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e2b5dacc128de6cbfed604453544f51eaeffd16b9b6cb83f5353ecca66d24a8d
|
|
4
|
+
data.tar.gz: 5c3eba335362aea288f851327fa96262e5b464b9a747b092955241bb362e9c0d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 32569c3544a1ac6734b3c81c1e2b6a46ea7dba324c1f71f5e85bd1df93b8b73bb70114a7cec37dd8e38e561a821e674e8c437c78757d78df75e1e51b1a5a79f8
|
|
7
|
+
data.tar.gz: 6f03d156fcc20e0dfba3d7fda0a5c7851ff5d9342fbb9b50c4d90d1f43bf65e92f0ecabb714c4023ce4ca8cfb95b231abd7ea5d1e05339e839ea046b17f3d1fe
|
data/CHANGELOG.md
CHANGED
|
@@ -1,82 +1,153 @@
|
|
|
1
1
|
# Changelog
|
|
2
|
+
All notable changes to this project will be documented in this file.
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
5
|
+
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
4
6
|
|
|
5
|
-
* Add HEAD request method (#16)
|
|
6
7
|
|
|
7
|
-
## 1.7.0
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## Unreleased
|
|
10
|
+
---
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
### New
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
### Changes
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
### Fixes
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
* Add MessagePack processor enabled by default
|
|
18
|
+
### Breaks
|
|
19
19
|
|
|
20
|
-
## 1.4.4
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
## 1.13.0 - (2020-06-12)
|
|
22
|
+
---
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
### New
|
|
25
|
+
* typhoeus: Support setting per-request libcurl options on adapter
|
|
26
|
+
* typhoeus: Enable short TCP keepalive probes by default (5s/5s)
|
|
26
27
|
|
|
27
|
-
* Add advanced logging capabilities using logging gem
|
|
28
|
-
* Improve compatibility with webmocks returning `nil` as headers
|
|
29
28
|
|
|
30
|
-
## 1.
|
|
29
|
+
## 1.12.0 - (2020-04-01)
|
|
30
|
+
---
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
### Added
|
|
33
|
+
* Explicit exception class for HTTP status code 410 (Gone)
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
### Changed
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
*
|
|
37
|
+
### Fixed
|
|
38
|
+
* `GatewayError` exception classes introduced in v1.11.0 now properly inherit from `ServerError` (#30)
|
|
38
39
|
|
|
39
|
-
## 1.3.1
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
## 1.11.0 - (2019-07-11)
|
|
42
|
+
### Added
|
|
43
|
+
* Explicit exception classes for HTTP status codes 500, 502, 503, 504
|
|
43
44
|
|
|
44
|
-
## 1.
|
|
45
|
+
## 1.10.0 - 2018-12-11
|
|
46
|
+
### Changed
|
|
47
|
+
- Raise more specific error on a few status codes (#17)
|
|
48
|
+
- Complete promises with an empty list (but a list) of dependencies (#18)
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
## 1.9.0 - 2018-11-13
|
|
51
|
+
### Changed
|
|
52
|
+
- Do not raise error on 3XX responses but return responses
|
|
48
53
|
|
|
49
|
-
## 1.
|
|
54
|
+
## 1.8.0 - 2018-08-22
|
|
55
|
+
### Added
|
|
56
|
+
- Add HEAD request method (#16)
|
|
50
57
|
|
|
51
|
-
|
|
58
|
+
## 1.7.0 - 2018-08-15
|
|
59
|
+
### Added
|
|
60
|
+
- Introduce promise dependency timeouts (#15)
|
|
52
61
|
|
|
53
|
-
## 1.
|
|
62
|
+
## 1.6.0 - 2018-08-09
|
|
63
|
+
### Changed
|
|
64
|
+
- Specify headers on restify clients and individual requests (#14)
|
|
54
65
|
|
|
55
|
-
|
|
56
|
-
|
|
66
|
+
## 1.5.0 - 2018-07-31
|
|
67
|
+
### Added
|
|
68
|
+
- Add MessagePack processor enabled by default
|
|
57
69
|
|
|
58
|
-
|
|
70
|
+
### Changed
|
|
71
|
+
- Tune typhoeus adapter to be more race-condition resilent
|
|
59
72
|
|
|
60
|
-
|
|
61
|
-
|
|
73
|
+
## 1.4.4 - 2018-07-13
|
|
74
|
+
### Added
|
|
75
|
+
- Add `#request` to `NetworkError` to ease debugging
|
|
62
76
|
|
|
63
|
-
|
|
77
|
+
### Changed
|
|
78
|
+
- Fix race condition in typhoeus adapter
|
|
64
79
|
|
|
65
|
-
|
|
66
|
-
|
|
80
|
+
## 1.4.3 - 2017-11-15
|
|
81
|
+
### Added
|
|
82
|
+
- Add advanced logging capabilities using logging gem
|
|
67
83
|
|
|
68
|
-
|
|
84
|
+
### Changed
|
|
85
|
+
- Improve compatibility with webmocks returning `nil` as headers
|
|
69
86
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
87
|
+
## 1.4.1 - 2017-11-15
|
|
88
|
+
### Changed
|
|
89
|
+
- Fix possible deadlock issues
|
|
73
90
|
|
|
74
|
-
##
|
|
91
|
+
## 1.4.0 - 2017-11-10
|
|
92
|
+
### Added
|
|
93
|
+
- Add timeout option to requests (only supported by typhoeus adapter)
|
|
75
94
|
|
|
76
|
-
|
|
77
|
-
|
|
95
|
+
### Changed
|
|
96
|
+
- Fix possible concurrency issue with typhoeus adapter
|
|
97
|
+
|
|
98
|
+
## 1.3.1 - 2017-11-10
|
|
99
|
+
### Changed
|
|
100
|
+
- Improve typhoeus adapters initial request queuing
|
|
101
|
+
- Disable default pipelining
|
|
102
|
+
|
|
103
|
+
## 1.3.0 - 2017-11-08
|
|
104
|
+
### Changed
|
|
105
|
+
- Improve typhoeus adapter to better utilize concurrency
|
|
106
|
+
- Default to new typhoeus adapter
|
|
107
|
+
|
|
108
|
+
## 1.2.1 - 2017-10-30
|
|
109
|
+
### Changed
|
|
110
|
+
- Fix issue with Ruby 2.2 compatibility
|
|
111
|
+
|
|
112
|
+
## 1.2.0 - 2017-10-30
|
|
113
|
+
### Added
|
|
114
|
+
- Add experimental PooledEM adapter (#10)
|
|
115
|
+
|
|
116
|
+
### Changed
|
|
117
|
+
- Improve marshaling of resources
|
|
118
|
+
|
|
119
|
+
## 1.1.0 - 2017-05-12
|
|
120
|
+
### Added
|
|
121
|
+
- Add shortcuts for creating fulfilled / rejected promises (#6)
|
|
122
|
+
|
|
123
|
+
### Changed
|
|
124
|
+
- Return response body if no processor matches (#7)
|
|
125
|
+
|
|
126
|
+
## 1.0.0 - 2016-08-22
|
|
127
|
+
### Added
|
|
128
|
+
- Experimental cache API doing nothing for now
|
|
129
|
+
|
|
130
|
+
### Changed
|
|
131
|
+
- Use `~> 1.0` of `concurrent-ruby`
|
|
132
|
+
|
|
133
|
+
## 0.5.0 - 2016-04-04
|
|
134
|
+
### Added
|
|
135
|
+
- Add `sync` option to typhoeus adapter
|
|
136
|
+
- Add registry for storing entry points
|
|
137
|
+
|
|
138
|
+
### Changed
|
|
139
|
+
- Make eventmachine based adapter default
|
|
140
|
+
|
|
141
|
+
## 0.4.0 - 2016-02-24
|
|
142
|
+
### Added
|
|
143
|
+
- Add method to explicit access resource data
|
|
144
|
+
|
|
145
|
+
### Changed
|
|
146
|
+
- Use typhoeus as default adapter
|
|
147
|
+
- `Restify.new` returns relation now instead of resource
|
|
148
|
+
|
|
149
|
+
### Removed
|
|
150
|
+
- Drop obligation in favor of simple Concurrent::IVar based promise class.
|
|
78
151
|
Notable changes:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
* Use typhoeus as default adapter
|
|
82
|
-
* `Restify.new` returns relation now instead of resource
|
|
152
|
+
- Returned object us of type `Restify::Promise` now.
|
|
153
|
+
- `value` will not raise exception but return `nil` in case of failure. Use `value!` for old behavior.
|
data/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Restify
|
|
2
2
|
|
|
3
3
|
[](https://travis-ci.org/jgraichen/restify)
|
|
4
|
+
[](https://codebeat.co/projects/github-com-jgraichen-restify-master)
|
|
4
5
|
|
|
5
6
|
Restify is an hypermedia REST client that does parallel, concurrent and keep-alive requests by default.
|
|
6
7
|
|
|
@@ -14,20 +15,17 @@ Restify is build upon the following libraries:
|
|
|
14
15
|
* [addressable](https://github.com/sporkmonger/addressable)
|
|
15
16
|
* [typhoeus](https://github.com/typhoeus/typhoeus)
|
|
16
17
|
|
|
17
|
-
It has optional HTTP adapters using:
|
|
18
|
-
|
|
19
|
-
* [em-http-request](https://github.com/igrigorik/em-http-request)
|
|
20
|
-
|
|
21
18
|
The HTTP adapters are mostly run in a background thread and may not survive mid-application forks.
|
|
22
19
|
|
|
23
|
-
|
|
20
|
+
Restify includes processors to parse responses and to extract links between resources. The following formats are can be parsed:
|
|
24
21
|
|
|
25
|
-
*
|
|
26
|
-
* MessagePack
|
|
22
|
+
* JSON
|
|
23
|
+
* MessagePack
|
|
27
24
|
|
|
28
|
-
|
|
25
|
+
Links are extracted from
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
* HTTP Link header
|
|
28
|
+
* Github-style relations in payloads
|
|
31
29
|
|
|
32
30
|
### Planned features
|
|
33
31
|
|
|
@@ -105,9 +103,9 @@ commit = repo.rel(:commits).get.value.first
|
|
|
105
103
|
And print it:
|
|
106
104
|
|
|
107
105
|
```ruby
|
|
108
|
-
puts "Last commit: #{commit[
|
|
109
|
-
puts "By #{commit[
|
|
110
|
-
puts "#{commit[
|
|
106
|
+
puts "Last commit: #{commit['sha']}"
|
|
107
|
+
puts "By #{commit['commit']['author']['name']} <#{commit['commit']['author']['email']}>"
|
|
108
|
+
puts "#{commit['commit']['message']}"
|
|
111
109
|
```
|
|
112
110
|
|
|
113
111
|
See commented example in main spec [`spec/restify_spec.rb`](https://github.com/jgraichen/restify/blob/master/spec/restify_spec.rb#L100) or in the `examples` directory.
|
data/lib/restify/adapter/em.rb
CHANGED
|
@@ -63,8 +63,8 @@ module Restify
|
|
|
63
63
|
query: request.uri.normalized_query,
|
|
64
64
|
body: request.body,
|
|
65
65
|
head: request.headers
|
|
66
|
-
rescue Exception =>
|
|
67
|
-
writer.reject
|
|
66
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
67
|
+
writer.reject e
|
|
68
68
|
requests.shift unless pipeline?
|
|
69
69
|
return
|
|
70
70
|
end
|
|
@@ -200,9 +200,9 @@ module Restify
|
|
|
200
200
|
@pool = Pool.new(**kwargs)
|
|
201
201
|
end
|
|
202
202
|
|
|
203
|
-
# rubocop:disable MethodLength
|
|
204
|
-
# rubocop:disable AbcSize
|
|
205
|
-
# rubocop:disable BlockLength
|
|
203
|
+
# rubocop:disable Metrics/MethodLength
|
|
204
|
+
# rubocop:disable Metrics/AbcSize
|
|
205
|
+
# rubocop:disable Metrics/BlockLength
|
|
206
206
|
def call_native(request, writer)
|
|
207
207
|
next_tick do
|
|
208
208
|
defer = @pool.get(request)
|
|
@@ -241,9 +241,9 @@ module Restify
|
|
|
241
241
|
@pool.remove(conn)
|
|
242
242
|
writer.reject(req.error)
|
|
243
243
|
end
|
|
244
|
-
rescue Exception =>
|
|
244
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
245
245
|
@pool.remove(conn)
|
|
246
|
-
writer.reject(
|
|
246
|
+
writer.reject(e)
|
|
247
247
|
end
|
|
248
248
|
end
|
|
249
249
|
end
|
|
@@ -16,19 +16,28 @@ module Restify
|
|
|
16
16
|
'Transfer-Encoding' => ''
|
|
17
17
|
}.freeze
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
DEFAULT_OPTIONS = {
|
|
20
|
+
followlocation: true,
|
|
21
|
+
tcp_keepalive: true,
|
|
22
|
+
tcp_keepidle: 5,
|
|
23
|
+
tcp_keepintvl: 5
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
26
|
+
def initialize(sync: false, options: {}, **kwargs)
|
|
27
|
+
@sync = sync
|
|
28
|
+
@hydra = ::Typhoeus::Hydra.new(**kwargs)
|
|
29
|
+
@mutex = Mutex.new
|
|
30
|
+
@signal = ConditionVariable.new
|
|
31
|
+
@thread = nil
|
|
32
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
|
24
33
|
end
|
|
25
34
|
|
|
26
35
|
def sync?
|
|
27
36
|
@sync
|
|
28
37
|
end
|
|
29
38
|
|
|
30
|
-
# rubocop:disable AbcSize
|
|
31
|
-
# rubocop:disable MethodLength
|
|
39
|
+
# rubocop:disable Metrics/AbcSize
|
|
40
|
+
# rubocop:disable Metrics/MethodLength
|
|
32
41
|
def call_native(request, writer)
|
|
33
42
|
req = convert(request, writer)
|
|
34
43
|
|
|
@@ -56,15 +65,15 @@ module Restify
|
|
|
56
65
|
|
|
57
66
|
private
|
|
58
67
|
|
|
59
|
-
# rubocop:disable AbcSize
|
|
60
|
-
# rubocop:disable MethodLength
|
|
68
|
+
# rubocop:disable Metrics/AbcSize
|
|
69
|
+
# rubocop:disable Metrics/MethodLength
|
|
61
70
|
def convert(request, writer)
|
|
62
71
|
::Typhoeus::Request.new(
|
|
63
72
|
request.uri,
|
|
73
|
+
**@options,
|
|
64
74
|
method: request.method,
|
|
65
75
|
headers: DEFAULT_HEADERS.merge(request.headers),
|
|
66
76
|
body: request.body,
|
|
67
|
-
followlocation: true,
|
|
68
77
|
timeout: request.timeout,
|
|
69
78
|
connecttimeout: request.timeout
|
|
70
79
|
).tap do |req|
|
|
@@ -123,7 +132,7 @@ module Restify
|
|
|
123
132
|
@hydra.queued_requests.any? || @hydra.multi.easy_handles.any?
|
|
124
133
|
end
|
|
125
134
|
|
|
126
|
-
# rubocop:disable MethodLength
|
|
135
|
+
# rubocop:disable Metrics/MethodLength
|
|
127
136
|
def _run
|
|
128
137
|
debug 'hydra:run'
|
|
129
138
|
@hydra.run while _ongoing?
|
data/lib/restify/context.rb
CHANGED
|
@@ -36,7 +36,7 @@ module Restify
|
|
|
36
36
|
|
|
37
37
|
def inherit(uri, **kwargs)
|
|
38
38
|
uri ||= self.uri
|
|
39
|
-
Context.new
|
|
39
|
+
Context.new(uri, **kwargs, **options)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def process(response)
|
|
@@ -62,7 +62,7 @@ module Restify
|
|
|
62
62
|
if !response.errored?
|
|
63
63
|
process response
|
|
64
64
|
else
|
|
65
|
-
|
|
65
|
+
raise ResponseError.from_code(response)
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
end
|
|
@@ -101,18 +101,5 @@ module Restify
|
|
|
101
101
|
def default_headers
|
|
102
102
|
options.fetch(:headers, {})
|
|
103
103
|
end
|
|
104
|
-
|
|
105
|
-
class << self
|
|
106
|
-
def raise_response_error(response)
|
|
107
|
-
case response.code
|
|
108
|
-
when 400...500
|
|
109
|
-
raise ClientError.new(response)
|
|
110
|
-
when 500...600
|
|
111
|
-
raise ServerError.new(response)
|
|
112
|
-
else
|
|
113
|
-
raise "Unknown response code: #{response.code}"
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
104
|
end
|
|
118
105
|
end
|
data/lib/restify/error.rb
CHANGED
|
@@ -20,6 +20,37 @@ module Restify
|
|
|
20
20
|
class ResponseError < StandardError
|
|
21
21
|
attr_reader :response
|
|
22
22
|
|
|
23
|
+
def self.from_code(response)
|
|
24
|
+
case response.code
|
|
25
|
+
when 400
|
|
26
|
+
BadRequest.new(response)
|
|
27
|
+
when 401
|
|
28
|
+
Unauthorized.new(response)
|
|
29
|
+
when 404
|
|
30
|
+
NotFound.new(response)
|
|
31
|
+
when 406
|
|
32
|
+
NotAcceptable.new(response)
|
|
33
|
+
when 410
|
|
34
|
+
Gone.new(response)
|
|
35
|
+
when 422
|
|
36
|
+
UnprocessableEntity.new(response)
|
|
37
|
+
when 400...500
|
|
38
|
+
ClientError.new(response)
|
|
39
|
+
when 500
|
|
40
|
+
InternalServerError.new(response)
|
|
41
|
+
when 502
|
|
42
|
+
BadGateway.new(response)
|
|
43
|
+
when 503
|
|
44
|
+
ServiceUnavailable.new(response)
|
|
45
|
+
when 504
|
|
46
|
+
GatewayTimeout.new(response)
|
|
47
|
+
when 500...600
|
|
48
|
+
ServerError.new(response)
|
|
49
|
+
else
|
|
50
|
+
raise "Unknown response code: #{response.code}"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
23
54
|
def initialize(response)
|
|
24
55
|
@response = response
|
|
25
56
|
super "#{response.message} (#{response.code}) for `#{response.uri}':\n" \
|
|
@@ -65,4 +96,29 @@ module Restify
|
|
|
65
96
|
# A {ServerError} will be raised when a response has a
|
|
66
97
|
# 5XX status code.
|
|
67
98
|
class ServerError < ResponseError; end
|
|
99
|
+
|
|
100
|
+
# A {GatewayError} is the common base class for 502, 503 and 504
|
|
101
|
+
# response codes often used by load balancers when upstream servers are
|
|
102
|
+
# failing or not available.
|
|
103
|
+
#
|
|
104
|
+
# This can be used to catch "common" gateway responses.
|
|
105
|
+
class GatewayError < ServerError; end
|
|
106
|
+
|
|
107
|
+
###
|
|
108
|
+
# CONCRETE SUBCLASSES FOR TYPICAL STATUS CODES
|
|
109
|
+
#
|
|
110
|
+
# This makes it easy to rescue specific expected error types.
|
|
111
|
+
|
|
112
|
+
class BadRequest < ClientError; end
|
|
113
|
+
class Unauthorized < ClientError; end
|
|
114
|
+
class NotFound < ClientError; end
|
|
115
|
+
class NotAcceptable < ClientError; end
|
|
116
|
+
class Gone < ClientError; end
|
|
117
|
+
class UnprocessableEntity < ClientError; end
|
|
118
|
+
|
|
119
|
+
class InternalServerError < ServerError; end
|
|
120
|
+
|
|
121
|
+
class BadGateway < GatewayError; end
|
|
122
|
+
class ServiceUnavailable < GatewayError; end
|
|
123
|
+
class GatewayTimeout < GatewayError; end
|
|
68
124
|
end
|
data/lib/restify/global.rb
CHANGED
|
@@ -39,9 +39,9 @@ module Restify
|
|
|
39
39
|
|
|
40
40
|
def resolve_context(uri, **opts)
|
|
41
41
|
if uri.is_a? Symbol
|
|
42
|
-
Restify::Registry.fetch(uri).inherit(nil, opts)
|
|
42
|
+
Restify::Registry.fetch(uri).inherit(nil, **opts)
|
|
43
43
|
else
|
|
44
|
-
Context.new
|
|
44
|
+
Context.new(uri, **opts)
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
end
|
data/lib/restify/link.rb
CHANGED
|
@@ -25,10 +25,10 @@ module Restify
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
class << self
|
|
28
|
-
REGEXP_URI = /<[^>]*>\s
|
|
29
|
-
REGEXP_PAR = /;\s*\w+\s*=\s*/i
|
|
30
|
-
REGEXP_QUT = /"[^"]*"\s
|
|
31
|
-
REGEXP_ARG = /\w+\s*/i
|
|
28
|
+
REGEXP_URI = /<[^>]*>\s*/.freeze
|
|
29
|
+
REGEXP_PAR = /;\s*\w+\s*=\s*/i.freeze
|
|
30
|
+
REGEXP_QUT = /"[^"]*"\s*/.freeze
|
|
31
|
+
REGEXP_ARG = /\w+\s*/i.freeze
|
|
32
32
|
|
|
33
33
|
def parse(string)
|
|
34
34
|
scanner = StringScanner.new(string.strip)
|
data/lib/restify/promise.rb
CHANGED
|
@@ -7,6 +7,13 @@ module Restify
|
|
|
7
7
|
@dependencies = dependencies.flatten
|
|
8
8
|
|
|
9
9
|
super(&nil)
|
|
10
|
+
|
|
11
|
+
# When dependencies were passed in, but none are left after flattening,
|
|
12
|
+
# then we don't have to wait for explicit dependencies or resolution
|
|
13
|
+
# through a writer.
|
|
14
|
+
if !@task && @dependencies.empty? && dependencies.any?
|
|
15
|
+
complete true, [], nil
|
|
16
|
+
end
|
|
10
17
|
end
|
|
11
18
|
|
|
12
19
|
def wait(timeout = nil)
|
|
@@ -16,6 +23,7 @@ module Restify
|
|
|
16
23
|
|
|
17
24
|
super
|
|
18
25
|
raise t if incomplete?
|
|
26
|
+
|
|
19
27
|
self
|
|
20
28
|
end
|
|
21
29
|
|
data/lib/restify/relation.rb
CHANGED
data/lib/restify/timeout.rb
CHANGED
data/lib/restify/version.rb
CHANGED
|
@@ -47,7 +47,7 @@ describe Restify::Context do
|
|
|
47
47
|
|
|
48
48
|
context 'YAML' do
|
|
49
49
|
let(:dump) { YAML.dump(context) }
|
|
50
|
-
let(:load) { YAML.load(dump) } # rubocop:disable YAMLLoad
|
|
50
|
+
let(:load) { YAML.load(dump) } # rubocop:disable Security/YAMLLoad
|
|
51
51
|
|
|
52
52
|
subject { load }
|
|
53
53
|
|
|
@@ -56,7 +56,7 @@ describe Restify::Context do
|
|
|
56
56
|
|
|
57
57
|
context 'Marshall' do
|
|
58
58
|
let(:dump) { Marshal.dump(context) }
|
|
59
|
-
let(:load) { Marshal.load(dump) } # rubocop:disable MarshalLoad
|
|
59
|
+
let(:load) { Marshal.load(dump) } # rubocop:disable Security/MarshalLoad
|
|
60
60
|
|
|
61
61
|
subject { load }
|
|
62
62
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Restify::ResponseError do
|
|
6
|
+
let(:response) { double 'response' }
|
|
7
|
+
let(:message) { 'Error' }
|
|
8
|
+
let(:uri) { 'http://localhost' }
|
|
9
|
+
|
|
10
|
+
before do
|
|
11
|
+
allow(response).to receive(:uri).and_return(uri)
|
|
12
|
+
allow(response).to receive(:code).and_return(code)
|
|
13
|
+
allow(response).to receive(:message).and_return(message)
|
|
14
|
+
allow(response).to receive(:decoded_body).and_return({})
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe '.from_code' do
|
|
18
|
+
subject(:err) { described_class.from_code(response) }
|
|
19
|
+
|
|
20
|
+
context 'with 400 Bad Request' do
|
|
21
|
+
let(:code) { 400 }
|
|
22
|
+
it { is_expected.to be_a ::Restify::BadRequest }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context 'with 401 Unauthorized' do
|
|
26
|
+
let(:code) { 401 }
|
|
27
|
+
it { is_expected.to be_a ::Restify::Unauthorized }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
context 'with 404 Unauthorized' do
|
|
31
|
+
let(:code) { 404 }
|
|
32
|
+
it { is_expected.to be_a ::Restify::NotFound }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
context 'with 406 Not Acceptable' do
|
|
36
|
+
let(:code) { 406 }
|
|
37
|
+
it { is_expected.to be_a ::Restify::NotAcceptable }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
context 'with 410 Gone' do
|
|
41
|
+
let(:code) { 410 }
|
|
42
|
+
it { is_expected.to be_a ::Restify::Gone }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
context 'with 422 Unprocessable Entity' do
|
|
46
|
+
let(:code) { 422 }
|
|
47
|
+
it { is_expected.to be_a ::Restify::UnprocessableEntity }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
context 'with 500 Internal Server Error' do
|
|
51
|
+
let(:code) { 500 }
|
|
52
|
+
it { is_expected.to be_a ::Restify::InternalServerError }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
context 'with 502 Bad Gateway' do
|
|
56
|
+
let(:code) { 502 }
|
|
57
|
+
it { is_expected.to be_a ::Restify::BadGateway }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
context 'with 503 Service Unavailable' do
|
|
61
|
+
let(:code) { 503 }
|
|
62
|
+
it { is_expected.to be_a ::Restify::ServiceUnavailable }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
context 'with 504 Gateway Timeout' do
|
|
66
|
+
let(:code) { 504 }
|
|
67
|
+
it { is_expected.to be_a ::Restify::GatewayTimeout }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Restify do
|
|
6
|
+
let!(:request_stub) do
|
|
7
|
+
stub_request(:get, 'http://localhost/base')
|
|
8
|
+
.to_return do
|
|
9
|
+
<<-RESPONSE.gsub(/^ {8}/, '')
|
|
10
|
+
HTTP/1.1 #{http_status}
|
|
11
|
+
Content-Length: 333
|
|
12
|
+
Transfer-Encoding: chunked
|
|
13
|
+
Link: <http://localhost/other>; rel="neat"
|
|
14
|
+
RESPONSE
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
let(:http_status) { '200 OK' }
|
|
19
|
+
|
|
20
|
+
describe 'Error handling' do
|
|
21
|
+
subject(:request) { Restify.new('http://localhost/base').get.value! }
|
|
22
|
+
|
|
23
|
+
context 'for 400 status codes' do
|
|
24
|
+
let(:http_status) { '400 Bad Request' }
|
|
25
|
+
|
|
26
|
+
it 'throws a BadRequest exception' do
|
|
27
|
+
expect { request }.to raise_error Restify::BadRequest
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
context 'for 401 status codes' do
|
|
32
|
+
let(:http_status) { '401 Unauthorized' }
|
|
33
|
+
|
|
34
|
+
it 'throws an Unauthorized exception' do
|
|
35
|
+
expect { request }.to raise_error Restify::Unauthorized
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context 'for 404 status codes' do
|
|
40
|
+
let(:http_status) { '404 Not Found' }
|
|
41
|
+
|
|
42
|
+
it 'throws a ClientError exception' do
|
|
43
|
+
expect { request }.to raise_error Restify::NotFoundError
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context 'for 406 status codes' do
|
|
48
|
+
let(:http_status) { '406 Not Acceptable' }
|
|
49
|
+
|
|
50
|
+
it 'throws a NotAcceptable exception' do
|
|
51
|
+
expect { request }.to raise_error Restify::NotAcceptable
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
context 'for 422 status codes' do
|
|
56
|
+
let(:http_status) { '422 Unprocessable Entity' }
|
|
57
|
+
|
|
58
|
+
it 'throws a UnprocessableEntity exception' do
|
|
59
|
+
expect { request }.to raise_error Restify::UnprocessableEntity
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
context 'for any other 4xx status codes' do
|
|
64
|
+
let(:http_status) { '415 Unsupported Media Type' }
|
|
65
|
+
|
|
66
|
+
it 'throws a generic ClientError exception' do
|
|
67
|
+
expect { request }.to raise_error Restify::ClientError
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
context 'for any 5xx status codes' do
|
|
72
|
+
let(:http_status) { '500 Internal Server Error' }
|
|
73
|
+
|
|
74
|
+
it 'throws a generic ServerError exception' do
|
|
75
|
+
expect { request }.to raise_error Restify::ServerError
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
data/spec/restify/global_spec.rb
CHANGED
|
@@ -26,7 +26,7 @@ describe Restify::Global do
|
|
|
26
26
|
let(:options) { {accept: 'application.vnd.github.v3+json'} }
|
|
27
27
|
let(:context) { Restify::Context.new uri, **options }
|
|
28
28
|
|
|
29
|
-
subject { global.new
|
|
29
|
+
subject { global.new(name, **options) }
|
|
30
30
|
|
|
31
31
|
it 'returns relation for stored registry item' do
|
|
32
32
|
Restify::Registry.store name, uri, options
|
|
@@ -163,6 +163,13 @@ describe Restify::Promise do
|
|
|
163
163
|
expect(subject).to eq 17
|
|
164
164
|
end
|
|
165
165
|
end
|
|
166
|
+
|
|
167
|
+
# Nobody does this explicitly, but it can happen when the array of
|
|
168
|
+
# dependencies is built dynamically.
|
|
169
|
+
context 'with an empty array of dependencies and without task' do
|
|
170
|
+
subject { described_class.new([]).value! }
|
|
171
|
+
it { is_expected.to eq [] }
|
|
172
|
+
end
|
|
166
173
|
end
|
|
167
174
|
|
|
168
175
|
describe '#wait' do
|
data/spec/restify_spec.rb
CHANGED
|
@@ -134,7 +134,6 @@ describe Restify do
|
|
|
134
134
|
# the result is here.
|
|
135
135
|
expect { create_user_promise.value! }.to \
|
|
136
136
|
raise_error(Restify::ClientError) do |e|
|
|
137
|
-
|
|
138
137
|
# Because we forgot to send a "name" the server complains
|
|
139
138
|
# with an error code that will lead to a raised error.
|
|
140
139
|
|
|
@@ -206,7 +205,6 @@ describe Restify do
|
|
|
206
205
|
|
|
207
206
|
expect { create_user_promise.value! }.to \
|
|
208
207
|
raise_error(Restify::ClientError) do |e|
|
|
209
|
-
|
|
210
208
|
expect(e.status).to eq :unprocessable_entity
|
|
211
209
|
expect(e.code).to eq 422
|
|
212
210
|
expect(e.errors).to eq 'name' => ["can't be blank"]
|
data/spec/spec_helper.rb
CHANGED
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
require 'rspec'
|
|
4
4
|
require 'webmock/rspec'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
require 'simplecov'
|
|
7
|
+
SimpleCov.start do
|
|
8
|
+
add_filter 'spec'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
if ENV['CI']
|
|
12
|
+
require 'codecov'
|
|
13
|
+
SimpleCov.formatter = SimpleCov::Formatter::Codecov
|
|
11
14
|
end
|
|
12
15
|
|
|
13
16
|
require 'restify'
|
|
@@ -35,7 +38,7 @@ require 'webmock/rspec'
|
|
|
35
38
|
require 'rspec/collection_matchers'
|
|
36
39
|
require 'em-synchrony'
|
|
37
40
|
|
|
38
|
-
Dir[File.expand_path('spec/support/**/*.rb')].each {|f| require f }
|
|
41
|
+
Dir[File.expand_path('spec/support/**/*.rb')].sort.each {|f| require f }
|
|
39
42
|
|
|
40
43
|
RSpec.configure do |config|
|
|
41
44
|
config.order = 'random'
|
|
@@ -51,6 +54,7 @@ RSpec.configure do |config|
|
|
|
51
54
|
::Logging.logger.root.add_appenders ::Logging.appenders.stdout
|
|
52
55
|
end
|
|
53
56
|
|
|
57
|
+
config.warnings = true
|
|
54
58
|
config.after(:suite) do
|
|
55
59
|
EventMachine.stop if defined?(EventMachine) && EventMachine.reactor_running?
|
|
56
60
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: restify
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.13.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jan Graichen
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-06-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -56,16 +56,22 @@ dependencies:
|
|
|
56
56
|
name: hashie
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
|
-
- - "
|
|
59
|
+
- - ">="
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
61
|
version: '3.3'
|
|
62
|
+
- - "<"
|
|
63
|
+
- !ruby/object:Gem::Version
|
|
64
|
+
version: '5.0'
|
|
62
65
|
type: :runtime
|
|
63
66
|
prerelease: false
|
|
64
67
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
68
|
requirements:
|
|
66
|
-
- - "
|
|
69
|
+
- - ">="
|
|
67
70
|
- !ruby/object:Gem::Version
|
|
68
71
|
version: '3.3'
|
|
72
|
+
- - "<"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '5.0'
|
|
69
75
|
- !ruby/object:Gem::Dependency
|
|
70
76
|
name: rack
|
|
71
77
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -140,16 +146,16 @@ dependencies:
|
|
|
140
146
|
name: bundler
|
|
141
147
|
requirement: !ruby/object:Gem::Requirement
|
|
142
148
|
requirements:
|
|
143
|
-
- - "
|
|
149
|
+
- - ">="
|
|
144
150
|
- !ruby/object:Gem::Version
|
|
145
|
-
version: '
|
|
151
|
+
version: '0'
|
|
146
152
|
type: :development
|
|
147
153
|
prerelease: false
|
|
148
154
|
version_requirements: !ruby/object:Gem::Requirement
|
|
149
155
|
requirements:
|
|
150
|
-
- - "
|
|
156
|
+
- - ">="
|
|
151
157
|
- !ruby/object:Gem::Version
|
|
152
|
-
version: '
|
|
158
|
+
version: '0'
|
|
153
159
|
description: An experimental hypermedia REST client that uses parallel, keep-alive
|
|
154
160
|
and pipelined requests by default.
|
|
155
161
|
email:
|
|
@@ -186,8 +192,10 @@ files:
|
|
|
186
192
|
- lib/restify/version.rb
|
|
187
193
|
- spec/restify/cache_spec.rb
|
|
188
194
|
- spec/restify/context_spec.rb
|
|
195
|
+
- spec/restify/error_spec.rb
|
|
189
196
|
- spec/restify/features/head_requests_spec.rb
|
|
190
197
|
- spec/restify/features/request_headers_spec.rb
|
|
198
|
+
- spec/restify/features/response_errors.rb
|
|
191
199
|
- spec/restify/global_spec.rb
|
|
192
200
|
- spec/restify/link_spec.rb
|
|
193
201
|
- spec/restify/processors/base_spec.rb
|
|
@@ -215,20 +223,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
215
223
|
version: '0'
|
|
216
224
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
217
225
|
requirements:
|
|
218
|
-
- - "
|
|
226
|
+
- - ">="
|
|
219
227
|
- !ruby/object:Gem::Version
|
|
220
|
-
version:
|
|
228
|
+
version: '0'
|
|
221
229
|
requirements: []
|
|
222
|
-
|
|
223
|
-
rubygems_version: 2.7.7
|
|
230
|
+
rubygems_version: 3.1.2
|
|
224
231
|
signing_key:
|
|
225
232
|
specification_version: 4
|
|
226
233
|
summary: An experimental hypermedia REST client.
|
|
227
234
|
test_files:
|
|
228
235
|
- spec/restify/cache_spec.rb
|
|
229
236
|
- spec/restify/context_spec.rb
|
|
237
|
+
- spec/restify/error_spec.rb
|
|
230
238
|
- spec/restify/features/head_requests_spec.rb
|
|
231
239
|
- spec/restify/features/request_headers_spec.rb
|
|
240
|
+
- spec/restify/features/response_errors.rb
|
|
232
241
|
- spec/restify/global_spec.rb
|
|
233
242
|
- spec/restify/link_spec.rb
|
|
234
243
|
- spec/restify/processors/base_spec.rb
|