lhc 9.4.0 → 9.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +688 -72
- data/gh-md-toc +228 -0
- data/lhc.gemspec +3 -3
- data/lib/lhc/version.rb +1 -1
- metadata +6 -20
- data/docs/configuration.md +0 -57
- data/docs/exceptions.md +0 -68
- data/docs/interceptors.md +0 -98
- data/docs/interceptors/authentication.md +0 -54
- data/docs/interceptors/caching.md +0 -68
- data/docs/interceptors/default_timeout.md +0 -17
- data/docs/interceptors/logging.md +0 -29
- data/docs/interceptors/monitoring.md +0 -68
- data/docs/interceptors/prometheus.md +0 -18
- data/docs/interceptors/retry.md +0 -24
- data/docs/interceptors/rollbar.md +0 -19
- data/docs/interceptors/zipkin.md +0 -23
- data/docs/request.md +0 -24
- data/docs/response.md +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 153c47a835c9f4f7e4ac644c18820bc4bb1f87a1f09b5321179fee621bfb42b0
|
4
|
+
data.tar.gz: 4995637bddf13e0fb48a97d2bb3c592a1ee1346675d38cc219d1f405d93cd35d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c934cbf9f4de81b821d3ae61ff93f9be65ed7700be82b842d697cf978880c4d368b2e0a26ec0bc302ba608ffb52832702069ccdf77d8218e505cf9967196f74
|
7
|
+
data.tar.gz: 83e666f17c733e063bb5d64e082d47d5a830c48d7bb331dfeee827ec56a18335b64db104f270541e1f88dbf17da5fb05f00ed58872cd51664074a079085c3d13
|
data/README.md
CHANGED
@@ -1,78 +1,137 @@
|
|
1
1
|
LHC
|
2
2
|
===
|
3
3
|
|
4
|
-
LHC uses [typhoeus](https://github.com/typhoeus/typhoeus) for http communication.
|
4
|
+
LHC uses [typhoeus](https://github.com/typhoeus/typhoeus) for low level http communication.
|
5
5
|
|
6
|
-
See [LHS](https://github.com/local-ch/LHS), if you are searching for something more **high level** that can query webservices easily and provides
|
6
|
+
See [LHS](https://github.com/local-ch/LHS), if you are searching for something more **high level** that can query webservices easily and provides an ActiveRecord like interface.
|
7
7
|
|
8
|
-
## Quick
|
8
|
+
## Quick start guide
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem install lhc
|
12
|
+
```
|
13
|
+
|
14
|
+
or add it to your Gemfile:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem 'lhc'
|
18
|
+
```
|
19
|
+
|
20
|
+
use it like:
|
9
21
|
|
10
22
|
```ruby
|
11
23
|
response = LHC.get('http://datastore/v2/feedbacks')
|
12
24
|
response.data.items[0]
|
13
25
|
response.data.items[0].recommended
|
14
|
-
response.body
|
15
|
-
response.headers
|
26
|
+
response.body
|
27
|
+
response.headers
|
16
28
|
```
|
17
29
|
|
30
|
+
## Table of contents
|
31
|
+
|
32
|
+
* [LHC](#lhc)
|
33
|
+
* [Quick start guide](#quick-start-guide)
|
34
|
+
* [Basic methods](#basic-methods)
|
35
|
+
* [Request](#request)
|
36
|
+
* [Formats](#formats)
|
37
|
+
* [Parallel requests](#parallel-requests)
|
38
|
+
* [Follow redirects](#follow-redirects)
|
39
|
+
* [Transfer data through the request body](#transfer-data-through-the-request-body)
|
40
|
+
* [Request parameters](#request-parameters)
|
41
|
+
* [Array Parameter Encoding](#array-parameter-encoding)
|
42
|
+
* [Request URL encoding](#request-url-encoding)
|
43
|
+
* [Request URL-Templates](#request-url-templates)
|
44
|
+
* [Request timeout](#request-timeout)
|
45
|
+
* [Response](#response)
|
46
|
+
* [Accessing response data](#accessing-response-data)
|
47
|
+
* [Exceptions](#exceptions)
|
48
|
+
* [Custom error handling](#custom-error-handling)
|
49
|
+
* [Ignore certain errors](#ignore-certain-errors)
|
50
|
+
* [Configuration](#configuration)
|
51
|
+
* [Configuring endpoints](#configuring-endpoints)
|
52
|
+
* [Configuring placeholders](#configuring-placeholders)
|
53
|
+
* [Interceptors](#interceptors)
|
54
|
+
* [Quick start: Configure/Enable Interceptors](#quick-start-configureenable-interceptors)
|
55
|
+
* [Interceptors on local request level](#interceptors-on-local-request-level)
|
56
|
+
* [Core Interceptors](#core-interceptors)
|
57
|
+
* [Authentication Interceptor](#authentication-interceptor)
|
58
|
+
* [Bearer Authentication](#bearer-authentication)
|
59
|
+
* [Basic Authentication](#basic-authentication)
|
60
|
+
* [Reauthenticate](#reauthenticate)
|
61
|
+
* [Bearer Authentication with client access token](#bearer-authentication-with-client-access-token)
|
62
|
+
* [Caching Interceptor](#caching-interceptor)
|
63
|
+
* [Options](#options)
|
64
|
+
* [Testing](#testing)
|
65
|
+
* [Default Timeout Interceptor](#default-timeout-interceptor)
|
66
|
+
* [Overwrite defaults](#overwrite-defaults)
|
67
|
+
* [Logging Interceptor](#logging-interceptor)
|
68
|
+
* [Installation](#installation)
|
69
|
+
* [What and how it logs](#what-and-how-it-logs)
|
70
|
+
* [Configure](#configure)
|
71
|
+
* [Monitoring Interceptor](#monitoring-interceptor)
|
72
|
+
* [Installation](#installation-1)
|
73
|
+
* [Environment](#environment)
|
74
|
+
* [What it tracks](#what-it-tracks)
|
75
|
+
* [Configure](#configure-1)
|
76
|
+
* [Prometheus Interceptor](#prometheus-interceptor)
|
77
|
+
* [Retry Interceptor](#retry-interceptor)
|
78
|
+
* [Limit the amount of retries while making the request](#limit-the-amount-of-retries-while-making-the-request)
|
79
|
+
* [Change the default maximum of retries of the retry interceptor](#change-the-default-maximum-of-retries-of-the-retry-interceptor)
|
80
|
+
* [Rollbar Interceptor](#rollbar-interceptor)
|
81
|
+
* [Forward additional parameters](#forward-additional-parameters)
|
82
|
+
* [Zipkin](#zipkin)
|
83
|
+
* [Create an interceptor from scratch](#create-an-interceptor-from-scratch)
|
84
|
+
* [Interceptor callbacks](#interceptor-callbacks)
|
85
|
+
* [Interceptor request/response](#interceptor-requestresponse)
|
86
|
+
* [Provide a response replacement through an interceptor](#provide-a-response-replacement-through-an-interceptor)
|
87
|
+
* [License](#license)
|
88
|
+
|
18
89
|
## Basic methods
|
19
90
|
|
20
91
|
Available are `get`, `post`, `put` & `delete`.
|
21
92
|
|
22
93
|
Other methods are available using `LHC.request(options)`.
|
23
94
|
|
24
|
-
##
|
95
|
+
## Request
|
25
96
|
|
26
|
-
|
97
|
+
The request class handles the http request, implements the interceptor pattern, loads configured endpoints, generates urls from url-templates and raises [exceptions](#exceptions) for any response code that is not indicating success (2xx).
|
27
98
|
|
28
99
|
```ruby
|
29
|
-
LHC.
|
30
|
-
```
|
100
|
+
response = LHC.request(url: 'http://local.ch', method: :options)
|
31
101
|
|
32
|
-
|
102
|
+
response.request.response #<LHC::Response> the associated response.
|
33
103
|
|
34
|
-
|
104
|
+
response.request.options #<Hash> the options used for creating the request.
|
35
105
|
|
36
|
-
|
37
|
-
LHC.json.post('http://slack', body: { text: 'Hi there' })
|
38
|
-
# Content-Type: application/json
|
39
|
-
# Accept: application/json
|
40
|
-
# Translates body to "{\"text\":\"Hi there\"}" before sending
|
41
|
-
```
|
106
|
+
response.request.params # access request params
|
42
107
|
|
43
|
-
|
108
|
+
response.request.headers # access request headers
|
44
109
|
|
45
|
-
|
46
|
-
response = LHC.request(url: 'http://local.ch', method: :options)
|
47
|
-
response.headers
|
110
|
+
response.request.url #<String> URL that is used for doing the request
|
48
111
|
|
49
|
-
response
|
50
|
-
response.data
|
112
|
+
response.request.method #<Symbol> provides the used http-method
|
51
113
|
```
|
52
114
|
|
53
|
-
|
54
|
-
|
55
|
-
→ [Read more about the response object](docs/response.md)
|
56
|
-
|
57
|
-
## Accessing data
|
115
|
+
### Formats
|
58
116
|
|
59
|
-
|
117
|
+
You can use any of the basic methods in combination with a format like `json`:
|
60
118
|
|
61
119
|
```ruby
|
62
|
-
|
63
|
-
response.data.as_open_struct #<OpenStruct name='local.ch'>
|
64
|
-
response.data.as_json # { name: 'local.ch' }
|
65
|
-
response.data.name # 'local.ch'
|
66
|
-
response.data[:name] # 'local.ch'
|
120
|
+
LHC.json.get(options)
|
67
121
|
```
|
68
122
|
|
69
|
-
|
123
|
+
Currently supported formats: `json`
|
124
|
+
|
125
|
+
If formats are used, headers for `Content-Type` and `Accept` are set by LHC, but also http bodies are translated by LHC, so you can pass bodies as ruby objects:
|
70
126
|
|
71
127
|
```ruby
|
72
|
-
|
128
|
+
LHC.json.post('http://slack', body: { text: 'Hi there' })
|
129
|
+
# Content-Type: application/json
|
130
|
+
# Accept: application/json
|
131
|
+
# Translates body to "{\"text\":\"Hi there\"}" before sending
|
73
132
|
```
|
74
133
|
|
75
|
-
|
134
|
+
### Parallel requests
|
76
135
|
|
77
136
|
If you pass an array of requests to `LHC.request`, it will perform those requests in parallel.
|
78
137
|
You will get back an array of LHC::Response objects in the same order of the passed requests.
|
@@ -89,17 +148,17 @@ LHC.request([request1, request2, request3])
|
|
89
148
|
# returns [response1, response2, response3]
|
90
149
|
```
|
91
150
|
|
92
|
-
|
151
|
+
### Follow redirects
|
93
152
|
|
94
153
|
```ruby
|
95
154
|
LHC.get('http://local.ch', followlocation: true)
|
96
155
|
```
|
97
156
|
|
98
|
-
|
157
|
+
### Transfer data through the request body
|
99
158
|
|
100
159
|
Data that is transfered using the HTTP request body is transfered using the selected format, or the default `json`, so you need to provide it as a ruby object.
|
101
160
|
|
102
|
-
Also consider setting the http header for content-type or use one of the provided formats, like `LHC.json`.
|
161
|
+
Also consider setting the http header for content-type or use one of the provided [formats](#formats), like `LHC.json`.
|
103
162
|
|
104
163
|
```ruby
|
105
164
|
LHC.post('http://datastore/v2/feedbacks',
|
@@ -108,9 +167,9 @@ Also consider setting the http header for content-type or use one of the provide
|
|
108
167
|
)
|
109
168
|
```
|
110
169
|
|
111
|
-
|
170
|
+
### Request parameters
|
112
171
|
|
113
|
-
When using LHC, try to pass params via `params` option. It's not recommended to build a url and attach the
|
172
|
+
When using LHC, try to pass params via `params` option. It's not recommended to build a url and attach the parameters yourself:
|
114
173
|
|
115
174
|
DO
|
116
175
|
```ruby
|
@@ -122,7 +181,7 @@ DON'T
|
|
122
181
|
LHC.get('http://local.ch?q=Restaurant')
|
123
182
|
```
|
124
183
|
|
125
|
-
|
184
|
+
#### Array Parameter Encoding
|
126
185
|
|
127
186
|
LHC can encode array parameters in URLs in two ways. The default is `:rack` which generates URL parameters compatible with Rack and Rails.
|
128
187
|
|
@@ -138,7 +197,7 @@ LHC.get('http://local.ch', params: { q: [1, 2] }, params_encoding: :multi)
|
|
138
197
|
# http://local.ch?q=1&q=2
|
139
198
|
```
|
140
199
|
|
141
|
-
|
200
|
+
### Request URL encoding
|
142
201
|
|
143
202
|
LHC, by default, encodes urls:
|
144
203
|
|
@@ -157,21 +216,29 @@ LHC.get('http://local.ch?q=some space', url_encoding: false)
|
|
157
216
|
# http://local.ch?q=some space
|
158
217
|
```
|
159
218
|
|
160
|
-
|
219
|
+
### Request URL-Templates
|
220
|
+
|
221
|
+
Instead of using concrete urls you can also use url-templates that contain placeholders.
|
222
|
+
This is especially handy for configuring an endpoint once and generate the url from the params when doing the request.
|
223
|
+
Since version `7.0` url templates follow the [RFC 6750](https://tools.ietf.org/html/rfc6570).
|
161
224
|
|
162
|
-
|
225
|
+
```ruby
|
226
|
+
LHC.get('http://datastore/v2/feedbacks/{id}', params:{ id: 123 })
|
227
|
+
# GET http://datastore/v2/feedbacks/123
|
228
|
+
```
|
163
229
|
|
230
|
+
You can also use URL templates, when [configuring endpoints](#configuring-endpoints):
|
164
231
|
```ruby
|
165
232
|
LHC.configure do |c|
|
166
|
-
c.
|
167
|
-
c.endpoint :feedbacks, '{+datastore}/feedbacks', params: { has_reviews: true }
|
168
|
-
c.interceptors = [LHC::Caching]
|
233
|
+
c.endpoint(:find_feedback, 'http://datastore/v2/feedbacks/{id}')
|
169
234
|
end
|
235
|
+
|
236
|
+
LHC.get(:find_feedback, params:{ id: 123 }) # GET http://datastore/v2/feedbacks/123
|
170
237
|
```
|
171
238
|
|
172
|
-
|
239
|
+
If you miss to provide a parameter that is part of the url-template, it will raise an exception.
|
173
240
|
|
174
|
-
|
241
|
+
### Request timeout
|
175
242
|
|
176
243
|
Working and configuring timeouts is important, to ensure your app stays alive when services you depend on start to get really slow...
|
177
244
|
|
@@ -184,35 +251,112 @@ LHC forwards two timeout options directly to typhoeus:
|
|
184
251
|
LHC.get('http://local.ch', timeout: 5, connecttimeout: 1)
|
185
252
|
```
|
186
253
|
|
187
|
-
LHC provides a [timeout interceptor](
|
254
|
+
LHC provides a [timeout interceptor](#default-timeout-interceptor) that lets you apply default timeout values to all the requests that you are performig in your application.
|
188
255
|
|
189
|
-
##
|
256
|
+
## Response
|
190
257
|
|
191
|
-
|
192
|
-
|
193
|
-
|
258
|
+
```ruby
|
259
|
+
response.request #<LHC::Request> the associated request.
|
260
|
+
|
261
|
+
response.data #<OpenStruct> in case response body contains parsable JSON.
|
262
|
+
response.data.something.nested
|
263
|
+
|
264
|
+
response.body #<String>
|
265
|
+
|
266
|
+
response.code #<Fixnum>
|
267
|
+
|
268
|
+
response.headers #<Hash>
|
269
|
+
|
270
|
+
response.time #<Fixnum> Provides response time in ms.
|
271
|
+
|
272
|
+
response.timeout? #true|false
|
273
|
+
```
|
274
|
+
|
275
|
+
### Accessing response data
|
276
|
+
|
277
|
+
The response data can be access with dot-notation and square-bracket notation. You can convert response data to open structs or json (if the response format is json).
|
194
278
|
|
195
279
|
```ruby
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
#
|
280
|
+
response = LHC.request(url: 'http://datastore/entry/1')
|
281
|
+
response.data.as_open_struct #<OpenStruct name='local.ch'>
|
282
|
+
response.data.as_json # { name: 'local.ch' }
|
283
|
+
response.data.name # 'local.ch'
|
284
|
+
response.data[:name] # 'local.ch'
|
200
285
|
```
|
201
286
|
|
202
|
-
|
287
|
+
You can also access response data directly through the response object (with square bracket notation only):
|
203
288
|
|
204
289
|
```ruby
|
205
|
-
LHC.get('http://datastore/
|
206
|
-
# GET http://datastore/v2/feedbacks/123
|
290
|
+
LHC.json.get(url: 'http://datastore/entry/1')[:name]
|
207
291
|
```
|
208
292
|
|
209
|
-
|
293
|
+
## Exceptions
|
210
294
|
|
211
|
-
|
295
|
+
Anything but a response code indicating success (2xx) raises an exception.
|
212
296
|
|
213
|
-
|
297
|
+
```ruby
|
214
298
|
|
215
|
-
|
299
|
+
LHC.get('localhost') # UnknownError: 0
|
300
|
+
LHC.get('http://localhost:3000') # LHC::Timeout: 0
|
301
|
+
|
302
|
+
```
|
303
|
+
|
304
|
+
You can access the response object that was causing the error.
|
305
|
+
|
306
|
+
```ruby
|
307
|
+
LHC.get('local.ch')
|
308
|
+
rescue => e
|
309
|
+
e.response #<LHC:Response>
|
310
|
+
e.response.code # 403
|
311
|
+
e.response.timeout? # false
|
312
|
+
Rails.logger.error e
|
313
|
+
# LHC::UnknownError: get http://local.cac
|
314
|
+
# Params: {:url=>"http://local.cac", :method=>:get}
|
315
|
+
# Response Code: 0
|
316
|
+
# <Response Body>
|
317
|
+
```
|
318
|
+
|
319
|
+
All errors that are raise by LHC inherit from `LHC::Error`.
|
320
|
+
They are divided into `LHC::ClientError`, `LHC::ServerError`, `LHC::Timeout` and `LHC::UnkownError` and mapped according to the following status code.
|
321
|
+
|
322
|
+
```ruby
|
323
|
+
400 => LHC::BadRequest
|
324
|
+
401 => LHC::Unauthorized
|
325
|
+
402 => LHC::PaymentRequired
|
326
|
+
403 => LHC::Forbidden
|
327
|
+
403 => LHC::Forbidden
|
328
|
+
404 => LHC::NotFound
|
329
|
+
405 => LHC::MethodNotAllowed
|
330
|
+
406 => LHC::NotAcceptable
|
331
|
+
407 => LHC::ProxyAuthenticationRequired
|
332
|
+
408 => LHC::RequestTimeout
|
333
|
+
409 => LHC::Conflict
|
334
|
+
410 => LHC::Gone
|
335
|
+
411 => LHC::LengthRequired
|
336
|
+
412 => LHC::PreconditionFailed
|
337
|
+
413 => LHC::RequestEntityTooLarge
|
338
|
+
414 => LHC::RequestUriToLong
|
339
|
+
415 => LHC::UnsupportedMediaType
|
340
|
+
416 => LHC::RequestedRangeNotSatisfiable
|
341
|
+
417 => LHC::ExpectationFailed
|
342
|
+
422 => LHC::UnprocessableEntity
|
343
|
+
423 => LHC::Locked
|
344
|
+
424 => LHC::FailedDependency
|
345
|
+
426 => LHC::UpgradeRequired
|
346
|
+
|
347
|
+
500 => LHC::InternalServerError
|
348
|
+
501 => LHC::NotImplemented
|
349
|
+
502 => LHC::BadGateway
|
350
|
+
503 => LHC::ServiceUnavailable
|
351
|
+
504 => LHC::GatewayTimeout
|
352
|
+
505 => LHC::HttpVersionNotSupported
|
353
|
+
507 => LHC::InsufficientStorage
|
354
|
+
510 => LHC::NotExtended
|
355
|
+
|
356
|
+
timeout? => LHC::Timeout
|
357
|
+
|
358
|
+
anything_else => LHC::UnknownError
|
359
|
+
```
|
216
360
|
|
217
361
|
### Custom error handling
|
218
362
|
|
@@ -244,9 +388,438 @@ response.error_ignored? # true
|
|
244
388
|
response.request.error_ignored? # true
|
245
389
|
```
|
246
390
|
|
391
|
+
## Configuration
|
392
|
+
|
393
|
+
If you want to configure LHC, do it on initialization (like in a Rails initializer, `environment.rb` or `application.rb`), otherwise you could run into the problem that certain configurations can only be set once.
|
394
|
+
|
395
|
+
You can use `LHC.configure` to prevent the initialization problem.
|
396
|
+
Take care that you only use `LHC.configure` once, because it is actually reseting previously made configurations and applies the new once.
|
397
|
+
|
398
|
+
```ruby
|
399
|
+
|
400
|
+
LHC.configure do |c|
|
401
|
+
c.placeholder :datastore, 'http://datastore/v2'
|
402
|
+
c.endpoint :feedbacks, '{+datastore}/feedbacks', params: { has_reviews: true }
|
403
|
+
c.interceptors = [CachingInterceptor, MonitorInterceptor, TrackingIdInterceptor]
|
404
|
+
end
|
405
|
+
|
406
|
+
```
|
407
|
+
|
408
|
+
### Configuring endpoints
|
409
|
+
|
410
|
+
You can configure endpoints, for later use, by giving them a name, a url and some parameters (optional).
|
411
|
+
|
412
|
+
```ruby
|
413
|
+
LHC.configure do |c|
|
414
|
+
c.endpoint(:feedbacks, 'http://datastore/v2/feedbacks', params: { has_reviews: true })
|
415
|
+
c.endpoint(:find_feedback, 'http://datastore/v2/feedbacks/{id}')
|
416
|
+
end
|
417
|
+
|
418
|
+
LHC.get(:feedbacks) # GET http://datastore/v2/feedbacks
|
419
|
+
LHC.get(:find_feedback, params:{ id: 123 }) # GET http://datastore/v2/feedbacks/123
|
420
|
+
```
|
421
|
+
|
422
|
+
Explicit request options override configured options.
|
423
|
+
|
424
|
+
```ruby
|
425
|
+
LHC.get(:feedbacks, params: { has_reviews: false }) # Overrides configured params
|
426
|
+
```
|
427
|
+
|
428
|
+
### Configuring placeholders
|
429
|
+
|
430
|
+
You can configure global placeholders, that are used when generating urls from url-templates.
|
431
|
+
|
432
|
+
```ruby
|
433
|
+
LHC.configure do |c|
|
434
|
+
c.placeholder(:datastore, 'http://datastore/v2')
|
435
|
+
c.endpoint(:feedbacks, '{+datastore}/feedbacks', { params: { has_reviews: true } })
|
436
|
+
end
|
437
|
+
|
438
|
+
LHC.get(:feedbacks) # http://datastore/v2/feedbacks
|
439
|
+
```
|
440
|
+
|
247
441
|
## Interceptors
|
248
442
|
|
249
|
-
To monitor and manipulate the
|
443
|
+
To monitor and manipulate the HTTP communication done with LHC, you can define interceptors that follow the (Inteceptor Pattern)[https://en.wikipedia.org/wiki/Interceptor_pattern].
|
444
|
+
There are some interceptors that are part of LHC already, so called [Core Interceptors](#core-interceptors), that cover some basic usecases.
|
445
|
+
|
446
|
+
### Quick start: Configure/Enable Interceptors
|
447
|
+
|
448
|
+
```ruby
|
449
|
+
LHC.configure do |c|
|
450
|
+
c.interceptors = [LHC::Auth, LHC::Caching, LHC::DefaultTimeout, LHC::Logging, LHC::Monitoring, LHC::Prometheus, LHC::Retry, LHC::Rollbar, LHC::Zipkin]
|
451
|
+
end
|
452
|
+
```
|
453
|
+
|
454
|
+
You can only set the list of global interceptors once and you can not alter it after you set it.
|
455
|
+
|
456
|
+
### Interceptors on local request level
|
457
|
+
|
458
|
+
You can override the global list of interceptors on local request level:
|
459
|
+
|
460
|
+
```ruby
|
461
|
+
interceptors = LHC.config.interceptors
|
462
|
+
interceptors -= [LHC::Caching] # remove caching
|
463
|
+
interceptors += [LHC::Retry] # add retry
|
464
|
+
LHC.request({url: 'http://local.ch', retry: 2, interceptors: interceptors})
|
465
|
+
|
466
|
+
LHC.request({url: 'http://local.ch', interceptors: []}) # no interceptor for this request at all
|
467
|
+
```
|
468
|
+
|
469
|
+
### Core Interceptors
|
470
|
+
|
471
|
+
#### Authentication Interceptor
|
472
|
+
|
473
|
+
Add the auth interceptor to your basic set of LHC interceptors.
|
474
|
+
|
475
|
+
```ruby
|
476
|
+
LHC.configure do |c|
|
477
|
+
c.interceptors = [LHC::Auth]
|
478
|
+
end
|
479
|
+
```
|
480
|
+
|
481
|
+
##### Bearer Authentication
|
482
|
+
|
483
|
+
```ruby
|
484
|
+
LHC.get('http://local.ch', auth: { bearer: -> { access_token } })
|
485
|
+
```
|
486
|
+
|
487
|
+
Adds the following header to the request:
|
488
|
+
```
|
489
|
+
'Authorization': 'Bearer 123456'
|
490
|
+
```
|
491
|
+
|
492
|
+
Assuming the method `access_token` responds on runtime of the request with `123456`.
|
493
|
+
|
494
|
+
##### Basic Authentication
|
495
|
+
|
496
|
+
```ruby
|
497
|
+
LHC.get('http://local.ch', auth: { basic: { username: 'steve', password: 'can' } })
|
498
|
+
```
|
499
|
+
|
500
|
+
Adds the following header to the request:
|
501
|
+
```
|
502
|
+
'Authorization': 'Basic c3RldmU6Y2Fu'
|
503
|
+
```
|
504
|
+
|
505
|
+
Which is the base64 encoded credentials "username:password".
|
506
|
+
|
507
|
+
##### Reauthenticate
|
508
|
+
|
509
|
+
The current implementation can only offer reauthenticate for _client access tokens_. For this to work the following has to be given:
|
510
|
+
|
511
|
+
* You have configured and implemented `LHC::Auth.refresh_client_token = -> { TokenRefreshUtil.client_access_token(true) }` which when called will force a refresh of the token and return the new value. It is also expected that this implementation will handle invalidating caches if necessary.
|
512
|
+
* Your interceptors contain `LHC::Auth` and `LHC::Retry`, whereas `LHC::Retry` comes _after_ `LHC::Auth` in the chain.
|
513
|
+
|
514
|
+
##### Bearer Authentication with client access token
|
515
|
+
|
516
|
+
Reauthentication will be initiated if:
|
517
|
+
|
518
|
+
* setup is correct
|
519
|
+
* `response.success?` is false and an `LHC::Unauthorized` was observed
|
520
|
+
* reauthentication wasn't already attempted once
|
521
|
+
|
522
|
+
If this is the case, this happens:
|
523
|
+
|
524
|
+
* refresh the client token, by calling `refresh_client_token`
|
525
|
+
* the authentication header will be updated with the new token
|
526
|
+
* `LHC::Retry` will be triggered by adding `retry: { max: 1 }` to the request options
|
527
|
+
|
528
|
+
#### Caching Interceptor
|
529
|
+
|
530
|
+
Add the cache interceptor to your basic set of LHC interceptors.
|
531
|
+
|
532
|
+
```ruby
|
533
|
+
LHC.configure do |c|
|
534
|
+
c.interceptors = [LHC::Caching]
|
535
|
+
end
|
536
|
+
```
|
537
|
+
|
538
|
+
You can configure your own cache (default Rails.cache) and logger (default Rails.logger):
|
539
|
+
|
540
|
+
```ruby
|
541
|
+
LHC::Caching.cache = ActiveSupport::Cache::MemoryStore.new
|
542
|
+
LHC::Caching.logger = Logger.new(STDOUT)
|
543
|
+
```
|
544
|
+
|
545
|
+
Caching is not enabled by default, although you added it to your basic set of interceptors.
|
546
|
+
If you want to have requests served/stored and stored in/from cache, you have to enable it by request.
|
547
|
+
|
548
|
+
```ruby
|
549
|
+
LHC.get('http://local.ch', cache: true)
|
550
|
+
```
|
551
|
+
|
552
|
+
You can also enable caching when configuring an endpoint in LHS.
|
553
|
+
|
554
|
+
```ruby
|
555
|
+
class Feedbacks < LHS::Service
|
556
|
+
endpoint '{+datastore}/v2/feedbacks', cache: true
|
557
|
+
end
|
558
|
+
```
|
559
|
+
|
560
|
+
Only GET requests are cached by default. If you want to cache any other request method, just configure it:
|
561
|
+
|
562
|
+
```ruby
|
563
|
+
LHC.get('http://local.ch', cache: { methods: [:get] })
|
564
|
+
```
|
565
|
+
|
566
|
+
Responses served from cache are marked as served from cache:
|
567
|
+
|
568
|
+
```ruby
|
569
|
+
response = LHC.get('http://local.ch', cache: true)
|
570
|
+
response.from_cache? # true
|
571
|
+
```
|
572
|
+
|
573
|
+
##### Options
|
574
|
+
|
575
|
+
```ruby
|
576
|
+
LHC.get('http://local.ch', cache: { key: 'key' expires_in: 1.day, race_condition_ttl: 15.seconds, use: ActiveSupport::Cache::MemoryStore.new })
|
577
|
+
```
|
578
|
+
|
579
|
+
`expires_in` - lets the cache expires every X seconds.
|
580
|
+
|
581
|
+
`key` - Set the key that is used for caching by using the option. Every key is prefixed with `LHC_CACHE(v1): `.
|
582
|
+
|
583
|
+
`race_condition_ttl` - very useful in situations where a cache entry is used very frequently and is under heavy load.
|
584
|
+
If a cache expires and due to heavy load several different processes will try to read data natively and then they all will try to write to cache.
|
585
|
+
To avoid that case the first process to find an expired cache entry will bump the cache expiration time by the value set in `cache_race_condition_ttl`.
|
586
|
+
|
587
|
+
`use` - Set an explicit cache to be used for this request. If this option is missing `LHC::Caching.cache` is used.
|
588
|
+
|
589
|
+
##### Testing
|
590
|
+
|
591
|
+
Add to your spec_helper.rb:
|
592
|
+
|
593
|
+
```ruby
|
594
|
+
require 'lhc/test/cache_helper.rb'
|
595
|
+
```
|
596
|
+
|
597
|
+
This will initialize a MemoryStore cache for LHC::Caching interceptor and resets the cache before every test.
|
598
|
+
|
599
|
+
#### Default Timeout Interceptor
|
600
|
+
|
601
|
+
Applies default timeout values to all requests made in an application, that uses LHC.
|
602
|
+
|
603
|
+
```ruby
|
604
|
+
LHC.configure do |c|
|
605
|
+
c.interceptors = [LHC::DefaultTimeout]
|
606
|
+
end
|
607
|
+
```
|
608
|
+
|
609
|
+
`timeout` default: 15 seconds
|
610
|
+
`connecttimeout` default: 2 seconds
|
611
|
+
|
612
|
+
##### Overwrite defaults
|
613
|
+
|
614
|
+
```ruby
|
615
|
+
LHC::DefaultTimeout.timeout = 5 # seconds
|
616
|
+
LHC::DefaultTimeout.connecttimeout = 3 # seconds
|
617
|
+
```
|
618
|
+
|
619
|
+
#### Logging Interceptor
|
620
|
+
|
621
|
+
The logging interceptor logs all requests done with LHC to the Rails logs.
|
622
|
+
|
623
|
+
##### Installation
|
624
|
+
|
625
|
+
```ruby
|
626
|
+
LHC.configure do |c|
|
627
|
+
c.interceptors = [LHC::Logging]
|
628
|
+
end
|
629
|
+
|
630
|
+
LHC::Logging.logger = Rails.logger
|
631
|
+
```
|
632
|
+
|
633
|
+
##### What and how it logs
|
634
|
+
|
635
|
+
The logging Interceptor logs basic information about the request and the response:
|
636
|
+
|
637
|
+
```ruby
|
638
|
+
LHC.get('http://local.ch')
|
639
|
+
# Before LHC request<70128730317500> GET http://local.ch at 2018-05-23T07:53:19+02:00 Params={} Headers={\"User-Agent\"=>\"Typhoeus - https://github.com/typhoeus/typhoeus\", \"Expect\"=>\"\"}
|
640
|
+
# After LHC response for request<70128730317500>: GET http://local.ch at 2018-05-23T07:53:28+02:00 Time=0ms URL=http://local.ch:80/
|
641
|
+
```
|
642
|
+
|
643
|
+
##### Configure
|
644
|
+
|
645
|
+
You can configure the logger beeing used by the logging interceptor:
|
646
|
+
|
647
|
+
```ruby
|
648
|
+
LHC::Logging.logger = Another::Logger
|
649
|
+
```
|
650
|
+
|
651
|
+
#### Monitoring Interceptor
|
652
|
+
|
653
|
+
The monitoring interceptor reports all requests done with LHC to a given StatsD instance.
|
654
|
+
|
655
|
+
##### Installation
|
656
|
+
|
657
|
+
```ruby
|
658
|
+
LHC.configure do |c|
|
659
|
+
c.interceptors = [LHC::Monitoring]
|
660
|
+
end
|
661
|
+
```
|
662
|
+
|
663
|
+
You also have to configure statsd in order to have the monitoring interceptor report.
|
664
|
+
|
665
|
+
```ruby
|
666
|
+
LHC::Monitoring.statsd = <your-instance-of-statsd>
|
667
|
+
```
|
668
|
+
|
669
|
+
##### Environment
|
670
|
+
|
671
|
+
By default, the monitoring interceptor uses Rails.env to determine the environment. In case you want to configure that, use:
|
672
|
+
|
673
|
+
```ruby
|
674
|
+
LHC::Monitoring.env = ENV['DEPLOYMENT_TYPE'] || Rails.env
|
675
|
+
```
|
676
|
+
|
677
|
+
##### What it tracks
|
678
|
+
|
679
|
+
It tracks request attempts with `before_request` and `after_request` (counts).
|
680
|
+
|
681
|
+
In case your workers/processes are getting killed due limited time constraints,
|
682
|
+
you are able to detect deltas with relying on "before_request", and "after_request" counts:
|
683
|
+
|
684
|
+
```ruby
|
685
|
+
"lhc.<app_name>.<env>.<host>.<http_method>.before_request", 1
|
686
|
+
"lhc.<app_name>.<env>.<host>.<http_method>.after_request", 1
|
687
|
+
```
|
688
|
+
|
689
|
+
In case of a successful response it reports the response code with a count and the response time with a gauge value.
|
690
|
+
|
691
|
+
```ruby
|
692
|
+
LHC.get('http://local.ch')
|
693
|
+
|
694
|
+
"lhc.<app_name>.<env>.<host>.<http_method>.count", 1
|
695
|
+
"lhc.<app_name>.<env>.<host>.<http_method>.200", 1
|
696
|
+
"lhc.<app_name>.<env>.<host>.<http_method>.time", 43
|
697
|
+
```
|
698
|
+
|
699
|
+
Timeouts are also reported:
|
700
|
+
|
701
|
+
```ruby
|
702
|
+
"lhc.<app_name>.<env>.<host>.<http_method>.timeout", 1
|
703
|
+
```
|
704
|
+
|
705
|
+
All the dots in the host are getting replaced with underscore, because dot is the default separator in graphite.
|
706
|
+
|
707
|
+
##### Configure
|
708
|
+
|
709
|
+
It is possible to set the key for Monitoring Interceptor on per request basis:
|
710
|
+
|
711
|
+
```ruby
|
712
|
+
LHC.get('http://local.ch', monitoring_key: 'local_website')
|
713
|
+
|
714
|
+
"local_website.count", 1
|
715
|
+
"local_website.200", 1
|
716
|
+
"local_website.time", 43
|
717
|
+
```
|
718
|
+
|
719
|
+
If you use this approach you need to add all namespaces (app, environment etc.) to the key on your own.
|
720
|
+
|
721
|
+
|
722
|
+
#### Prometheus Interceptor
|
723
|
+
|
724
|
+
Logs basic request/response information to prometheus.
|
725
|
+
|
726
|
+
```ruby
|
727
|
+
require 'prometheus/client'
|
728
|
+
|
729
|
+
LHC.configure do |c|
|
730
|
+
c.interceptors = [LHC::Prometheus]
|
731
|
+
end
|
732
|
+
|
733
|
+
LHC::Prometheus.client = Prometheus::Client
|
734
|
+
LHC::Prometheus.namespace = 'web_location_app'
|
735
|
+
```
|
736
|
+
|
737
|
+
```ruby
|
738
|
+
LHC.get('http://local.ch')
|
739
|
+
```
|
740
|
+
|
741
|
+
- Creates a prometheus counter that receives additional meta information for: `:code`, `:success` and `:timeout`.
|
742
|
+
|
743
|
+
- Creates a prometheus histogram for response times in milliseconds.
|
744
|
+
|
745
|
+
|
746
|
+
#### Retry Interceptor
|
747
|
+
|
748
|
+
If you enable the retry interceptor, you can have LHC retry requests for you:
|
749
|
+
|
750
|
+
```ruby
|
751
|
+
LHC.configure do |c|
|
752
|
+
c.interceptors = [LHC::Retry]
|
753
|
+
end
|
754
|
+
|
755
|
+
response = LHC.get('http://local.ch', retry: true)
|
756
|
+
```
|
757
|
+
|
758
|
+
It will try to retry the request up to 3 times (default) internally, before it passes the last response back, or raises an error for the last response.
|
759
|
+
|
760
|
+
Consider, that all other interceptors will run for every single retry.
|
761
|
+
|
762
|
+
##### Limit the amount of retries while making the request
|
763
|
+
|
764
|
+
```ruby
|
765
|
+
LHC.get('http://local.ch', retry: { max: 1 })
|
766
|
+
```
|
767
|
+
|
768
|
+
##### Change the default maximum of retries of the retry interceptor
|
769
|
+
|
770
|
+
```ruby
|
771
|
+
LHC::Retry.max = 3
|
772
|
+
```
|
773
|
+
|
774
|
+
#### Rollbar Interceptor
|
775
|
+
|
776
|
+
Forward errors to rollbar when exceptions occur during http requests.
|
777
|
+
|
778
|
+
```ruby
|
779
|
+
LHC.configure do |c|
|
780
|
+
c.interceptors = [LHC::Rollbar]
|
781
|
+
end
|
782
|
+
```
|
783
|
+
|
784
|
+
```ruby
|
785
|
+
LHC.get('http://local.ch')
|
786
|
+
```
|
787
|
+
|
788
|
+
If it raises, it forwards the request and response object to rollbar, which contain all necessary data.
|
789
|
+
|
790
|
+
##### Forward additional parameters
|
791
|
+
|
792
|
+
```ruby
|
793
|
+
LHC.get('http://local.ch', rollbar: { tracking_key: 'this particular request' })
|
794
|
+
```
|
795
|
+
|
796
|
+
#### Zipkin
|
797
|
+
|
798
|
+
Zipkin is a distributed tracing system. It helps gather timing data needed to troubleshoot latency problems in microservice architectures [Zipkin Distributed Tracing](https://zipkin.io/).
|
799
|
+
|
800
|
+
Add the zipkin interceptor to your basic set of LHC interceptors.
|
801
|
+
|
802
|
+
```ruby
|
803
|
+
LHC.configure do |c|
|
804
|
+
c.interceptors = [LHC::Zipkin]
|
805
|
+
end
|
806
|
+
```
|
807
|
+
|
808
|
+
The following configuration needs to happen in the application that wants to run this interceptor:
|
809
|
+
|
810
|
+
1. Add `gem 'zipkin-tracer'` to your Gemfile.
|
811
|
+
2. Add the necessary Rack middleware and configuration
|
812
|
+
|
813
|
+
```ruby
|
814
|
+
config.middleware.use ZipkinTracer::RackHandler, {
|
815
|
+
service_name: 'service-name', # name your service will be known as in zipkin
|
816
|
+
service_port: 80, # the port information that is sent along the trace
|
817
|
+
json_api_host: 'http://zipkin-collector', # the zipkin endpoint
|
818
|
+
sample_rate: 1 # sample rate, where 1 = 100% of all requests, and 0.1 is 10% of all requests
|
819
|
+
}
|
820
|
+
```
|
821
|
+
|
822
|
+
### Create an interceptor from scratch
|
250
823
|
|
251
824
|
```ruby
|
252
825
|
class TrackingIdInterceptor < LHC::Interceptor
|
@@ -257,13 +830,56 @@ To monitor and manipulate the http communication done with LHC, you can define i
|
|
257
830
|
end
|
258
831
|
```
|
259
832
|
|
260
|
-
|
833
|
+
```ruby
|
834
|
+
LHC.configure do |c|
|
835
|
+
c.interceptors = [TrackingIdInterceptor]
|
836
|
+
end
|
837
|
+
```
|
261
838
|
|
262
|
-
|
839
|
+
#### Interceptor callbacks
|
840
|
+
|
841
|
+
`before_raw_request` is called before the raw typhoeus request is prepared/created.
|
842
|
+
|
843
|
+
`before_request` is called when the request is prepared and about to be executed.
|
844
|
+
|
845
|
+
`after_request` is called after request was started.
|
846
|
+
|
847
|
+
`before_response` is called when response started to arrive.
|
848
|
+
|
849
|
+
`after_response` is called after the response arrived completely.
|
850
|
+
|
851
|
+
|
852
|
+
#### Interceptor request/response
|
853
|
+
|
854
|
+
Every interceptor can directly access their instance [request](#request) or [response](#response).
|
855
|
+
|
856
|
+
#### Provide a response replacement through an interceptor
|
263
857
|
|
264
|
-
|
858
|
+
Inside an interceptor, you are able to provide a response, rather then doing a real request.
|
859
|
+
This is useful for implementing e.g. caching.
|
265
860
|
|
266
|
-
|
861
|
+
```ruby
|
862
|
+
class LHC::Cache < LHC::Interceptor
|
863
|
+
|
864
|
+
def before_request(request)
|
865
|
+
cached_response = Rails.cache.fetch(request.url)
|
866
|
+
return LHC::Response.new(cached_response) if cached_response
|
867
|
+
end
|
868
|
+
end
|
869
|
+
```
|
870
|
+
|
871
|
+
Take care that having more than one interceptor trying to return a response will cause an exception.
|
872
|
+
You can access the request.response to identify if a response was already provided by another interceptor.
|
873
|
+
|
874
|
+
```ruby
|
875
|
+
class RemoteCacheInterceptor < LHC::Interceptor
|
876
|
+
|
877
|
+
def before_request(request)
|
878
|
+
return unless request.response.nil?
|
879
|
+
return LHC::Response.new(remote_cache)
|
880
|
+
end
|
881
|
+
end
|
882
|
+
```
|
267
883
|
|
268
884
|
## License
|
269
885
|
|