1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 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 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
# frozen_string_literal: true
require 'zlib'
module Aws
module EventStream
# This class provides #encode method for encoding
# Aws::EventStream::Message into binary.
#
# * {#encode} - encode Aws::EventStream::Message into binary
# when output IO-like object is provided, binary string
# would be written to IO. If not, the encoded binary string
# would be returned directly
#
# ## Examples
#
# message = Aws::EventStream::Message.new(
# headers: {
# "foo" => Aws::EventStream::HeaderValue.new(
# value: "bar", type: "string"
# )
# },
# payload: "payload"
# )
# encoder = Aws::EventsStream::Encoder.new
# file = Tempfile.new
#
# # encode into IO ouput
# encoder.encode(message, file)
#
# # get encoded binary string
# encoded_message = encoder.encode(message)
#
# file.read == encoded_message
# # => true
#
class Encoder
# bytes of total overhead in a message, including prelude
# and 4 bytes total message crc checksum
OVERHEAD_LENGTH = 16
# Maximum header length allowed (after encode) 128kb
MAX_HEADERS_LENGTH = 131072
# Maximum payload length allowed (after encode) 16mb
MAX_PAYLOAD_LENGTH = 16777216
# Encodes Aws::EventStream::Message to output IO when
# provided, else return the encoded binary string
#
# @param [Aws::EventStream::Message] message
#
# @param [IO#write, nil] io An IO-like object that
# responds to `#write`, encoded message will be
# written to this IO when provided
#
# @return [nil, String] when output IO is provided,
# encoded message will be written to that IO, nil
# will be returned. Else, encoded binary string is
# returned.
def encode(message, io = nil)
encoded = encode_message(message)
if io
io.write(encoded)
io.close
else
encoded
end
end
# Encodes an Aws::EventStream::Message
# into String
#
# @param [Aws::EventStream::Message] message
#
# @return [String]
def encode_message(message)
# create context buffer with encode headers
encoded_header = encode_headers(message)
header_length = encoded_header.bytesize
# encode payload
if message.payload.length > MAX_PAYLOAD_LENGTH
raise Aws::EventStream::Errors::EventPayloadLengthExceedError.new
end
encoded_payload = message.payload.read
total_length = header_length + encoded_payload.bytesize + OVERHEAD_LENGTH
# create message buffer with prelude section
encoded_prelude = encode_prelude(total_length, header_length)
# append message context (headers, payload)
encoded_content = [
encoded_prelude,
encoded_header,
encoded_payload,
].pack('a*a*a*')
# append message checksum
message_checksum = Zlib.crc32(encoded_content)
[encoded_content, message_checksum].pack('a*N')
end
# Encodes headers part of an Aws::EventStream::Message
# into String
#
# @param [Aws::EventStream::Message] message
#
# @return [String]
def encode_headers(message)
header_entries = message.headers.map do |key, value|
encoded_key = [key.bytesize, key].pack('Ca*')
# header value
pattern, value_length, type_index = Types.pattern[value.type]
encoded_value = [type_index].pack('C')
# boolean types doesn't need to specify value
next [encoded_key, encoded_value].pack('a*a*') if !!pattern == pattern
encoded_value = [encoded_value, value.value.bytesize].pack('a*S>') unless value_length
[
encoded_key,
encoded_value,
pattern ? [value.value].pack(pattern) : value.value,
].pack('a*a*a*')
end
header_entries.join.tap do |encoded_header|
break encoded_header if encoded_header.bytesize <= MAX_HEADERS_LENGTH
raise Aws::EventStream::Errors::EventHeadersLengthExceedError.new
end
end
private
def encode_prelude(total_length, headers_length)
prelude_body = [total_length, headers_length].pack('NN')
checksum = Zlib.crc32(prelude_body)
[prelude_body, checksum].pack('a*N')
end
end
end
end
|