Module: Otto::Static

Extended by:
Static
Included in:
Static
Defined in:
lib/otto/static.rb

Overview

Static response utilities for common HTTP responses

Instance Method Summary collapse

Instance Method Details

#copy_headers(headers) ⇒ Hash, Rack::Headers

Copy a Rack headers container, keeping its class and copying Array values.

Parameters:

  • headers (Hash, Rack::Headers, nil)

    the headers to copy

Returns:

  • (Hash, Rack::Headers)

    a new container of the same class



46
47
48
49
50
51
52
53
54
# File 'lib/otto/static.rb', line 46

def copy_headers(headers)
  return {} if headers.nil?

  copied = headers.dup
  copied.each_pair do |key, value|
    copied[key] = value.dup if value.is_a?(Array)
  end
  copied
end

#copy_response(response) ⇒ Array

Return a per-request copy of a Rack triple so callers can never hand a shared object back to the Rack stack.

Middleware above Otto (rack-session, Otto's own CSRF middleware, anything that calls Rack::Utils.set_cookie_header!) writes response headers in place. Returning a configured triple by reference lets those writes accumulate on the shared object for the life of the process, so every subsequent 404/500 replays every Set-Cookie any earlier one committed.

The copy is intentionally shallow-plus-one: the headers container keeps its class (a Rack::Headers stays case-insensitive), each Array-valued header (Rack 3's representation of a repeated header) is copied so an append cannot reach the shared Array, and an Array body is copied so a middleware appending chunks cannot grow the shared body. A frozen configured triple yields an unfrozen copy, so cookie middleware works after configuration freezing as well.

Parameters:

  • response (Array)

    a Rack triple [status, headers, body]

Returns:

  • (Array)

    a new triple that shares no mutable container with response



37
38
39
40
# File 'lib/otto/static.rb', line 37

def copy_response(response)
  status, headers, body = response
  [status, copy_headers(headers), body.is_a?(Array) ? body.dup : body]
end

#indifferent_hashObject

Creates a Hash with indifferent access.



86
87
88
# File 'lib/otto/static.rb', line 86

def indifferent_hash
  Hash.new { |hash, key| hash[key.to_s] if key.is_a?(Symbol) }
end

#indifferent_params(params) ⇒ Object

Enable string or symbol key access to the nested params hash.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/otto/static.rb', line 66

def indifferent_params(params)
  if params.is_a?(Hash)
    params = indifferent_hash.merge(params)
    params.each do |key, value|
      next unless value.is_a?(Hash) || value.is_a?(Array)

      params[key] = indifferent_params(value)
    end
  elsif params.is_a?(Array)
    params.collect! do |value|
      if value.is_a?(Hash) || value.is_a?(Array)
        indifferent_params(value)
      else
        value
      end
    end
  end
end

#not_foundObject



14
15
16
# File 'lib/otto/static.rb', line 14

def not_found
  [404, security_headers.merge({ 'content-type' => 'text/plain' }), ['Not Found']]
end

#security_headersObject



56
57
58
59
60
61
62
63
# File 'lib/otto/static.rb', line 56

def security_headers
  {
    'x-frame-options' => 'DENY',
    'x-content-type-options' => 'nosniff',
    'x-xss-protection' => '1; mode=block',
    'referrer-policy' => 'strict-origin-when-cross-origin',
  }
end

#server_errorObject



10
11
12
# File 'lib/otto/static.rb', line 10

def server_error
  [500, security_headers.merge({ 'content-type' => 'text/plain' }), ['Server error']]
end