Class: Falcon::Adapters::Response

Inherits:
Protocol::HTTP::Response
  • Object
show all
Defined in:
lib/falcon/adapters/response.rb

Overview

A wrapper for a ‘Rack` response.

A Rack response consisting of ‘[status, headers, body]` includes various rack-specific elements, including:

  • A ‘headers` callback which bypasses normal response handling.

  • Potentially invalid content length.

  • Potentially invalid body when processing a ‘HEAD` request.

  • Newline-separated header values.

  • Other ‘rack.` specific header key/value pairs.

This wrapper takes those issues into account and adapts the rack response tuple into a Protocol::HTTP::Response.

Constant Summary collapse

IGNORE_HEADERS =
Middleware::Proxy::HOP_HEADERS

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(status, headers, body, protocol = nil) ⇒ Response

Initialize the response wrapper.



108
109
110
# File 'lib/falcon/adapters/response.rb', line 108

def initialize(status, headers, body, protocol = nil)
  super(nil, status, headers, body, protocol)
end

Class Method Details

.wrap(status, headers, body, request = nil) ⇒ Object

Wrap a rack response.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/falcon/adapters/response.rb', line 72

def self.wrap(status, headers, body, request = nil)
  headers, meta = wrap_headers(headers)
  
  if block = meta['rack.hijack']
    body = Async::HTTP::Body::Hijack.wrap(request, &block)
  else
    ignored = headers.extract(IGNORE_HEADERS)
    
    unless ignored.empty?
      Async.logger.warn("Ignoring protocol-level headers: #{ignored.inspect}")
    end
    
    body = Output.wrap(status, headers, body)
  end
  
  if request&.head?
    # I thought about doing this in Output.wrap, but decided the semantics are too tricky. Specifically, the various ways a rack response body can be wrapped, and the need to invoke #close at the right point.
    body = ::Protocol::HTTP::Body::Head.for(body)
  end
  
  protocol = meta['rack.protocol']
  
  # https://tools.ietf.org/html/rfc7231#section-7.4.2
  headers.add('server', "falcon/#{Falcon::VERSION}")
  
  # https://tools.ietf.org/html/rfc7231#section-7.1.1.2
  headers.add('date', Time.now.httpdate)
  
  return self.new(status, headers, body, protocol)
end

.wrap_headers(fields) ⇒ Object

Process the rack response headers into into a Protocol::HTTP::Headers instance, along with any extra ‘rack.` metadata.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/falcon/adapters/response.rb', line 48

def self.wrap_headers(fields)
  headers = ::Protocol::HTTP::Headers.new
  meta = {}
  
  fields.each do |key, value|
    key = key.downcase
    
    if key.start_with?('rack.')
      meta[key] = value
    else
      value.to_s.split("\n").each do |part|
        headers.add(key, part)
      end
    end
  end
  
  return headers, meta
end