Class: WebPackage::SignedHttpExchange

Inherits:
Object
  • Object
show all
Includes:
Helpers
Defined in:
lib/web_package/signed_http_exchange.rb

Overview

Builds headers and body of SXG format for a given pair of HTTP request-response. SXG format allows a browser to trust that a single HTTP request/response pair was generated by the origin it claims.

Current implementation is lazy, meaning that signing is performed upon the invocation of the ‘body` method.

Constant Summary collapse

SIGNATURE_MAX_SIZE =
2**14
HEADERS_MAX_SIZE =
2**19
MOCK_URL =

Mock request-response pair just in case:

'https://example.com/wow-fake-path'.freeze
MOCK_RESP =
[200, { 'Content-Type' => 'text/html; charset=utf-8' }, ['<h1>Hello!</h1>']].freeze

Instance Method Summary collapse

Constructor Details

#initialize(url = MOCK_URL, response = MOCK_RESP) ⇒ SignedHttpExchange

Accepts two args representing a request-response pair:

url      - request url (string)
response - an array, equivalent to Rack's one: [status_code, headers, body]


23
24
25
26
27
28
29
30
31
# File 'lib/web_package/signed_http_exchange.rb', line 23

def initialize(url = MOCK_URL, response = MOCK_RESP)
  @uri    = build_uri_from url
  @url    = @uri.to_s
  @inner  = InnerResponse.new(*response)
  @signer = Signer.take

  @digest, @payload_body = MICE.new.encode @inner.payload
  @inner.headers.merge! 'digest' => "mi-sha256-03=#{base64(@digest)}"
end

Instance Method Details

#bodyObject



38
39
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/web_package/signed_http_exchange.rb', line 38

def body
  return @body if @body
  buffer = ''

  # 1. 8 bytes consisting of the ASCII characters "sxg1" followed by 4
  #    0x00 bytes, to serve as a file signature.  This is redundant with
  #    the MIME type, and recipients that receive both MUST check that
  #    they match and stop parsing if they don't.
  # TODO: The implementation of the final RFC MUST use the following line:
  # buffer << "sxg1\x00\x00\x00\x00"
  buffer << "sxg1-b3\x00"

  # 2.  2 bytes storing a big-endian integer "fallbackUrlLength".
  buffer << [@url.bytesize].pack('S>')

  # 3.  "fallbackUrlLength" bytes holding a "fallbackUrl", which MUST be
  #     an absolute URL with a scheme of "https".
  buffer << @url

  # 4.  3 bytes storing a big-endian integer "sigLength".  If this is
  #     larger than 16384 (16*1024), parsing MUST fail.
  if signature.bytesize > SIGNATURE_MAX_SIZE
    raise Errors::BodyEncodingError, 'Structured Signature Header length is too large: '\
          "#{signature.bytesize} bytes, max: #{SIGNATURE_MAX_SIZE} bytes."
  end
  buffer << [signature.bytesize].pack('L>').byteslice(-3, 3)

  # 5.  3 bytes storing a big-endian integer "headerLength".  If this is
  #     larger than 524288 (512*1024), parsing MUST fail.
  if cbor_encoded_headers.bytesize > HEADERS_MAX_SIZE
    raise Errors::BodyEncodingError, 'Response Headers length is too large: '\
          "#{cbor_encoded_headers.bytesize} bytes, max: #{HEADERS_MAX_SIZE} bytes."
  end
  buffer << [cbor_encoded_headers.bytesize].pack('L>').byteslice(-3, 3)

  # 6.  "sigLength" bytes holding the "Signature" header field's value
  #     (Section 3.1).
  buffer << signature

  # 7.  "headerLength" bytes holding "signedHeaders", the canonical
  #     serialization (Section 3.4) of the CBOR representation of the
  #     response headers of the exchange represented by the "application/
  #     signed-exchange" resource (Section 3.2), excluding the
  #     "Signature" header field.
  buffer << cbor_encoded_headers

  # 8.  The payload body (Section 3.3 of [RFC7230]) of the exchange
  #     represented by the "application/signed-exchange" resource.
  #     Note that the use of the payload body here means that a
  #     "Transfer-Encoding" header field inside the "application/signed-
  #     exchange" header block has no effect.  A "Transfer-Encoding"
  #     header field on the outer HTTP response that transfers this
  #     resource still has its normal effect.
  buffer << @payload_body

  @body = buffer
end

#headersObject



33
34
35
# File 'lib/web_package/signed_http_exchange.rb', line 33

def headers
  Settings.headers
end

#to_rack_responseObject



96
97
98
# File 'lib/web_package/signed_http_exchange.rb', line 96

def to_rack_response
  [200, headers, [body]]
end