Class: Quicsilver::Protocol::Adapter

Inherits:
Object
  • Object
show all
Defined in:
lib/quicsilver/protocol/adapter.rb

Overview

Converts between QUIC/HTTP/3 frames and protocol-http Request/Response objects. This enables integration with Falcon and any other server built on protocol-http.

Usage:

adapter = Protocol::Adapter.new(app)
request, body = adapter.build_request(parsed_headers)
body.write(chunk)               # feed body data
body.close_write                # signal end of body
response = adapter.call(request)
adapter.send_response(response, writer)

Constant Summary collapse

VERSION =
"HTTP/3"

Instance Method Summary collapse

Constructor Details

#initialize(app) ⇒ Adapter

Returns a new instance of Adapter.



26
27
28
29
# File 'lib/quicsilver/protocol/adapter.rb', line 26

def initialize(app)
  @app = app
  @qpack_encoder = Quicsilver::Protocol::Qpack::Encoder.new
end

Instance Method Details

#build_request(headers) ⇒ Array(Protocol::HTTP::Request, Protocol::StreamInput)

Build a Protocol::HTTP::Request from parsed HTTP/3 headers.

Returns [request, body] where body is a Protocol::StreamInput that the transport feeds RECEIVE chunks into.

Parameters:

  • headers (Hash)

    Parsed headers from RequestParser (includes pseudo-headers).

Returns:

  • (Array(Protocol::HTTP::Request, Protocol::StreamInput))

    request and body. Body is nil for bodyless methods (GET, HEAD, TRACE). Caller feeds RECEIVE data into body via write(), then close_write on FIN.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/quicsilver/protocol/adapter.rb', line 40

def build_request(headers)
  method = headers[":method"]
  scheme = headers[":scheme"] || "https"
  authority = headers[":authority"]
  path = headers[":path"]
  protocol = headers[":protocol"]
  content_length = headers["content-length"]&.to_i

  protocol_headers = ::Protocol::HTTP::Headers.new
  headers.each do |name, value|
    next if name.start_with?(":")
    protocol_headers.add(name, value)
  end

  body = unless bodyless_request?(method)
    Protocol::StreamInput.new(content_length)
  end

  request = ::Protocol::HTTP::Request.new(
    scheme, authority, method, path, VERSION,
    protocol_headers, body, protocol
  )

  [request, body]
end

#call(request) ⇒ Protocol::HTTP::Response

Call the protocol-http application with the request.

Parameters:

  • request (Protocol::HTTP::Request)

Returns:

  • (Protocol::HTTP::Response)


95
96
97
# File 'lib/quicsilver/protocol/adapter.rb', line 95

def call(request)
  @app.call(request)
end

#send_response(response, writer, head_request: false) ⇒ void

This method returns an undefined value.

Send a Protocol::HTTP::Response via a transport writer.

Encodes the response headers as an HTTP/3 HEADERS frame, then streams the response body as DATA frames via Protocol::StreamOutput.

Parameters:

  • response (Protocol::HTTP::Response)

    The response to send.

  • writer (#call)

    Transport writer — accepts (data, fin) for sending bytes.

  • head_request (Boolean) (defaults to: false)

    Whether this was a HEAD request.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/quicsilver/protocol/adapter.rb', line 75

def send_response(response, writer, head_request: false)
  status = response.status
  headers = response.headers
  trailers = extract_trailers(headers)
  headers_hash = response_headers_hash(headers)
  body = response.body

  if body.nil? || head_request
    send_headers_only(status, headers_hash, writer, trailers: trailers)
  elsif body.respond_to?(:read)
    stream_response(status, headers_hash, body, writer, trailers: trailers)
  else
    buffer_response(status, headers_hash, body, writer, trailers: trailers)
  end
end