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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2716d5e639b506ed4d3b10782a61a8c326463b5941e1b33ba9c5759ea51e9566
4
- data.tar.gz: 535d64143094e6b005ae9f0c161fb9d046c610778d3204d4024c4cf3b4d54451
3
+ metadata.gz: 153c47a835c9f4f7e4ac644c18820bc4bb1f87a1f09b5321179fee621bfb42b0
4
+ data.tar.gz: 4995637bddf13e0fb48a97d2bb3c592a1ee1346675d38cc219d1f405d93cd35d
5
5
  SHA512:
6
- metadata.gz: c0471c962b62778ba637ae90ef90671c88510c6f4c9ad5e10024dae82f00266edb1d88fdd10395c6e8bd6dbca2da98c2717ccff09a79d0db05afa09eef6307f5
7
- data.tar.gz: c2ef427484136c827a507d30964c0db9b32174286324db1ba13a6b3c4d180b09b496ea720c9da092c8ecfce2e71dd6dd774b351078957482d3267caae7889a39
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 easy data access.
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 Start Guide
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 # String
15
- response.headers # Hash
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
- ## Formats: like json etc.
95
+ ## Request
25
96
 
26
- You can use any of the basic methods in combination with a format like `json`:
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.json.get(options)
30
- ```
100
+ response = LHC.request(url: 'http://local.ch', method: :options)
31
101
 
32
- Currently supported formats: `json`
102
+ response.request.response #<LHC::Response> the associated response.
33
103
 
34
- 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:
104
+ response.request.options #<Hash> the options used for creating the request.
35
105
 
36
- ```ruby
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
- ## A request from scratch
108
+ response.request.headers # access request headers
44
109
 
45
- ```ruby
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 = LHC.request(url: 'http://datastore/v2/feedbacks', method: :get)
50
- response.data
112
+ response.request.method #<Symbol> provides the used http-method
51
113
  ```
52
114
 
53
- [Read more about the request object](docs/request.md)
54
-
55
- → [Read more about the response object](docs/response.md)
56
-
57
- ## Accessing data
115
+ ### Formats
58
116
 
59
- 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).
117
+ You can use any of the basic methods in combination with a format like `json`:
60
118
 
61
119
  ```ruby
62
- response = LHC.request(url: 'http://datastore/entry/1')
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
- You can also access response data directly through the response object (with square bracket notation only):
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
- LHC.json.get(url: 'http://datastore/entry/1')[:name]
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
- ## Parallel requests
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
- ## Follow redirects
151
+ ### Follow redirects
93
152
 
94
153
  ```ruby
95
154
  LHC.get('http://local.ch', followlocation: true)
96
155
  ```
97
156
 
98
- ## Transfer data through the body
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
- ## Parameter
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 parameter yourself:
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
- ### Array Parameter Encoding
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
- ## Encoding
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
- ## Configuration
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
- You can configure global endpoints, placeholders and interceptors.
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.placeholder :datastore, 'http://datastore/v2'
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
- [Read more about configuration](docs/configuration.md)
239
+ If you miss to provide a parameter that is part of the url-template, it will raise an exception.
173
240
 
174
- ## Timeout
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](docs/interceptors/default_timeout.md) that lets you apply default timeout values to all the requests that you are performig in your application.
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
- ## URL-Templates
256
+ ## Response
190
257
 
191
- Instead of using concrete urls you can also use url-templates that contain placeholders.
192
- This is especially handy for configuring an endpoint once and generate the url from the params when doing the request.
193
- Since version `7.0` url templates follow the [RFC 6750](https://tools.ietf.org/html/rfc6570).
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
- url = 'http://datastore/v2/feedbacks/{id}'
197
- LHC.config.endpoint(:find_feedback, url, options)
198
- LHC.get(:find_feedback, params:{ id: 123 })
199
- # GET http://datastore/v2/feedbacks/123
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
- This also works in place without configuring an endpoint.
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/v2/feedbacks/{id}', params:{ id: 123 })
206
- # GET http://datastore/v2/feedbacks/123
290
+ LHC.json.get(url: 'http://datastore/entry/1')[:name]
207
291
  ```
208
292
 
209
- If you miss to provide a parameter that is part of the url-template, it will raise an exception.
293
+ ## Exceptions
210
294
 
211
- ## Exception handling
295
+ Anything but a response code indicating success (2xx) raises an exception.
212
296
 
213
- Anything but a response code indicating success (2**) throws an exception.
297
+ ```ruby
214
298
 
215
- [Read more about exceptions](docs/exceptions.md)
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 http communication done with LHC, you can define interceptors.
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
- → [Read more about interceptors](docs/interceptors.md)
833
+ ```ruby
834
+ LHC.configure do |c|
835
+ c.interceptors = [TrackingIdInterceptor]
836
+ end
837
+ ```
261
838
 
262
- ### Core Interceptors
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
- There are some interceptors that are part of LHC already, that cover some basic usecases:
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
- [Available Core Interceptors](/docs/interceptors)
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