right_support 2.10.1 → 2.11.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/right_support/data/token.rb +51 -0
- data/lib/right_support/data.rb +1 -0
- data/lib/right_support/net/lb/base.rb +96 -0
- data/lib/right_support/net/lb/health_check.rb +22 -46
- data/lib/right_support/net/lb/round_robin.rb +5 -23
- data/lib/right_support/net/lb/sticky.rb +10 -27
- data/lib/right_support/net/lb.rb +2 -1
- data/lib/right_support/net/request_balancer.rb +184 -60
- data/lib/right_support/rack/request_logger.rb +221 -42
- data/lib/right_support/rack/request_tracker.rb +112 -25
- data/right_support.gemspec +11 -6
- data/spec/data/token_spec.rb +21 -0
- data/spec/net/{balancing → lb}/health_check_spec.rb +56 -21
- data/spec/net/{balancing → lb}/round_robin_spec.rb +0 -0
- data/spec/net/{balancing/sticky_policy_spec.rb → lb/sticky_spec.rb} +1 -1
- data/spec/net/request_balancer_spec.rb +161 -57
- data/spec/rack/request_logger_spec.rb +91 -27
- data/spec/rack/request_tracker_spec.rb +111 -1
- metadata +8 -5
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2012 RightScale Inc
|
2
|
+
# Copyright (c) 2012-2016 RightScale Inc
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -35,20 +35,60 @@ module RightSupport::Rack
|
|
35
35
|
# with Rack::Logger or another middleware that provides logging services.
|
36
36
|
class RequestLogger
|
37
37
|
|
38
|
-
#
|
39
|
-
#
|
40
|
-
|
41
|
-
|
38
|
+
# limit exception backtrace length; usually only the first few lines are
|
39
|
+
# significant. the same error dumped repeatedly can make grepping difficult.
|
40
|
+
BACKTRACE_LIMIT = 10
|
41
|
+
|
42
|
+
# for debug-mode only logging. debug-mode can also be enabled by the
|
43
|
+
# "X-Debug=<anything>" header.
|
44
|
+
DEBUG_MODE_ENV_KEY = 'rack.debug-mode'.freeze
|
45
|
+
DEBUG_MODE = ::ENV['DEBUG_MODE'] == 'true'
|
46
|
+
DEBUG_MODE_HTTP_HEADER = 'HTTP_X_DEBUG'
|
47
|
+
|
48
|
+
# defaults to STDERR by Rack but usually overridden to use syslog, etc.
|
49
|
+
RACK_LOGGER = 'rack.logger'.freeze
|
50
|
+
|
51
|
+
# indicates whether current request is enabled for logging by filter, etc.
|
52
|
+
RACK_LOGGING_ENABLED = 'rack.logging.enabled'.freeze
|
53
|
+
|
54
|
+
# list of status codes that are 'normalized' when :normalize_40x=true
|
55
|
+
# normalization means stripping any developer-provided details to avoid
|
56
|
+
# leaking information about the service. overridden by DEBUG_MODE=true but
|
57
|
+
# specifically *not* affected by the 'X-Debug' header.
|
58
|
+
NORMALIZE_40X = [401, 403, 404]
|
59
|
+
|
60
|
+
# Initialize an instance of the middleware, optionally providing a whitelist
|
61
|
+
# (:only) or a blacklist (:except) of path regexps to which request logging
|
62
|
+
# will apply.
|
42
63
|
#
|
43
|
-
# @
|
44
|
-
#
|
45
|
-
# @
|
64
|
+
# @param [Object] app inner application or middleware layer; must respond to
|
65
|
+
# #call.
|
66
|
+
# @param [Hash] options
|
67
|
+
# @option options [Array] :only log for these path Regexps unless in debug
|
68
|
+
# mode.
|
69
|
+
# @option options [Array] :except log except for these path Regexps unless
|
70
|
+
# in debug mode; this option is ignored if :only is provided.
|
71
|
+
# @option options [Array] :include_query_string log the contents of the
|
72
|
+
# query string.
|
73
|
+
# @option options [Logger] :logger to override any previously set logger or
|
74
|
+
# nil. Rack sets a STDERR logger by default or it may be set by other
|
75
|
+
# framework middleware so this is only for when those are not used.
|
76
|
+
# @option options [TrueClass|FalseClass] :normalize_40x to remove detail
|
77
|
+
# from some 40x responses for security reasons. default=false.
|
78
|
+
# @option options [TrueClass|FalseClass] :normalize_40x_set_cookie to
|
79
|
+
# override the usual global session behavior of always responding with a
|
80
|
+
# 'Set-Cookie' header regardless of status code. defaults to having the
|
81
|
+
# same value as the :normalize_40x option.
|
46
82
|
def initialize(app, options = {})
|
47
83
|
@app = app
|
48
84
|
@only = options[:only] || []
|
49
85
|
@except = options[:except] || []
|
50
86
|
# by default we don't log the query string contents because it may have sensitive data.
|
51
87
|
@include_query_string = options[:include_query_string] || false
|
88
|
+
@logger = options[:logger]
|
89
|
+
@normalize_40x = !!options[:normalize_40x]
|
90
|
+
@normalize_40x_set_cookie = options[:normalize_40x_set_cookie]
|
91
|
+
@normalize_40x_set_cookie = @normalize_40x if @normalize_40x_set_cookie.nil?
|
52
92
|
end
|
53
93
|
|
54
94
|
# Add a logger to the Rack environment and call the next middleware.
|
@@ -67,25 +107,154 @@ module RightSupport::Rack
|
|
67
107
|
# @return [Object] always returns whatever value is returned by the next
|
68
108
|
# layer of middleware
|
69
109
|
def call(env)
|
70
|
-
|
71
|
-
|
110
|
+
# forward-declare in case of exception.
|
111
|
+
enabled = true
|
72
112
|
|
113
|
+
# override logger, if necessary.
|
114
|
+
if @logger
|
115
|
+
logger = env[RACK_LOGGER] = @logger
|
116
|
+
else
|
117
|
+
logger = env[RACK_LOGGER]
|
118
|
+
end
|
119
|
+
|
120
|
+
# determine if debug-mode is enabled.
|
121
|
+
env[DEBUG_MODE_ENV_KEY] = debug_enabled?(env)
|
122
|
+
|
123
|
+
# log request only when enabled.
|
124
|
+
env[RACK_LOGGING_ENABLED] = enabled = logging_enabled?(logger, env)
|
73
125
|
began_at = Time.now
|
74
126
|
log_request_begin(logger, env) if enabled
|
75
|
-
|
127
|
+
|
128
|
+
# next
|
129
|
+
status, headers, body = @app.call(env)
|
130
|
+
|
131
|
+
# log exception, if necessary.
|
76
132
|
if env['sinatra.error'] && !env['sinatra.error.handled']
|
133
|
+
if !enabled
|
134
|
+
# after the fact but better than logging exception without its request.
|
135
|
+
log_request_begin(logger, env)
|
136
|
+
enabled = true
|
137
|
+
end
|
77
138
|
log_exception(logger, env['sinatra.error'])
|
139
|
+
elsif !enabled && env[RACK_LOGGING_ENABLED]
|
140
|
+
# controller can conditionally enable logging for the current request
|
141
|
+
# even though by default such requests are not logged. an example is a
|
142
|
+
# periodic request for a listing (scheduled tasks, etc.) that is only
|
143
|
+
# logged when not empty.
|
144
|
+
log_request_begin(logger, env)
|
145
|
+
enabled = true
|
78
146
|
end
|
79
|
-
log_request_end(logger, env, status, header, body, began_at) if enabled
|
80
147
|
|
81
|
-
|
148
|
+
# respond identically to some 40Xs; do not reveal any internals by
|
149
|
+
# varying the response headers or body (as developers tend to do for
|
150
|
+
# debugging reasons). public APIs will receive such requests frequently
|
151
|
+
# but this is generally not needed for internal APIs.
|
152
|
+
#
|
153
|
+
# note that it is important to *not* accept the "X-Debug" header override
|
154
|
+
# and show more information in response ;)
|
155
|
+
if @normalize_40x && !DEBUG_MODE && NORMALIZE_40X.include?(status)
|
156
|
+
# note that ::Rack::CONTENT_LENGTH was not defined before rack v1.6+ :@
|
157
|
+
headers = { 'Content-Length' => '0' }
|
158
|
+
body = []
|
159
|
+
env[DEBUG_MODE_ENV_KEY] = false # disable any verbose debugging
|
160
|
+
end
|
161
|
+
|
162
|
+
# defeat global session renew, update and set-cookie for normalized 40x.
|
163
|
+
# has no effect if the app is not using global session middleware.
|
164
|
+
if @normalize_40x_set_cookie && NORMALIZE_40X.include?(status)
|
165
|
+
env['global_session.req.renew'] = false
|
166
|
+
env['global_session.req.update'] = false
|
167
|
+
end
|
168
|
+
|
169
|
+
# log response only when enabled.
|
170
|
+
if enabled
|
171
|
+
log_request_end(logger, env, status, headers, body, began_at)
|
172
|
+
end
|
173
|
+
|
174
|
+
return [status, headers, body]
|
82
175
|
rescue Exception => e
|
176
|
+
if !enabled
|
177
|
+
# after the fact but better than logging exception without its request.
|
178
|
+
log_request_begin(logger, env)
|
179
|
+
end
|
83
180
|
log_exception(logger, e)
|
84
181
|
raise
|
85
182
|
end
|
86
183
|
|
184
|
+
# provides a canonical representation of a header key that is acceptable to
|
185
|
+
# Rack, etc.
|
186
|
+
def self.canonicalize_header_key(key)
|
187
|
+
key.downcase.gsub('_', '-')
|
188
|
+
end
|
189
|
+
|
190
|
+
# @return [String] header value by canonical key name, if present.
|
191
|
+
def self.header_value_get(headers, key)
|
192
|
+
return nil unless headers
|
193
|
+
key = canonicalize_header_key(key)
|
194
|
+
value = nil
|
195
|
+
headers.each do |k, v|
|
196
|
+
if canonicalize_header_key(k) == key
|
197
|
+
value = v
|
198
|
+
break
|
199
|
+
end
|
200
|
+
end
|
201
|
+
value
|
202
|
+
end
|
203
|
+
|
204
|
+
# safely sets a header value by first locating any existing key by canonical
|
205
|
+
# search.
|
206
|
+
#
|
207
|
+
# @param [Hash] headers to modify
|
208
|
+
# @param [String] key for header
|
209
|
+
# @param [String] value for header
|
210
|
+
# @return [Hash] updated headers
|
211
|
+
def self.header_value_set(headers, key, value)
|
212
|
+
headers ||= {}
|
213
|
+
key = canonicalize_header_key(key)
|
214
|
+
headers.each do |k, v|
|
215
|
+
if canonicalize_header_key(k) == key
|
216
|
+
key = k
|
217
|
+
break
|
218
|
+
end
|
219
|
+
end
|
220
|
+
headers[key] = value
|
221
|
+
headers
|
222
|
+
end
|
223
|
+
|
224
|
+
# Formats a concise error message with limited backtrace, etc.
|
225
|
+
#
|
226
|
+
# @param [Exception] e to format
|
227
|
+
#
|
228
|
+
# @return [String] formatted error
|
229
|
+
def self.format_exception_message(e)
|
230
|
+
trace = e.backtrace || []
|
231
|
+
if trace.size > BACKTRACE_LIMIT
|
232
|
+
trace = trace[0, BACKTRACE_LIMIT] << '...'
|
233
|
+
end
|
234
|
+
kind = e.class.name
|
235
|
+
if (msg = e.message) && !msg.empty? && msg != kind
|
236
|
+
kind = "#{kind}: #{msg}"
|
237
|
+
end
|
238
|
+
[kind, *trace].join("\n")
|
239
|
+
end
|
240
|
+
|
241
|
+
# @return [String] accepted/generated request UUID or else 'missing' to
|
242
|
+
# indicate that the RequestTracker middleware is missing.
|
243
|
+
def self.request_uuid_from_env(env)
|
244
|
+
env[RequestTracker::REQUEST_UUID_ENV_NAME] || 'missing'
|
245
|
+
end
|
246
|
+
|
87
247
|
private
|
88
248
|
|
249
|
+
# determines if debug-mode is enabled for the current request by environment
|
250
|
+
# variable override or by specific X-Debug header in request.
|
251
|
+
#
|
252
|
+
# note that the legacy code accepted any value for the debug header so long
|
253
|
+
# as it was not nil.
|
254
|
+
def debug_enabled?(env)
|
255
|
+
DEBUG_MODE || !env[DEBUG_MODE_HTTP_HEADER].nil?
|
256
|
+
end
|
257
|
+
|
89
258
|
# Determine whether logging enabled for given request
|
90
259
|
#
|
91
260
|
# @param [Object] logger for Rack
|
@@ -96,7 +265,7 @@ module RightSupport::Rack
|
|
96
265
|
path = env["PATH_INFO"]
|
97
266
|
|
98
267
|
# the request can always ask for debugging
|
99
|
-
if env[
|
268
|
+
if env[DEBUG_MODE_ENV_KEY]
|
100
269
|
true
|
101
270
|
# some apps have a blacklist of boring requests (e.g. health checks)
|
102
271
|
elsif @except.any? { |e| e.is_a?(Regexp) ? (path =~ e) : (path == e) }
|
@@ -110,6 +279,12 @@ module RightSupport::Rack
|
|
110
279
|
end
|
111
280
|
end
|
112
281
|
|
282
|
+
# @return [String] accepted/generated request UUID or else 'missing' to
|
283
|
+
# indicate that the RequestTracker middleware is missing.
|
284
|
+
def request_uuid_from_env(env)
|
285
|
+
self.class.request_uuid_from_env(env)
|
286
|
+
end
|
287
|
+
|
113
288
|
# Log beginning of request
|
114
289
|
#
|
115
290
|
# @param [Object] logger for Rack
|
@@ -137,24 +312,20 @@ module RightSupport::Rack
|
|
137
312
|
cookie = env['global_session']
|
138
313
|
info = [ env['global_session'].id,
|
139
314
|
cookie.keys.map{|k| %{"#{k}"=>"#{cookie[k]}"} }.join(', ') ]
|
140
|
-
sess = %Q{Session ID: %s
|
315
|
+
sess = %Q{Session ID: %s, Session Data: {%s}, } % info
|
141
316
|
else
|
142
317
|
sess = ""
|
143
318
|
end
|
144
319
|
|
145
|
-
|
146
|
-
|
147
|
-
params = [
|
320
|
+
logger.info '[%s] Started %s "%s%s" (for %s), %sShard: %s' % [
|
321
|
+
request_uuid_from_env(env),
|
148
322
|
env['REQUEST_METHOD'],
|
149
323
|
env['PATH_INFO'],
|
150
324
|
query_info,
|
151
325
|
remote_addr,
|
152
326
|
sess,
|
153
|
-
|
154
|
-
env['rack.request_uuid'] || ''
|
327
|
+
env['HTTP_X_SHARD'] || 'default'
|
155
328
|
]
|
156
|
-
|
157
|
-
logger.info 'Processing %s "%s%s" (for %s) %s %s Request ID: %s' % params
|
158
329
|
end
|
159
330
|
|
160
331
|
# Log end of request
|
@@ -167,30 +338,39 @@ module RightSupport::Rack
|
|
167
338
|
# @return [TrueClass] always true
|
168
339
|
def log_request_end(logger, env, status, headers, body, began_at)
|
169
340
|
duration = (Time.now - began_at) * 1000
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
else
|
177
|
-
if body.is_a?(Array)
|
178
|
-
body.reduce(0) {|accum, e| accum += e.bytesize}
|
179
|
-
elsif body.is_a?(String)
|
180
|
-
body.bytesize
|
341
|
+
unless content_length = self.class.header_value_get(headers, 'Content-Length')
|
342
|
+
case body
|
343
|
+
when Array
|
344
|
+
content_length = body.reduce(0) {|accum, e| accum += e.bytesize}
|
345
|
+
when String
|
346
|
+
content_length = body.bytesize
|
181
347
|
else
|
182
|
-
'-'
|
348
|
+
content_length = '-'
|
183
349
|
end
|
184
350
|
end
|
185
351
|
|
186
|
-
|
352
|
+
request_uuid = request_uuid_from_env(env)
|
353
|
+
logger.info '[%s] Completed in %dms | %s | %s bytes' % [
|
354
|
+
request_uuid,
|
187
355
|
duration,
|
188
356
|
"#{status} #{Rack::Utils::HTTP_STATUS_CODES[status]}",
|
189
|
-
content_length.to_s
|
190
|
-
env['rack.request_uuid'] || ''
|
357
|
+
content_length.to_s
|
191
358
|
]
|
192
359
|
|
193
|
-
|
360
|
+
# more detail, if appropriate.
|
361
|
+
if env[DEBUG_MODE_ENV_KEY] && logger.debug?
|
362
|
+
unless headers.nil? || headers.empty?
|
363
|
+
logger.debug("[#{request_uuid}] #{headers.inspect}")
|
364
|
+
end
|
365
|
+
unless body.nil? || body.empty?
|
366
|
+
body = Array(body) unless body.respond_to?(:each)
|
367
|
+
body.each do |b|
|
368
|
+
b = b.to_s.strip
|
369
|
+
logger.debug("[#{request_uuid}] #{b}") unless b.empty?
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
true
|
194
374
|
end
|
195
375
|
|
196
376
|
# Log exception
|
@@ -199,11 +379,10 @@ module RightSupport::Rack
|
|
199
379
|
# @param [Exception] exception to be logged
|
200
380
|
#
|
201
381
|
# @return [TrueClass] always true
|
202
|
-
def log_exception(logger,
|
203
|
-
|
204
|
-
logger.error(msg)
|
382
|
+
def log_exception(logger, e)
|
383
|
+
logger.error(self.class.format_exception_message(e))
|
205
384
|
rescue
|
206
|
-
#no-op
|
385
|
+
# no-op; something is seriously messed up by this point...
|
207
386
|
end
|
208
387
|
end
|
209
388
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2011 RightScale Inc
|
2
|
+
# Copyright (c) 2011-2016 RightScale Inc
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -21,43 +21,130 @@
|
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
|
23
23
|
module RightSupport::Rack
|
24
|
-
|
24
|
+
|
25
|
+
# middleware to detect a request ID header or generate a new ID for each
|
26
|
+
# incoming request. the purpose is to track a request lineage throughout a
|
27
|
+
# chain of internal API calls and, in some cases, out to external APIs that
|
28
|
+
# also support the X-Request-ID header as a de-facto standard (google it).
|
25
29
|
class RequestTracker
|
26
|
-
|
27
|
-
|
28
|
-
REQUEST_UUID_ENV_NAME = "rack.request_uuid".freeze
|
29
|
-
UUID_SEPARATOR = " ".freeze
|
30
|
+
# shorthand
|
31
|
+
Generator = ::RightSupport::Data::Token
|
30
32
|
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
#
|
33
|
+
# used by many public services to represent a client-generated request ID
|
34
|
+
# that can be tracked and safely logged throughout the lineage of a request.
|
35
|
+
# this is also supported by goa middleware.
|
36
|
+
REQUEST_ID_HEADER = 'X-Request-Id'.freeze
|
37
|
+
|
38
|
+
# incoming header as found in Rack env hash, if any.
|
39
|
+
HTTP_REQUEST_ID_HEADER = 'HTTP_X_REQUEST_ID'.freeze
|
40
|
+
|
41
|
+
# LEGACY: still supported but new code should use the standard (see above).
|
42
|
+
REQUEST_UUID_HEADER = "X-Request-Uuid".freeze
|
43
|
+
|
44
|
+
# LEGACY: incoming header as found in Rack env hash, if any.
|
45
|
+
HTTP_REQUEST_UUID_HEADER = 'HTTP_X_REQUEST_UUID'.freeze
|
46
|
+
|
47
|
+
# LEGACY: refers to the generated or passed-in request ID. the key has been
|
48
|
+
# hardcoded in some places so is not easy to change and/or there is not much
|
49
|
+
# value to finding and replacing with _id in all cases.
|
50
|
+
REQUEST_UUID_ENV_NAME = 'rack.request_uuid'.freeze
|
51
|
+
|
52
|
+
# @deprecated do not send the lineage header as support may go away.
|
53
|
+
REQUEST_LINEAGE_UUID_HEADER = 'HTTP_X_REQUEST_LINEAGE_UUID'.freeze
|
54
|
+
|
55
|
+
# @deprecated do not send the lineage header as support may go away.
|
56
|
+
UUID_SEPARATOR = ' '.freeze
|
57
|
+
|
58
|
+
# limit request [UU]ID to something reasonable to keep our logs from
|
59
|
+
# overflowing on bad user-provided IDs. we do not want to be too restrictive
|
60
|
+
# in case people are encoding some useful information, etc. the issue is
|
61
|
+
# that the request ID will tend to show up a lot in logs.
|
62
|
+
MAX_REQUEST_UUID_LENGTH = 128
|
63
|
+
|
64
|
+
# @param [Object] app as next middleware or the rack application
|
37
65
|
def initialize(app)
|
38
66
|
@app = app
|
39
67
|
end
|
40
68
|
|
69
|
+
# request tracking.
|
70
|
+
#
|
71
|
+
# @param [Hash] env from preceding middleware
|
72
|
+
#
|
73
|
+
# @return [Array] tuple of [status, headers, body]
|
41
74
|
def call(env)
|
42
|
-
|
43
|
-
request_uuid = env[REQUEST_LINEAGE_UUID_HEADER] + UUID_SEPARATOR +
|
44
|
-
generate_request_uuid
|
45
|
-
else
|
46
|
-
request_uuid = generate_request_uuid
|
47
|
-
end
|
48
|
-
|
75
|
+
request_uuid, response_header_name = self.class.detect_request_uuid(env)
|
49
76
|
env[REQUEST_UUID_ENV_NAME] = request_uuid
|
50
77
|
|
51
78
|
status, headers, body = @app.call(env)
|
52
79
|
|
53
|
-
headers[
|
54
|
-
[status, headers,body]
|
80
|
+
headers[response_header_name] = request_uuid
|
81
|
+
[status, headers, body]
|
55
82
|
end
|
56
83
|
|
84
|
+
# detects whether the incoming env hash contains a request ID is some form
|
85
|
+
# and generates a new ID when missing.
|
86
|
+
#
|
87
|
+
# @return [Array] tuple of detected/generated ID and the name of the header
|
88
|
+
# to use to represent the ID in a response.
|
89
|
+
def self.detect_request_uuid(env)
|
90
|
+
request_uuid = ''
|
91
|
+
response_header_name = nil
|
92
|
+
{
|
93
|
+
HTTP_REQUEST_ID_HEADER => REQUEST_ID_HEADER,
|
94
|
+
HTTP_REQUEST_UUID_HEADER => REQUEST_UUID_HEADER,
|
95
|
+
REQUEST_LINEAGE_UUID_HEADER => REQUEST_UUID_HEADER
|
96
|
+
}.each do |in_key, out_key|
|
97
|
+
if env.has_key?(in_key)
|
98
|
+
request_uuid = env[in_key].to_s.strip
|
99
|
+
response_header_name = out_key
|
100
|
+
break
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# for legacy reasons we default to the -UUID header in response for newly-
|
105
|
+
# generated IDs. we will use the -ID standard if that was passed-in. once
|
106
|
+
# all apps are updated you will mostly see -ID in response except for API
|
107
|
+
# calls initiated by browser code. the javascript can gradually be changed
|
108
|
+
# to send -ID with requests as well.
|
109
|
+
if request_uuid.empty?
|
110
|
+
request_uuid = generate_request_uuid
|
111
|
+
response_header_name = REQUEST_UUID_HEADER
|
112
|
+
else
|
113
|
+
# truncate, if necessary.
|
114
|
+
request_uuid = request_uuid[0, MAX_REQUEST_UUID_LENGTH]
|
115
|
+
end
|
57
116
|
|
58
|
-
|
59
|
-
|
117
|
+
return request_uuid, response_header_name
|
118
|
+
end
|
119
|
+
|
120
|
+
# copies the request [UU]ID from the request environment to the given hash,
|
121
|
+
# if present. does nothing if request [UU]ID is not set (because middleware
|
122
|
+
# was not present, etc.)
|
123
|
+
#
|
124
|
+
# @param [Hash] from_env as source
|
125
|
+
# @param [Hash] to_headers as target
|
126
|
+
#
|
127
|
+
# @return [Hash] updated headers
|
128
|
+
def self.copy_request_uuid(from_env, to_headers)
|
129
|
+
to_headers ||= {}
|
130
|
+
if from_env
|
131
|
+
if request_uuid = from_env[REQUEST_UUID_ENV_NAME]
|
132
|
+
# note we always forward the _ID header as the standard. none of the
|
133
|
+
# RS code ever actually accepted the _UUID header so that is a
|
134
|
+
# non-issue.
|
135
|
+
to_headers[REQUEST_ID_HEADER] = request_uuid
|
136
|
+
end
|
137
|
+
end
|
138
|
+
to_headers
|
139
|
+
end
|
140
|
+
|
141
|
+
# generates a token (nicer than an actual UUID for logging) but we continue
|
142
|
+
# to refer to it as the "request UUID" for legacy reasons.
|
143
|
+
#
|
144
|
+
# @return [String] a new token
|
145
|
+
def self.generate_request_uuid
|
146
|
+
Generator.generate
|
60
147
|
end
|
61
|
-
end
|
62
148
|
|
63
|
-
end
|
149
|
+
end # RequestTracker
|
150
|
+
end # RightSupport::Rack
|
data/right_support.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: right_support 2.
|
5
|
+
# stub: right_support 2.11.2 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "right_support"
|
9
|
-
s.version = "2.
|
9
|
+
s.version = "2.11.2"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Tony Spataro", "Sergey Sergyenko", "Ryan Williamson", "Lee Kirchhoff", "Alexey Karpik", "Scott Messier"]
|
14
|
-
s.date = "2016-
|
14
|
+
s.date = "2016-07-08"
|
15
15
|
s.description = "A toolkit of useful, reusable foundation code created by RightScale."
|
16
16
|
s.email = "support@rightscale.com"
|
17
17
|
s.extra_rdoc_files = [
|
@@ -58,6 +58,7 @@ Gem::Specification.new do |s|
|
|
58
58
|
"lib/right_support/data/hash_tools.rb",
|
59
59
|
"lib/right_support/data/mash.rb",
|
60
60
|
"lib/right_support/data/serializer.rb",
|
61
|
+
"lib/right_support/data/token.rb",
|
61
62
|
"lib/right_support/data/unknown_type.rb",
|
62
63
|
"lib/right_support/data/uuid.rb",
|
63
64
|
"lib/right_support/db.rb",
|
@@ -70,6 +71,7 @@ Gem::Specification.new do |s|
|
|
70
71
|
"lib/right_support/log/mixin.rb",
|
71
72
|
"lib/right_support/log/multiplexer.rb",
|
72
73
|
"lib/right_support/log/null_logger.rb",
|
74
|
+
"lib/right_support/log/step_level_logger.rb",
|
73
75
|
"lib/right_support/log/syslog/remote.rb",
|
74
76
|
"lib/right_support/log/system_logger.rb",
|
75
77
|
"lib/right_support/net.rb",
|
@@ -77,6 +79,7 @@ Gem::Specification.new do |s|
|
|
77
79
|
"lib/right_support/net/dns.rb",
|
78
80
|
"lib/right_support/net/http_client.rb",
|
79
81
|
"lib/right_support/net/lb.rb",
|
82
|
+
"lib/right_support/net/lb/base.rb",
|
80
83
|
"lib/right_support/net/lb/health_check.rb",
|
81
84
|
"lib/right_support/net/lb/round_robin.rb",
|
82
85
|
"lib/right_support/net/lb/sticky.rb",
|
@@ -107,6 +110,7 @@ Gem::Specification.new do |s|
|
|
107
110
|
"spec/crypto/signed_hash_spec.rb",
|
108
111
|
"spec/data/hash_tools_spec.rb",
|
109
112
|
"spec/data/mash_spec.rb",
|
113
|
+
"spec/data/token_spec.rb",
|
110
114
|
"spec/data/uuid_spec.rb",
|
111
115
|
"spec/db/cassandra_model_part1_spec.rb",
|
112
116
|
"spec/db/cassandra_model_part2_spec.rb",
|
@@ -122,13 +126,14 @@ Gem::Specification.new do |s|
|
|
122
126
|
"spec/log/mixin_spec.rb",
|
123
127
|
"spec/log/multiplexer_spec.rb",
|
124
128
|
"spec/log/null_logger_spec.rb",
|
129
|
+
"spec/log/step_level_logger_spec.rb",
|
125
130
|
"spec/log/system_logger_spec.rb",
|
126
131
|
"spec/net/address_helper_spec.rb",
|
127
|
-
"spec/net/balancing/health_check_spec.rb",
|
128
|
-
"spec/net/balancing/round_robin_spec.rb",
|
129
|
-
"spec/net/balancing/sticky_policy_spec.rb",
|
130
132
|
"spec/net/dns_spec.rb",
|
131
133
|
"spec/net/http_client_spec.rb",
|
134
|
+
"spec/net/lb/health_check_spec.rb",
|
135
|
+
"spec/net/lb/round_robin_spec.rb",
|
136
|
+
"spec/net/lb/sticky_spec.rb",
|
132
137
|
"spec/net/request_balancer_spec.rb",
|
133
138
|
"spec/net/s3_helper_spec.rb",
|
134
139
|
"spec/net/ssl_spec.rb",
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RightSupport::Data::Token do
|
4
|
+
|
5
|
+
context :generate do
|
6
|
+
let(:match) { /^[0-9a-zA-Z]{13}$/ }
|
7
|
+
|
8
|
+
subject { described_class }
|
9
|
+
|
10
|
+
it 'generates UUIDs' do
|
11
|
+
seen = {}
|
12
|
+
100.times do
|
13
|
+
actual = subject.generate
|
14
|
+
actual.should =~ match
|
15
|
+
seen[actual].should be_nil
|
16
|
+
seen[actual] = true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|