otto 1.5.0 → 2.0.0.pre1
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/.github/workflows/ci.yml +44 -5
- data/.github/workflows/claude-code-review.yml +53 -0
- data/.github/workflows/claude.yml +49 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +24 -345
- data/CHANGELOG.rst +83 -0
- data/CLAUDE.md +56 -0
- data/Gemfile +21 -5
- data/Gemfile.lock +69 -31
- data/README.md +2 -0
- data/bin/rspec +16 -0
- data/changelog.d/20250911_235619_delano_next.rst +28 -0
- data/changelog.d/20250912_123055_delano_remove_ostruct.rst +21 -0
- data/changelog.d/20250912_175625_claude_delano_remove_ostruct.rst +21 -0
- data/changelog.d/README.md +120 -0
- data/changelog.d/scriv.ini +5 -0
- data/docs/.gitignore +1 -0
- data/docs/migrating/v2.0.0-pre1.md +276 -0
- data/examples/.gitignore +1 -0
- data/examples/advanced_routes/README.md +33 -0
- data/examples/advanced_routes/app/controllers/handlers/async.rb +9 -0
- data/examples/advanced_routes/app/controllers/handlers/dynamic.rb +9 -0
- data/examples/advanced_routes/app/controllers/handlers/static.rb +9 -0
- data/examples/advanced_routes/app/controllers/modules/auth.rb +9 -0
- data/examples/advanced_routes/app/controllers/modules/transformer.rb +9 -0
- data/examples/advanced_routes/app/controllers/modules/validator.rb +9 -0
- data/examples/advanced_routes/app/controllers/routes_app.rb +232 -0
- data/examples/advanced_routes/app/controllers/v2/admin.rb +9 -0
- data/examples/advanced_routes/app/controllers/v2/config.rb +9 -0
- data/examples/advanced_routes/app/controllers/v2/settings.rb +9 -0
- data/examples/advanced_routes/app/logic/admin/logic/manager.rb +27 -0
- data/examples/advanced_routes/app/logic/admin/panel.rb +27 -0
- data/examples/advanced_routes/app/logic/analytics_processor.rb +25 -0
- data/examples/advanced_routes/app/logic/complex/business/handler.rb +27 -0
- data/examples/advanced_routes/app/logic/data_logic.rb +23 -0
- data/examples/advanced_routes/app/logic/data_processor.rb +25 -0
- data/examples/advanced_routes/app/logic/input_validator.rb +24 -0
- data/examples/advanced_routes/app/logic/nested/feature/logic.rb +27 -0
- data/examples/advanced_routes/app/logic/reports_generator.rb +27 -0
- data/examples/advanced_routes/app/logic/simple_logic.rb +25 -0
- data/examples/advanced_routes/app/logic/system/config/manager.rb +27 -0
- data/examples/advanced_routes/app/logic/test_logic.rb +23 -0
- data/examples/advanced_routes/app/logic/transform_logic.rb +23 -0
- data/examples/advanced_routes/app/logic/upload_logic.rb +23 -0
- data/examples/advanced_routes/app/logic/v2/logic/dashboard.rb +27 -0
- data/examples/advanced_routes/app/logic/v2/logic/processor.rb +27 -0
- data/examples/advanced_routes/app.rb +33 -0
- data/examples/advanced_routes/config.rb +23 -0
- data/examples/advanced_routes/config.ru +7 -0
- data/examples/advanced_routes/puma.rb +20 -0
- data/examples/advanced_routes/routes +167 -0
- data/examples/advanced_routes/run.rb +39 -0
- data/examples/advanced_routes/test.rb +58 -0
- data/examples/authentication_strategies/README.md +32 -0
- data/examples/authentication_strategies/app/auth.rb +68 -0
- data/examples/authentication_strategies/app/controllers/auth_controller.rb +29 -0
- data/examples/authentication_strategies/app/controllers/main_controller.rb +28 -0
- data/examples/authentication_strategies/config.ru +24 -0
- data/examples/authentication_strategies/routes +37 -0
- data/examples/basic/README.md +29 -0
- data/examples/basic/app.rb +7 -35
- data/examples/basic/routes +0 -9
- data/examples/mcp_demo/README.md +87 -0
- data/examples/mcp_demo/app.rb +51 -0
- data/examples/mcp_demo/config.ru +17 -0
- data/examples/mcp_demo/routes +9 -0
- data/examples/security_features/README.md +46 -0
- data/examples/security_features/app.rb +23 -24
- data/examples/security_features/config.ru +8 -10
- data/lib/otto/core/configuration.rb +167 -0
- data/lib/otto/core/error_handler.rb +86 -0
- data/lib/otto/core/file_safety.rb +61 -0
- data/lib/otto/core/middleware_stack.rb +157 -0
- data/lib/otto/core/router.rb +183 -0
- data/lib/otto/core/uri_generator.rb +44 -0
- data/lib/otto/design_system.rb +7 -5
- data/lib/otto/helpers/base.rb +3 -0
- data/lib/otto/helpers/request.rb +10 -8
- data/lib/otto/helpers/response.rb +5 -4
- data/lib/otto/helpers/validation.rb +85 -0
- data/lib/otto/mcp/auth/token.rb +77 -0
- data/lib/otto/mcp/protocol.rb +164 -0
- data/lib/otto/mcp/rate_limiting.rb +155 -0
- data/lib/otto/mcp/registry.rb +100 -0
- data/lib/otto/mcp/route_parser.rb +77 -0
- data/lib/otto/mcp/server.rb +206 -0
- data/lib/otto/mcp/validation.rb +123 -0
- data/lib/otto/response_handlers/auto.rb +39 -0
- data/lib/otto/response_handlers/base.rb +16 -0
- data/lib/otto/response_handlers/default.rb +16 -0
- data/lib/otto/response_handlers/factory.rb +39 -0
- data/lib/otto/response_handlers/json.rb +28 -0
- data/lib/otto/response_handlers/redirect.rb +25 -0
- data/lib/otto/response_handlers/view.rb +24 -0
- data/lib/otto/response_handlers.rb +9 -135
- data/lib/otto/route.rb +9 -9
- data/lib/otto/route_definition.rb +30 -33
- data/lib/otto/route_handlers/base.rb +121 -0
- data/lib/otto/route_handlers/class_method.rb +89 -0
- data/lib/otto/route_handlers/factory.rb +29 -0
- data/lib/otto/route_handlers/instance_method.rb +69 -0
- data/lib/otto/route_handlers/lambda.rb +59 -0
- data/lib/otto/route_handlers/logic_class.rb +93 -0
- data/lib/otto/route_handlers.rb +10 -376
- data/lib/otto/security/authentication/auth_strategy.rb +44 -0
- data/lib/otto/security/authentication/authentication_middleware.rb +123 -0
- data/lib/otto/security/authentication/failure_result.rb +36 -0
- data/lib/otto/security/authentication/strategies/api_key_strategy.rb +40 -0
- data/lib/otto/security/authentication/strategies/permission_strategy.rb +47 -0
- data/lib/otto/security/authentication/strategies/public_strategy.rb +19 -0
- data/lib/otto/security/authentication/strategies/role_strategy.rb +57 -0
- data/lib/otto/security/authentication/strategies/session_strategy.rb +41 -0
- data/lib/otto/security/authentication/strategy_result.rb +223 -0
- data/lib/otto/security/authentication.rb +28 -282
- data/lib/otto/security/config.rb +15 -11
- data/lib/otto/security/configurator.rb +219 -0
- data/lib/otto/security/csrf.rb +8 -143
- data/lib/otto/security/middleware/csrf_middleware.rb +151 -0
- data/lib/otto/security/middleware/rate_limit_middleware.rb +38 -0
- data/lib/otto/security/middleware/validation_middleware.rb +252 -0
- data/lib/otto/security/rate_limiter.rb +86 -0
- data/lib/otto/security/rate_limiting.rb +16 -0
- data/lib/otto/security/validator.rb +8 -292
- data/lib/otto/static.rb +3 -0
- data/lib/otto/utils.rb +14 -0
- data/lib/otto/version.rb +3 -1
- data/lib/otto.rb +184 -414
- data/otto.gemspec +11 -6
- metadata +134 -25
- data/examples/dynamic_pages/app.rb +0 -115
- data/examples/dynamic_pages/config.ru +0 -30
- data/examples/dynamic_pages/routes +0 -21
- data/examples/helpers_demo/app.rb +0 -244
- data/examples/helpers_demo/config.ru +0 -26
- data/examples/helpers_demo/routes +0 -7
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/otto/route_handlers/class_method.rb
|
4
|
+
|
5
|
+
require 'json'
|
6
|
+
require 'securerandom'
|
7
|
+
|
8
|
+
require_relative 'base'
|
9
|
+
|
10
|
+
class Otto
|
11
|
+
module RouteHandlers
|
12
|
+
# Handler for class methods (existing Otto pattern)
|
13
|
+
# Maintains backward compatibility for Controller.action patterns
|
14
|
+
class ClassMethodHandler < BaseHandler
|
15
|
+
def call(env, extra_params = {})
|
16
|
+
req = Rack::Request.new(env)
|
17
|
+
res = Rack::Response.new
|
18
|
+
|
19
|
+
begin
|
20
|
+
# Apply the same extensions and processing as original Route#call
|
21
|
+
setup_request_response(req, res, env, extra_params)
|
22
|
+
|
23
|
+
# Call class method directly (existing Otto behavior)
|
24
|
+
result = target_class.send(route_definition.method_name, req, res)
|
25
|
+
|
26
|
+
# Only handle response if response_type is not default
|
27
|
+
if route_definition.response_type != 'default'
|
28
|
+
handle_response(result, res, {
|
29
|
+
class: target_class,
|
30
|
+
request: req,
|
31
|
+
})
|
32
|
+
end
|
33
|
+
rescue StandardError => e
|
34
|
+
# Check if we're being called through Otto's integrated context (vs direct handler testing)
|
35
|
+
# In integrated context, let Otto's centralized error handler manage the response
|
36
|
+
# In direct testing context, handle errors locally for unit testing
|
37
|
+
if otto_instance
|
38
|
+
# Log error for handler-specific context but let Otto's centralized error handler manage the response
|
39
|
+
Otto.logger.error "[ClassMethodHandler] #{e.class}: #{e.message}"
|
40
|
+
Otto.logger.debug "[ClassMethodHandler] Backtrace: #{e.backtrace.join("\n")}" if Otto.debug
|
41
|
+
raise e # Re-raise to let Otto's centralized error handler manage the response
|
42
|
+
else
|
43
|
+
# Direct handler testing context - handle errors locally with security improvements
|
44
|
+
error_id = SecureRandom.hex(8)
|
45
|
+
Otto.logger.error "[#{error_id}] #{e.class}: #{e.message}"
|
46
|
+
Otto.logger.debug "[#{error_id}] Backtrace: #{e.backtrace.join("\n")}" if Otto.debug
|
47
|
+
|
48
|
+
res.status = 500
|
49
|
+
|
50
|
+
# Content negotiation for error response
|
51
|
+
accept_header = env['HTTP_ACCEPT'].to_s
|
52
|
+
if accept_header.include?('application/json')
|
53
|
+
res.headers['content-type'] = 'application/json'
|
54
|
+
error_data = if Otto.env?(:dev, :development)
|
55
|
+
{
|
56
|
+
error: 'Internal Server Error',
|
57
|
+
message: 'Server error occurred. Check logs for details.',
|
58
|
+
error_id: error_id,
|
59
|
+
}
|
60
|
+
else
|
61
|
+
{
|
62
|
+
error: 'Internal Server Error',
|
63
|
+
message: 'An error occurred. Please try again later.',
|
64
|
+
}
|
65
|
+
end
|
66
|
+
res.write JSON.generate(error_data)
|
67
|
+
else
|
68
|
+
res.headers['content-type'] = 'text/plain'
|
69
|
+
if Otto.env?(:dev, :development)
|
70
|
+
res.write "Server error (ID: #{error_id}). Check logs for details."
|
71
|
+
else
|
72
|
+
res.write 'An error occurred. Please try again later.'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Add security headers if available
|
77
|
+
if otto_instance.respond_to?(:security_config) && otto_instance.security_config
|
78
|
+
otto_instance.security_config.security_headers.each do |header, value|
|
79
|
+
res.headers[header] = value
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
finalize_response(res)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/otto/route_handlers/factory.rb
|
4
|
+
|
5
|
+
require_relative 'base'
|
6
|
+
|
7
|
+
class Otto
|
8
|
+
module RouteHandlers
|
9
|
+
# Factory for creating appropriate handlers based on route definitions
|
10
|
+
class HandlerFactory
|
11
|
+
# Create a handler for the given route definition
|
12
|
+
# @param route_definition [Otto::RouteDefinition] The route definition
|
13
|
+
# @param otto_instance [Otto] The Otto instance for configuration access
|
14
|
+
# @return [BaseHandler] Appropriate handler for the route
|
15
|
+
def self.create_handler(route_definition, otto_instance = nil)
|
16
|
+
case route_definition.kind
|
17
|
+
when :logic
|
18
|
+
LogicClassHandler.new(route_definition, otto_instance)
|
19
|
+
when :instance
|
20
|
+
InstanceMethodHandler.new(route_definition, otto_instance)
|
21
|
+
when :class
|
22
|
+
ClassMethodHandler.new(route_definition, otto_instance)
|
23
|
+
else
|
24
|
+
raise ArgumentError, "Unknown handler kind: #{route_definition.kind}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/otto/route_handlers/instance_method.rb
|
4
|
+
require 'securerandom'
|
5
|
+
|
6
|
+
require_relative 'base'
|
7
|
+
|
8
|
+
class Otto
|
9
|
+
module RouteHandlers
|
10
|
+
# Handler for instance methods (existing Otto pattern)
|
11
|
+
# Maintains backward compatibility for Controller#action patterns
|
12
|
+
class InstanceMethodHandler < BaseHandler
|
13
|
+
def call(env, extra_params = {})
|
14
|
+
req = Rack::Request.new(env)
|
15
|
+
res = Rack::Response.new
|
16
|
+
|
17
|
+
begin
|
18
|
+
# Apply the same extensions and processing as original Route#call
|
19
|
+
setup_request_response(req, res, env, extra_params)
|
20
|
+
|
21
|
+
# Create instance and call method (existing Otto behavior)
|
22
|
+
instance = target_class.new(req, res)
|
23
|
+
result = instance.send(route_definition.method_name)
|
24
|
+
|
25
|
+
# Only handle response if response_type is not default
|
26
|
+
if route_definition.response_type != 'default'
|
27
|
+
handle_response(result, res, {
|
28
|
+
instance: instance,
|
29
|
+
request: req,
|
30
|
+
})
|
31
|
+
end
|
32
|
+
rescue StandardError => e
|
33
|
+
# Check if we're being called through Otto's integrated context (vs direct handler testing)
|
34
|
+
# In integrated context, let Otto's centralized error handler manage the response
|
35
|
+
# In direct testing context, handle errors locally for unit testing
|
36
|
+
if otto_instance
|
37
|
+
# Log error for handler-specific context but let the centralized error handler manage the response
|
38
|
+
Otto.logger.error "[InstanceMethodHandler] #{e.class}: #{e.message}"
|
39
|
+
Otto.logger.debug "[InstanceMethodHandler] Backtrace: #{e.backtrace.join("\n")}" if Otto.debug
|
40
|
+
raise e # Re-raise to let Otto's centralized error handler manage the response
|
41
|
+
else
|
42
|
+
# Direct handler testing context - handle errors locally with security improvements
|
43
|
+
error_id = SecureRandom.hex(8)
|
44
|
+
Otto.logger.error "[#{error_id}] #{e.class}: #{e.message}"
|
45
|
+
Otto.logger.debug "[#{error_id}] Backtrace: #{e.backtrace.join("\n")}" if Otto.debug
|
46
|
+
|
47
|
+
res.status = 500
|
48
|
+
res.headers['content-type'] = 'text/plain'
|
49
|
+
|
50
|
+
if Otto.env?(:dev, :development)
|
51
|
+
res.write "Server error (ID: #{error_id}). Check logs for details."
|
52
|
+
else
|
53
|
+
res.write 'An error occurred. Please try again later.'
|
54
|
+
end
|
55
|
+
|
56
|
+
# Add security headers if available
|
57
|
+
if otto_instance.respond_to?(:security_config) && otto_instance.security_config
|
58
|
+
otto_instance.security_config.security_headers.each do |header, value|
|
59
|
+
res.headers[header] = value
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
finalize_response(res)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/otto/route_handlers/lambda.rb
|
4
|
+
require 'securerandom'
|
5
|
+
|
6
|
+
require_relative 'base'
|
7
|
+
|
8
|
+
class Otto
|
9
|
+
module RouteHandlers
|
10
|
+
# Custom handler for lambda/proc definitions (future extension)
|
11
|
+
class LambdaHandler < BaseHandler
|
12
|
+
def call(env, extra_params = {})
|
13
|
+
req = Rack::Request.new(env)
|
14
|
+
res = Rack::Response.new
|
15
|
+
|
16
|
+
begin
|
17
|
+
# Security: Lambda handlers require pre-configured procs from Otto instance
|
18
|
+
# This prevents code injection via eval and maintains security
|
19
|
+
handler_name = route_definition.klass_name
|
20
|
+
lambda_registry = otto_instance&.config&.dig(:lambda_handlers) || {}
|
21
|
+
|
22
|
+
lambda_proc = lambda_registry[handler_name]
|
23
|
+
unless lambda_proc.respond_to?(:call)
|
24
|
+
raise ArgumentError, "Lambda handler '#{handler_name}' not found in registry or not callable"
|
25
|
+
end
|
26
|
+
|
27
|
+
result = lambda_proc.call(req, res, extra_params)
|
28
|
+
|
29
|
+
handle_response(result, res, {
|
30
|
+
lambda: lambda_proc,
|
31
|
+
request: req,
|
32
|
+
})
|
33
|
+
rescue StandardError => e
|
34
|
+
error_id = SecureRandom.hex(8)
|
35
|
+
Otto.logger.error "[#{error_id}] #{e.class}: #{e.message}"
|
36
|
+
Otto.logger.debug "[#{error_id}] Backtrace: #{e.backtrace.join("\n")}" if Otto.debug
|
37
|
+
|
38
|
+
res.status = 500
|
39
|
+
res.headers['content-type'] = 'text/plain'
|
40
|
+
|
41
|
+
if Otto.env?(:dev, :development)
|
42
|
+
res.write "Lambda handler error (ID: #{error_id}). Check logs for details."
|
43
|
+
else
|
44
|
+
res.write 'An error occurred. Please try again later.'
|
45
|
+
end
|
46
|
+
|
47
|
+
# Add security headers if available
|
48
|
+
if otto_instance.respond_to?(:security_config) && otto_instance.security_config
|
49
|
+
otto_instance.security_config.security_headers.each do |header, value|
|
50
|
+
res.headers[header] = value
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
res.finish
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/otto/route_handlers/logic_class.rb
|
4
|
+
require 'json'
|
5
|
+
require 'securerandom'
|
6
|
+
|
7
|
+
require_relative 'base'
|
8
|
+
|
9
|
+
class Otto
|
10
|
+
module RouteHandlers
|
11
|
+
# Handler for Logic classes (new in Otto Framework Enhancement)
|
12
|
+
# Handles Logic class routes with the modern RequestContext pattern
|
13
|
+
# Logic classes use signature: initialize(context, params, locale)
|
14
|
+
class LogicClassHandler < BaseHandler
|
15
|
+
def call(env, extra_params = {})
|
16
|
+
req = Rack::Request.new(env)
|
17
|
+
res = Rack::Response.new
|
18
|
+
|
19
|
+
begin
|
20
|
+
# Get strategy result (guaranteed to exist from auth middleware)
|
21
|
+
strategy_result = env['otto.strategy_result'] || Otto::Security::Authentication::StrategyResult.anonymous
|
22
|
+
|
23
|
+
# Initialize Logic class with new signature: context, params, locale
|
24
|
+
logic_params = req.params.merge(extra_params)
|
25
|
+
|
26
|
+
# Handle JSON request bodies
|
27
|
+
if req.content_type&.include?('application/json') && req.body.size.positive?
|
28
|
+
begin
|
29
|
+
req.body.rewind
|
30
|
+
json_data = JSON.parse(req.body.read)
|
31
|
+
logic_params = logic_params.merge(json_data) if json_data.is_a?(Hash)
|
32
|
+
rescue JSON::ParserError => e
|
33
|
+
Otto.logger.error "[LogicClassHandler] JSON parsing error: #{e.message}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
locale = env['otto.locale'] || 'en'
|
38
|
+
|
39
|
+
logic = target_class.new(strategy_result, logic_params, locale)
|
40
|
+
|
41
|
+
# Execute standard Logic class lifecycle
|
42
|
+
logic.raise_concerns if logic.respond_to?(:raise_concerns)
|
43
|
+
|
44
|
+
result = if logic.respond_to?(:process)
|
45
|
+
logic.process
|
46
|
+
else
|
47
|
+
logic.call || logic
|
48
|
+
end
|
49
|
+
|
50
|
+
# Handle response with Logic instance context
|
51
|
+
handle_response(result, res, {
|
52
|
+
logic_instance: logic,
|
53
|
+
request: req,
|
54
|
+
status_code: logic.respond_to?(:status_code) ? logic.status_code : nil,
|
55
|
+
})
|
56
|
+
rescue StandardError => e
|
57
|
+
# Check if we're being called through Otto's integrated context (vs direct handler testing)
|
58
|
+
# In integrated context, let Otto's centralized error handler manage the response
|
59
|
+
# In direct testing context, handle errors locally for unit testing
|
60
|
+
if otto_instance
|
61
|
+
# Log error for handler-specific context but let Otto's centralized error handler manage the response
|
62
|
+
Otto.logger.error "[LogicClassHandler] #{e.class}: #{e.message}"
|
63
|
+
Otto.logger.debug "[LogicClassHandler] Backtrace: #{e.backtrace.join("\n")}" if Otto.debug
|
64
|
+
raise e # Re-raise to let Otto's centralized error handler manage the response
|
65
|
+
else
|
66
|
+
# Direct handler testing context - handle errors locally with security improvements
|
67
|
+
error_id = SecureRandom.hex(8)
|
68
|
+
Otto.logger.error "[#{error_id}] #{e.class}: #{e.message}"
|
69
|
+
Otto.logger.debug "[#{error_id}] Backtrace: #{e.backtrace.join("\n")}" if Otto.debug
|
70
|
+
|
71
|
+
res.status = 500
|
72
|
+
res.headers['content-type'] = 'text/plain'
|
73
|
+
|
74
|
+
if Otto.env?(:dev, :development)
|
75
|
+
res.write "Server error (ID: #{error_id}). Check logs for details."
|
76
|
+
else
|
77
|
+
res.write 'An error occurred. Please try again later.'
|
78
|
+
end
|
79
|
+
|
80
|
+
# Add security headers if available
|
81
|
+
if otto_instance.respond_to?(:security_config) && otto_instance.security_config
|
82
|
+
otto_instance.security_config.security_headers.each do |header, value|
|
83
|
+
res.headers[header] = value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
res.finish
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|