right_support 2.10.1 → 2.11.2
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/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
|