Module: Hyperion::Adapter::Rack

Defined in:
lib/hyperion/adapter/rack.rb

Overview

NOTE: this is Hyperion::Adapter::Rack, not the Rack gem. Reference the Rack gem as ::Rack inside this module if needed.

Constant Summary collapse

HTTP_KEY_CACHE =

Pre-frozen mapping for the 16 most common HTTP request headers. Skips the per-request ‘“HTTP_#’_’)”‘ allocation (5–15 string ops per request × N headers). Uncached header names fall back to the dynamic computation. Keys are lowercased to match the parser’s normalisation.

{
  'host' => 'HTTP_HOST',
  'user-agent' => 'HTTP_USER_AGENT',
  'accept' => 'HTTP_ACCEPT',
  'accept-encoding' => 'HTTP_ACCEPT_ENCODING',
  'accept-language' => 'HTTP_ACCEPT_LANGUAGE',
  'connection' => 'HTTP_CONNECTION',
  'content-type' => 'HTTP_CONTENT_TYPE',
  'content-length' => 'HTTP_CONTENT_LENGTH',
  'cookie' => 'HTTP_COOKIE',
  'authorization' => 'HTTP_AUTHORIZATION',
  'cache-control' => 'HTTP_CACHE_CONTROL',
  'referer' => 'HTTP_REFERER',
  'origin' => 'HTTP_ORIGIN',
  'x-forwarded-for' => 'HTTP_X_FORWARDED_FOR',
  'x-forwarded-proto' => 'HTTP_X_FORWARDED_PROTO',
  'x-real-ip' => 'HTTP_X_REAL_IP'
}.freeze
ENV_POOL =
Hyperion::Pool.new(
  max_size: 256,
  factory: -> { {} },
  reset: ->(env) { env.clear }
)
INPUT_POOL =
Hyperion::Pool.new(
  max_size: 256,
  factory: -> { StringIO.new },
  reset: lambda { |io|
    io.string = +''
    io.rewind
  }
)

Class Method Summary collapse

Class Method Details

.call(app, request) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/hyperion/adapter/rack.rb', line 66

def call(app, request)
  env, input = build_env(request)
  status, headers, body = app.call(env)
  [status, headers, body]
rescue StandardError => e
  Hyperion.metrics.increment(:app_errors)
  Hyperion.logger.error do
    {
      message: 'app raised',
      error: e.message,
      error_class: e.class.name,
      backtrace: (e.backtrace || []).first(20).join(' | ')
    }
  end
  [500, { 'content-type' => 'text/plain' }, ['Internal Server Error']]
ensure
  # Return env + input to pools after the response has been fully
  # iterated by the writer. We can't release here because Rack body
  # is iterated lazily — release happens after the writer.
  # For Phase 5 simplicity we release synchronously since the writer
  # buffers fully. Phase 7 (HTTP/2 streaming) will revisit.
  ENV_POOL.release(env) if env
  INPUT_POOL.release(input) if input
end

.warmup_pool(count = 8) ⇒ Object

Pre-allocate ‘n` env-hash and rack-input objects in master before fork. Children inherit the populated free-list via copy-on-write —the hash slots stay shared until a request mutates them. Eliminates the first-N-requests allocation tax that every fresh worker would otherwise pay on cold start. Idempotent: safe to call multiple times; the pool simply caps at its configured `max_size`.



58
59
60
61
62
63
64
# File 'lib/hyperion/adapter/rack.rb', line 58

def warmup_pool(count = 8)
  warmed_envs = Array.new(count) { ENV_POOL.acquire }
  warmed_inputs = Array.new(count) { INPUT_POOL.acquire }
  warmed_envs.each { |e| ENV_POOL.release(e) }
  warmed_inputs.each { |i| INPUT_POOL.release(i) }
  nil
end