in Sources/NIOHTTP2/HTTP2FrameParser.swift [997:1187]
mutating func encode(frame: HTTP2Frame, to buf: inout ByteBuffer) throws -> IOData? {
// note our starting point
let start = buf.writerIndex
// +-----------------------------------------------+
// | Length (24) |
// +---------------+---------------+---------------+
// | Type (8) | Flags (8) |
// +-+-------------+---------------+-------------------------------+
// |R| Stream Identifier (31) |
// +=+=============================================================+
// | Frame Payload (0...) ...
// +---------------------------------------------------------------+
// skip 24-bit length for now, we'll fill that in later
buf.moveWriterIndex(forwardBy: 3)
// 8-bit type
buf.writeInteger(frame.payload.code)
// skip the 8 bit flags for now, we'll fill it in later as well.
let flagsIndex = buf.writerIndex
var flags = FrameFlags()
buf.moveWriterIndex(forwardBy: 1)
// 32-bit stream identifier -- ensuring the top bit is empty
buf.writeInteger(Int32(frame.streamID))
// frame payload follows, which depends on the frame type itself
let payloadStart = buf.writerIndex
let extraFrameData: IOData?
let payloadSize: Int
switch frame.payload {
case .data(let dataContent):
if dataContent.paddingBytes != nil {
// we don't support sending padded frames just now
throw NIOHTTP2Errors.unsupported(info: "Padding is not supported on sent frames at this time")
}
if dataContent.endStream {
flags.insert(.endStream)
}
extraFrameData = dataContent.data
payloadSize = dataContent.data.readableBytes
case .headers(let headerData):
if headerData.paddingBytes != nil {
// we don't support sending padded frames just now
throw NIOHTTP2Errors.unsupported(info: "Padding is not supported on sent frames at this time")
}
flags.insert(.endHeaders)
if headerData.endStream {
flags.insert(.endStream)
}
if let priority = headerData.priorityData {
flags.insert(.priority)
var dependencyRaw = UInt32(priority.dependency)
if priority.exclusive {
dependencyRaw |= 0x8000_0000
}
buf.writeInteger(dependencyRaw)
buf.writeInteger(priority.weight)
}
try self.headerEncoder.encode(headers: headerData.headers, to: &buf)
payloadSize = buf.writerIndex - payloadStart
extraFrameData = nil
case .priority(let priorityData):
var raw = UInt32(priorityData.dependency)
if priorityData.exclusive {
raw |= 0x8000_0000
}
buf.writeInteger(raw)
buf.writeInteger(priorityData.weight)
extraFrameData = nil
payloadSize = 5
case .rstStream(let errcode):
buf.writeInteger(UInt32(errcode.networkCode))
payloadSize = 4
extraFrameData = nil
case .settings(.settings(let settings)):
for setting in settings {
buf.writeInteger(setting.parameter.networkRepresentation)
buf.writeInteger(setting._value)
}
payloadSize = settings.count * 6
extraFrameData = nil
case .settings(.ack):
payloadSize = 0
extraFrameData = nil
flags.insert(.ack)
case .pushPromise(let pushPromiseData):
if pushPromiseData.paddingBytes != nil {
// we don't support sending padded frames just now
throw NIOHTTP2Errors.unsupported(info: "Padding is not supported on sent frames at this time")
}
let streamVal: UInt32 = UInt32(pushPromiseData.pushedStreamID)
buf.writeInteger(streamVal)
try self.headerEncoder.encode(headers: pushPromiseData.headers, to: &buf)
payloadSize = buf.writerIndex - payloadStart
extraFrameData = nil
flags.insert(.endHeaders)
case .ping(let pingData, let ack):
withUnsafeBytes(of: pingData.bytes) { ptr -> Void in
_ = buf.writeBytes(ptr)
}
if ack {
flags.insert(.ack)
}
payloadSize = 8
extraFrameData = nil
case .goAway(let lastStreamID, let errorCode, let opaqueData):
let streamVal: UInt32 = UInt32(lastStreamID) & ~0x8000_0000
buf.writeInteger(streamVal)
buf.writeInteger(UInt32(errorCode.networkCode))
if let data = opaqueData {
payloadSize = data.readableBytes + 8
extraFrameData = .byteBuffer(data)
} else {
payloadSize = 8
extraFrameData = nil
}
case .windowUpdate(let size):
buf.writeInteger(UInt32(size) & ~0x8000_0000)
payloadSize = 4
extraFrameData = nil
case .alternativeService(let origin, let field):
if let org = origin {
buf.moveWriterIndex(forwardBy: 2)
let start = buf.writerIndex
buf.writeString(org)
buf.setInteger(UInt16(buf.writerIndex - start), at: payloadStart)
} else {
buf.writeInteger(UInt16(0))
}
if let value = field {
payloadSize = buf.writerIndex - payloadStart + value.readableBytes
extraFrameData = .byteBuffer(value)
} else {
payloadSize = buf.writerIndex - payloadStart
extraFrameData = nil
}
case .origin(let origins):
for origin in origins {
let sizeLoc = buf.writerIndex
buf.moveWriterIndex(forwardBy: 2)
let start = buf.writerIndex
buf.writeString(origin)
buf.setInteger(UInt16(buf.writerIndex - start), at: sizeLoc)
}
payloadSize = buf.writerIndex - payloadStart
extraFrameData = nil
}
// Confirm we're not about to violate SETTINGS_MAX_FRAME_SIZE.
guard payloadSize <= Int(self.maxFrameSize) else {
throw InternalError.codecError(code: .frameSizeError)
}
// Write the frame data. This is the payload size and the flags byte.
buf.writePayloadSize(payloadSize, at: start)
buf.setInteger(flags.rawValue, at: flagsIndex)
// all bytes to write are in the provided buffer now
return extraFrameData
}