lhc 9.4.0 → 9.4.1
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/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
|
|