2017-06-28 21:13:10 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This source file is part of the SwiftNIO open source project
|
|
|
|
//
|
|
|
|
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
|
|
|
|
// Licensed under Apache License v2.0
|
|
|
|
//
|
|
|
|
// See LICENSE.txt for license information
|
|
|
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
import NIO
|
2017-11-16 22:20:13 +08:00
|
|
|
import CNIOHTTPParser
|
2017-06-28 21:13:10 +08:00
|
|
|
|
2017-11-10 12:19:35 +08:00
|
|
|
private struct HTTPParserState {
|
|
|
|
var dataAwaitingState: DataAwaitingState = .messageBegin
|
|
|
|
var currentHeaders: HTTPHeaders?
|
|
|
|
var currentUri: String?
|
|
|
|
var currentStatus: String?
|
|
|
|
var currentHeaderName: String?
|
|
|
|
var slice: (readerIndex: Int, length: Int)?
|
|
|
|
var readerIndexAdjustment = 0
|
|
|
|
// This is set before http_parser_execute(...) is called and set to nil again after it finish
|
|
|
|
var baseAddress: UnsafePointer<UInt8>?
|
2018-04-06 18:26:32 +08:00
|
|
|
var currentError: HTTPParserError?
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2017-11-10 12:19:35 +08:00
|
|
|
enum DataAwaitingState {
|
2017-06-29 19:20:52 +08:00
|
|
|
case messageBegin
|
2017-11-02 20:50:46 +08:00
|
|
|
case status
|
2017-06-29 19:20:52 +08:00
|
|
|
case url
|
|
|
|
case headerField
|
|
|
|
case headerValue
|
|
|
|
case body
|
|
|
|
}
|
|
|
|
|
2017-11-10 12:19:35 +08:00
|
|
|
mutating func reset() {
|
|
|
|
self.currentHeaders = nil
|
|
|
|
self.currentUri = nil
|
|
|
|
self.currentStatus = nil
|
|
|
|
self.currentHeaderName = nil
|
|
|
|
self.slice = nil
|
|
|
|
self.readerIndexAdjustment = 0
|
2017-06-29 19:20:52 +08:00
|
|
|
}
|
|
|
|
|
2017-11-10 12:19:35 +08:00
|
|
|
var cumulationBuffer: ByteBuffer?
|
|
|
|
|
|
|
|
mutating func readCurrentString() -> String {
|
|
|
|
let (index, length) = self.slice!
|
2018-01-09 17:32:39 +08:00
|
|
|
let string = self.cumulationBuffer!.getString(at: index, length: length)!
|
2017-11-10 12:19:35 +08:00
|
|
|
self.slice = nil
|
2017-11-02 20:50:46 +08:00
|
|
|
return string
|
|
|
|
}
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2017-11-10 12:19:35 +08:00
|
|
|
mutating func complete(state: DataAwaitingState) {
|
2017-06-29 19:20:52 +08:00
|
|
|
switch state {
|
|
|
|
case .messageBegin:
|
2017-11-10 12:19:35 +08:00
|
|
|
assert(self.slice == nil, "non-empty slice on begin (\(self.slice!))")
|
2017-06-29 19:20:52 +08:00
|
|
|
case .headerField:
|
2017-11-10 12:19:35 +08:00
|
|
|
assert(self.currentUri != nil || self.currentStatus != nil, "URI or Status not set before header field")
|
|
|
|
self.currentHeaderName = readCurrentString()
|
2017-06-29 19:20:52 +08:00
|
|
|
case .headerValue:
|
2017-11-10 12:19:35 +08:00
|
|
|
assert(self.currentUri != nil || self.currentStatus != nil, "URI or Status not set before header field")
|
|
|
|
if self.currentHeaders == nil {
|
|
|
|
self.currentHeaders = HTTPHeaders()
|
2017-11-02 20:50:46 +08:00
|
|
|
}
|
2017-11-10 12:19:35 +08:00
|
|
|
self.currentHeaders!.add(name: self.currentHeaderName!, value: readCurrentString())
|
2017-06-29 19:20:52 +08:00
|
|
|
case .url:
|
2017-11-10 12:19:35 +08:00
|
|
|
assert(self.currentUri == nil)
|
|
|
|
self.currentUri = readCurrentString()
|
2017-11-02 20:50:46 +08:00
|
|
|
case .status:
|
2017-11-10 12:19:35 +08:00
|
|
|
assert(self.currentStatus == nil)
|
|
|
|
self.currentStatus = readCurrentString()
|
2017-06-29 19:20:52 +08:00
|
|
|
case .body:
|
2017-11-10 12:19:35 +08:00
|
|
|
self.slice = nil
|
2017-06-29 19:20:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-10 12:19:35 +08:00
|
|
|
func calculateIndex(data: UnsafePointer<Int8>, length: Int) -> Int {
|
|
|
|
return data.withMemoryRebound(to: UInt8.self, capacity: length, { p in
|
|
|
|
return self.baseAddress!.distance(to: p)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
mutating func storeSlice(currentState: DataAwaitingState,
|
|
|
|
data: UnsafePointer<Int8>!,
|
|
|
|
len: Int,
|
|
|
|
previousComplete: (inout HTTPParserState, DataAwaitingState) -> Void) -> Void {
|
|
|
|
if currentState != self.dataAwaitingState {
|
|
|
|
let oldState = self.dataAwaitingState
|
|
|
|
self.dataAwaitingState = currentState
|
|
|
|
previousComplete(&self, oldState)
|
2017-06-28 21:13:10 +08:00
|
|
|
}
|
2017-11-10 12:19:35 +08:00
|
|
|
if let slice = self.slice {
|
|
|
|
// If we had a slice stored before we just need to update the length
|
|
|
|
self.slice = (slice.readerIndex, slice.length + len)
|
|
|
|
} else {
|
|
|
|
// Store the slice
|
|
|
|
let index = self.calculateIndex(data: data, length: len)
|
|
|
|
assert(index >= 0)
|
|
|
|
self.slice = (index, len)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private protocol AnyHTTPDecoder: class {
|
|
|
|
var state: HTTPParserState { get set }
|
2018-04-04 22:03:00 +08:00
|
|
|
var pendingInOut: [NIOAny] { get set }
|
2017-12-14 01:10:44 +08:00
|
|
|
func popRequestMethod() -> HTTPMethod?
|
2017-11-10 12:19:35 +08:00
|
|
|
}
|
|
|
|
|
2017-12-14 01:10:44 +08:00
|
|
|
/// A `ChannelInboundHandler` used to decode HTTP requests. See the documentation
|
|
|
|
/// on `HTTPDecoder` for more.
|
2017-12-13 23:38:40 +08:00
|
|
|
///
|
|
|
|
/// While the `HTTPRequestDecoder` does not currently have a specific ordering requirement in the
|
|
|
|
/// `ChannelPipeline` (unlike `HTTPResponseDecoder`), it is possible that it will develop one. For
|
|
|
|
/// that reason, applications should try to ensure that the `HTTPRequestDecoder` *later* in the
|
|
|
|
/// `ChannelPipeline` than the `HTTPResponseEncoder`.
|
|
|
|
///
|
|
|
|
/// Rather than set this up manually, consider using `ChannelPipeline.addHTTPServerHandlers`.
|
2017-12-14 01:10:44 +08:00
|
|
|
public final class HTTPRequestDecoder: HTTPDecoder<HTTPServerRequestPart> {
|
2017-11-10 12:19:35 +08:00
|
|
|
public convenience init() {
|
2017-12-14 01:10:44 +08:00
|
|
|
self.init(type: HTTPServerRequestPart.self)
|
2017-06-28 21:13:10 +08:00
|
|
|
}
|
2017-11-10 12:19:35 +08:00
|
|
|
}
|
2017-06-28 21:13:10 +08:00
|
|
|
|
2017-12-14 01:10:44 +08:00
|
|
|
/// A `ChannelInboundHandler` used to decode HTTP responses. See the documentation
|
|
|
|
/// on `HTTPDecoder` for more.
|
2017-12-13 23:38:40 +08:00
|
|
|
///
|
|
|
|
/// The `HTTPResponseDecoder` must be placed later in the channel pipeline than the `HTTPRequestEncoder`,
|
|
|
|
/// as it needs to see the outbound messages in order to keep track of what the HTTP request methods
|
|
|
|
/// were for accurate decoding.
|
|
|
|
///
|
|
|
|
/// Rather than set this up manually, consider using `ChannelPipeline.addHTTPClientHandlers`.
|
2017-12-14 01:10:44 +08:00
|
|
|
public final class HTTPResponseDecoder: HTTPDecoder<HTTPClientResponsePart>, ChannelOutboundHandler {
|
|
|
|
public typealias OutboundIn = HTTPClientRequestPart
|
|
|
|
public typealias OutboundOut = HTTPClientRequestPart
|
|
|
|
|
|
|
|
/// A FIFO buffer used to store the HTTP request verbs we've seen, to ensure
|
|
|
|
/// we handle HTTP HEAD responses correctly.
|
|
|
|
///
|
|
|
|
/// Because we have to handle pipelining, this is a FIFO buffer instead of a single
|
|
|
|
/// state variable. However, most users will never pipeline, so we initialize the buffer
|
|
|
|
/// to a base size of 1 to avoid allocating too much memory in the average case.
|
|
|
|
private var methods: CircularBuffer<HTTPMethod> = CircularBuffer(initialRingCapacity: 1)
|
|
|
|
|
|
|
|
/// The method of the request the next response will be responding to.
|
|
|
|
fileprivate override func popRequestMethod() -> HTTPMethod? {
|
|
|
|
return methods.removeFirst()
|
|
|
|
}
|
|
|
|
|
2017-11-10 12:19:35 +08:00
|
|
|
public convenience init() {
|
2017-12-14 01:10:44 +08:00
|
|
|
self.init(type: HTTPClientResponsePart.self)
|
|
|
|
}
|
|
|
|
|
|
|
|
public func write(ctx: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
|
2018-03-03 16:02:49 +08:00
|
|
|
if case .head(let head) = unwrapOutboundIn(data) {
|
2017-12-14 01:10:44 +08:00
|
|
|
methods.append(head.method)
|
|
|
|
}
|
|
|
|
|
2018-02-13 19:13:30 +08:00
|
|
|
ctx.write(data, promise: promise)
|
2017-11-02 20:50:46 +08:00
|
|
|
}
|
2017-11-10 12:19:35 +08:00
|
|
|
}
|
|
|
|
|
2017-12-01 00:07:41 +08:00
|
|
|
/// A `ChannelInboundHandler` that parses HTTP/1-style messages, converting them from
|
|
|
|
/// unstructured bytes to a sequence of HTTP messages.
|
|
|
|
///
|
|
|
|
/// The `HTTPDecoder` is a generic channel handler which can produce messages in
|
|
|
|
/// either the form of `HTTPClientResponsePart` or `HTTPServerRequestPart`: that is,
|
|
|
|
/// it produces messages that correspond to the semantic units of HTTP produced by
|
|
|
|
/// the remote peer.
|
2017-12-14 01:10:44 +08:00
|
|
|
public class HTTPDecoder<HTTPMessageT>: ByteToMessageDecoder, AnyHTTPDecoder {
|
2017-11-10 12:19:35 +08:00
|
|
|
public typealias InboundIn = ByteBuffer
|
|
|
|
public typealias InboundOut = HTTPMessageT
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2017-11-10 12:19:35 +08:00
|
|
|
private var parser = http_parser()
|
|
|
|
private var settings = http_parser_settings()
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2018-04-04 22:03:00 +08:00
|
|
|
fileprivate var pendingInOut: [NIOAny] = []
|
2017-11-10 12:19:35 +08:00
|
|
|
fileprivate var state = HTTPParserState()
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2017-12-14 01:10:44 +08:00
|
|
|
fileprivate init(type: HTTPMessageT.Type) {
|
2017-11-10 12:19:35 +08:00
|
|
|
/* this is a private init, the public versions only allow HTTPClientResponsePart and HTTPServerRequestPart */
|
|
|
|
assert(HTTPMessageT.self == HTTPClientResponsePart.self || HTTPMessageT.self == HTTPServerRequestPart.self)
|
2017-07-10 19:45:59 +08:00
|
|
|
}
|
2017-12-14 01:10:44 +08:00
|
|
|
|
|
|
|
/// The most recent method seen by request handlers.
|
|
|
|
///
|
|
|
|
/// Naturally, in the base case this returns nil, as servers never issue requests!
|
|
|
|
fileprivate func popRequestMethod() -> HTTPMethod? { return nil }
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2017-11-02 20:50:46 +08:00
|
|
|
private func newRequestHead(_ parser: UnsafeMutablePointer<http_parser>!) -> HTTPRequestHead {
|
|
|
|
let method = HTTPMethod.from(httpParserMethod: http_method(rawValue: parser.pointee.method))
|
|
|
|
let version = HTTPVersion(major: parser.pointee.http_major, minor: parser.pointee.http_minor)
|
|
|
|
let request = HTTPRequestHead(version: version, method: method, uri: state.currentUri!, headers: state.currentHeaders ?? HTTPHeaders())
|
|
|
|
state.currentHeaders = nil
|
|
|
|
return request
|
|
|
|
}
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2017-11-02 20:50:46 +08:00
|
|
|
private func newResponseHead(_ parser: UnsafeMutablePointer<http_parser>!) -> HTTPResponseHead {
|
2018-03-16 22:34:49 +08:00
|
|
|
let status = HTTPResponseStatus(statusCode: Int(parser.pointee.status_code), reasonPhrase: state.currentStatus!)
|
2017-11-02 20:50:46 +08:00
|
|
|
let version = HTTPVersion(major: parser.pointee.http_major, minor: parser.pointee.http_minor)
|
|
|
|
let response = HTTPResponseHead(version: version, status: status, headers: state.currentHeaders ?? HTTPHeaders())
|
|
|
|
state.currentHeaders = nil
|
|
|
|
return response
|
|
|
|
}
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2018-02-20 02:10:21 +08:00
|
|
|
public func decoderAdded(ctx: ChannelHandlerContext) {
|
2017-11-10 12:19:35 +08:00
|
|
|
if HTTPMessageT.self == HTTPServerRequestPart.self {
|
2018-01-30 19:14:03 +08:00
|
|
|
c_nio_http_parser_init(&parser, HTTP_REQUEST)
|
2017-11-10 12:19:35 +08:00
|
|
|
} else if HTTPMessageT.self == HTTPClientResponsePart.self {
|
2018-01-30 19:14:03 +08:00
|
|
|
c_nio_http_parser_init(&parser, HTTP_RESPONSE)
|
2017-11-10 12:19:35 +08:00
|
|
|
} else {
|
|
|
|
fatalError("the impossible happened: MsgT neither HTTPClientRequestPart nor HTTPClientResponsePart but \(HTTPMessageT.self)")
|
2017-11-02 20:50:46 +08:00
|
|
|
}
|
2017-06-28 21:13:10 +08:00
|
|
|
|
2017-11-02 20:50:46 +08:00
|
|
|
parser.data = Unmanaged.passUnretained(ctx).toOpaque()
|
2017-06-28 21:13:10 +08:00
|
|
|
|
2018-01-30 19:14:03 +08:00
|
|
|
c_nio_http_parser_settings_init(&settings)
|
2017-06-28 21:13:10 +08:00
|
|
|
|
2017-11-02 20:50:46 +08:00
|
|
|
settings.on_message_begin = { parser in
|
2017-11-10 12:19:35 +08:00
|
|
|
let handler = evacuateHTTPDecoder(parser)
|
2017-06-28 21:13:10 +08:00
|
|
|
handler.state.reset()
|
|
|
|
|
|
|
|
return 0
|
|
|
|
}
|
2017-06-29 19:20:52 +08:00
|
|
|
|
2017-11-02 20:50:46 +08:00
|
|
|
settings.on_headers_complete = { parser in
|
2017-11-10 12:19:35 +08:00
|
|
|
let handler = evacuateHTTPDecoder(parser)
|
2017-06-29 19:20:52 +08:00
|
|
|
|
2017-11-10 12:19:35 +08:00
|
|
|
handler.state.complete(state: handler.state.dataAwaitingState)
|
2017-06-29 19:20:52 +08:00
|
|
|
handler.state.dataAwaitingState = .body
|
|
|
|
|
2017-11-10 12:19:35 +08:00
|
|
|
switch handler {
|
|
|
|
case let handler as HTTPRequestDecoder:
|
2017-11-02 20:50:46 +08:00
|
|
|
let head = handler.newRequestHead(parser)
|
2018-04-06 18:26:32 +08:00
|
|
|
guard head.version.major == 1 else {
|
|
|
|
handler.state.currentError = HTTPParserError.invalidVersion
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
2018-04-04 22:03:00 +08:00
|
|
|
handler.pendingInOut.append(handler.wrapInboundOut(HTTPServerRequestPart.head(head)))
|
2017-12-14 01:10:44 +08:00
|
|
|
return 0
|
2017-11-10 12:19:35 +08:00
|
|
|
case let handler as HTTPResponseDecoder:
|
2017-11-02 20:50:46 +08:00
|
|
|
let head = handler.newResponseHead(parser)
|
2018-04-06 18:26:32 +08:00
|
|
|
guard head.version.major == 1 else {
|
|
|
|
handler.state.currentError = HTTPParserError.invalidVersion
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
2018-04-04 22:03:00 +08:00
|
|
|
handler.pendingInOut.append(handler.wrapInboundOut(HTTPClientResponsePart.head(head)))
|
2017-12-14 01:10:44 +08:00
|
|
|
|
|
|
|
// http_parser doesn't correctly handle responses to HEAD requests. We have to do something
|
|
|
|
// annoyingly opaque here, and in those cases return 1 instead of 0. This forces http_parser
|
|
|
|
// to not expect a request body.
|
|
|
|
//
|
|
|
|
// See also: https://github.com/nodejs/http-parser/issues/251. Note that despite the text in
|
|
|
|
// that issue, http_parser *does* seem to handle the case of 204 and friends: it's just HEAD
|
|
|
|
// that doesn't work.
|
|
|
|
//
|
|
|
|
// Note that this issue is the *entire* reason this must be a duplex: we need to know what the
|
|
|
|
// request verb is that we're seeing a response for.
|
|
|
|
let method = handler.popRequestMethod()
|
|
|
|
return method == .HEAD ? 1 : 0
|
2017-11-10 12:19:35 +08:00
|
|
|
default:
|
|
|
|
fatalError("the impossible happened: handler neither a HTTPRequestDecoder nor a HTTPResponseDecoder which should be impossible")
|
2017-09-25 22:33:02 +08:00
|
|
|
}
|
2017-06-28 21:13:10 +08:00
|
|
|
}
|
|
|
|
|
2017-11-02 20:50:46 +08:00
|
|
|
settings.on_body = { parser, data, len in
|
2017-11-10 12:19:35 +08:00
|
|
|
let handler = evacuateHTTPDecoder(parser)
|
2017-06-29 19:20:52 +08:00
|
|
|
assert(handler.state.dataAwaitingState == .body)
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2017-07-09 19:25:43 +08:00
|
|
|
// Calculate the index of the data in the cumulationBuffer so we can slice out the ByteBuffer without doing any memory copy
|
2017-11-10 12:19:35 +08:00
|
|
|
let index = handler.state.calculateIndex(data: data!, length: len)
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2017-12-15 19:24:09 +08:00
|
|
|
let slice = handler.state.cumulationBuffer!.getSlice(at: index, length: len)!
|
2018-04-04 22:03:00 +08:00
|
|
|
switch handler {
|
|
|
|
case let handler as HTTPRequestDecoder:
|
|
|
|
handler.pendingInOut.append(handler.wrapInboundOut(HTTPServerRequestPart.body(slice)))
|
|
|
|
case let handler as HTTPResponseDecoder:
|
|
|
|
handler.pendingInOut.append(handler.wrapInboundOut(HTTPClientResponsePart.body(slice)))
|
|
|
|
default:
|
|
|
|
fatalError("the impossible happened: handler neither a HTTPRequestDecoder nor a HTTPResponseDecoder which should be impossible")
|
2017-09-25 22:33:02 +08:00
|
|
|
}
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2017-06-28 21:13:10 +08:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2017-11-02 20:50:46 +08:00
|
|
|
settings.on_header_field = { parser, data, len in
|
2017-11-10 12:19:35 +08:00
|
|
|
let handler = evacuateHTTPDecoder(parser)
|
2017-06-28 21:13:10 +08:00
|
|
|
|
2017-11-10 12:19:35 +08:00
|
|
|
handler.state.storeSlice(currentState: .headerField, data: data, len: len) { parserState, previousState in
|
|
|
|
parserState.complete(state: previousState)
|
2017-06-28 21:13:10 +08:00
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2017-11-02 20:50:46 +08:00
|
|
|
settings.on_header_value = { parser, data, len in
|
2017-11-10 12:19:35 +08:00
|
|
|
let handler = evacuateHTTPDecoder(parser)
|
2017-06-28 21:13:10 +08:00
|
|
|
|
2017-11-10 12:19:35 +08:00
|
|
|
handler.state.storeSlice(currentState: .headerValue, data: data, len: len) { parserState, previousState in
|
|
|
|
parserState.complete(state: previousState)
|
2017-06-28 21:13:10 +08:00
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2017-11-02 20:50:46 +08:00
|
|
|
settings.on_status = { parser, data, len in
|
2017-11-10 12:19:35 +08:00
|
|
|
let handler = evacuateHTTPDecoder(parser)
|
|
|
|
assert(handler is HTTPResponseDecoder)
|
2017-11-02 20:50:46 +08:00
|
|
|
|
2017-11-10 12:19:35 +08:00
|
|
|
handler.state.storeSlice(currentState: .status, data: data, len: len) { parserState, previousState in
|
2017-11-02 20:50:46 +08:00
|
|
|
assert(previousState == .messageBegin, "expected: messageBegin, actual: \(previousState)")
|
2017-11-10 12:19:35 +08:00
|
|
|
parserState.complete(state: previousState)
|
2017-11-02 20:50:46 +08:00
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2017-11-02 20:50:46 +08:00
|
|
|
settings.on_url = { parser, data, len in
|
2017-11-10 12:19:35 +08:00
|
|
|
let handler = evacuateHTTPDecoder(parser)
|
|
|
|
assert(handler is HTTPRequestDecoder)
|
2017-06-28 21:13:10 +08:00
|
|
|
|
2017-11-10 12:19:35 +08:00
|
|
|
handler.state.storeSlice(currentState: .url, data: data, len: len) { parserState, previousState in
|
2017-06-29 19:20:52 +08:00
|
|
|
assert(previousState == .messageBegin, "expected: messageBegin, actual: \(previousState)")
|
2017-11-10 12:19:35 +08:00
|
|
|
parserState.complete(state: previousState)
|
2017-06-29 19:20:52 +08:00
|
|
|
}
|
2017-06-28 21:13:10 +08:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2017-11-02 20:50:46 +08:00
|
|
|
settings.on_message_complete = { parser in
|
2017-11-10 12:19:35 +08:00
|
|
|
let handler = evacuateHTTPDecoder(parser)
|
2017-06-29 19:20:52 +08:00
|
|
|
|
2017-11-10 12:19:35 +08:00
|
|
|
handler.state.complete(state: handler.state.dataAwaitingState)
|
2017-06-29 19:20:52 +08:00
|
|
|
handler.state.dataAwaitingState = .messageBegin
|
2017-09-25 22:33:02 +08:00
|
|
|
|
2017-11-02 20:50:46 +08:00
|
|
|
let trailers = handler.state.currentHeaders?.count ?? 0 > 0 ? handler.state.currentHeaders : nil
|
2018-04-04 22:03:00 +08:00
|
|
|
switch handler {
|
|
|
|
case let handler as HTTPRequestDecoder:
|
|
|
|
handler.pendingInOut.append(handler.wrapInboundOut(HTTPServerRequestPart.end(trailers)))
|
|
|
|
case let handler as HTTPResponseDecoder:
|
|
|
|
handler.pendingInOut.append(handler.wrapInboundOut(HTTPClientResponsePart.end(trailers)))
|
|
|
|
default:
|
|
|
|
fatalError("the impossible happened: handler neither a HTTPRequestDecoder nor a HTTPResponseDecoder which should be impossible")
|
2017-09-25 22:33:02 +08:00
|
|
|
}
|
2017-06-28 21:13:10 +08:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-29 22:52:38 +08:00
|
|
|
public func decoderRemoved(ctx: ChannelHandlerContext) {
|
2017-11-02 20:50:46 +08:00
|
|
|
// Remove the stored reference to ChannelHandlerContext
|
|
|
|
parser.data = UnsafeMutableRawPointer(bitPattern: 0x0000deadbeef0000)
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2017-11-02 20:50:46 +08:00
|
|
|
// Set the callbacks to nil as we dont need these anymore
|
|
|
|
settings.on_body = nil
|
|
|
|
settings.on_chunk_complete = nil
|
|
|
|
settings.on_url = nil
|
|
|
|
settings.on_status = nil
|
|
|
|
settings.on_chunk_header = nil
|
|
|
|
settings.on_chunk_complete = nil
|
|
|
|
settings.on_header_field = nil
|
|
|
|
settings.on_header_value = nil
|
|
|
|
settings.on_message_begin = nil
|
2017-06-28 21:13:10 +08:00
|
|
|
}
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2018-02-20 20:07:31 +08:00
|
|
|
public func decode(ctx: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState {
|
2017-07-10 19:45:59 +08:00
|
|
|
if let slice = state.slice {
|
2018-02-20 16:02:43 +08:00
|
|
|
// If we stored a slice before we need to ensure we move the readerIndex so we don't try to parse the data again. We
|
|
|
|
// also need to update the reader index to whatever it is now.
|
|
|
|
state.slice = (buffer.readerIndex, slice.length)
|
2017-07-10 19:45:59 +08:00
|
|
|
buffer.moveReaderIndex(forwardBy: state.readerIndexAdjustment)
|
|
|
|
}
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2017-10-11 00:12:02 +08:00
|
|
|
let result = try buffer.withVeryUnsafeBytes { (pointer) -> size_t in
|
2017-07-10 19:45:59 +08:00
|
|
|
state.baseAddress = pointer.baseAddress!.assumingMemoryBound(to: UInt8.self)
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2017-07-10 19:45:59 +08:00
|
|
|
let result = state.baseAddress!.withMemoryRebound(to: Int8.self, capacity: pointer.count, { p in
|
2018-01-30 19:14:03 +08:00
|
|
|
c_nio_http_parser_execute(&parser, &settings, p.advanced(by: buffer.readerIndex), buffer.readableBytes)
|
2017-06-28 21:13:10 +08:00
|
|
|
})
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2018-04-06 18:26:32 +08:00
|
|
|
if let error = state.currentError {
|
|
|
|
throw error
|
|
|
|
}
|
|
|
|
|
2017-07-10 19:45:59 +08:00
|
|
|
state.baseAddress = nil
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2017-11-02 20:50:46 +08:00
|
|
|
let errno = parser.http_errno
|
2017-07-10 19:45:59 +08:00
|
|
|
if errno != 0 {
|
2017-09-25 22:33:02 +08:00
|
|
|
throw HTTPParserError.httpError(fromCHTTPParserErrno: http_errno(rawValue: errno))!
|
2017-07-10 19:45:59 +08:00
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
2017-09-25 22:33:02 +08:00
|
|
|
|
2017-07-10 19:45:59 +08:00
|
|
|
if let slice = state.slice {
|
2018-02-20 16:02:43 +08:00
|
|
|
// If we have a slice, we need to preserve all of these bytes. To do that, we move the
|
|
|
|
// reader index to where the slice wants it, and then record how many readable bytes that leaves
|
|
|
|
// us with. Then, invalidate the reader index, as it's not stable across calls to decode()
|
|
|
|
// *anyway*, so we want to make sure we can see the bad value in debug errors.
|
2017-07-10 19:45:59 +08:00
|
|
|
buffer.moveReaderIndex(to: slice.readerIndex)
|
|
|
|
state.readerIndexAdjustment = buffer.readableBytes
|
2018-02-20 16:02:43 +08:00
|
|
|
state.slice = (-1, slice.length)
|
2018-02-20 20:07:31 +08:00
|
|
|
return .needMoreData
|
2017-07-10 19:45:59 +08:00
|
|
|
} else {
|
|
|
|
buffer.moveReaderIndex(forwardBy: result)
|
|
|
|
state.readerIndexAdjustment = 0
|
2018-02-20 20:07:31 +08:00
|
|
|
return .continue
|
2017-07-10 19:45:59 +08:00
|
|
|
}
|
2017-06-28 21:13:10 +08:00
|
|
|
}
|
2018-03-02 14:40:52 +08:00
|
|
|
|
2017-09-25 22:33:02 +08:00
|
|
|
public func channelReadComplete(ctx: ChannelHandlerContext) {
|
|
|
|
/* call all the callbacks generated while parsing */
|
2018-04-04 22:03:00 +08:00
|
|
|
let pending = self.pendingInOut
|
|
|
|
self.pendingInOut = []
|
|
|
|
pending.forEach { ctx.fireChannelRead($0) }
|
2017-09-29 21:41:17 +08:00
|
|
|
ctx.fireChannelReadComplete()
|
2017-09-25 22:33:02 +08:00
|
|
|
}
|
|
|
|
|
2017-06-28 21:13:10 +08:00
|
|
|
public func errorCaught(ctx: ChannelHandlerContext, error: Error) {
|
2018-02-13 19:13:30 +08:00
|
|
|
ctx.fireErrorCaught(error)
|
2017-06-28 21:13:10 +08:00
|
|
|
if error is HTTPParserError {
|
2017-11-02 20:50:46 +08:00
|
|
|
ctx.close(promise: nil)
|
2017-06-28 21:13:10 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-18 20:40:26 +08:00
|
|
|
extension HTTPDecoder {
|
2017-11-10 12:19:35 +08:00
|
|
|
public var cumulationBuffer: ByteBuffer? {
|
|
|
|
get {
|
|
|
|
return self.state.cumulationBuffer
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
self.state.cumulationBuffer = newValue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func evacuateChannelHandlerContext(_ opaqueContext: UnsafeMutablePointer<http_parser>!) -> ChannelHandlerContext {
|
2017-06-28 21:13:10 +08:00
|
|
|
return Unmanaged.fromOpaque(opaqueContext.pointee.data).takeUnretainedValue()
|
|
|
|
}
|
|
|
|
|
2017-11-10 12:19:35 +08:00
|
|
|
private func evacuateHTTPDecoder(_ opaqueContext: UnsafeMutablePointer<http_parser>!) -> AnyHTTPDecoder {
|
|
|
|
return evacuateChannelHandlerContext(opaqueContext).handler as! AnyHTTPDecoder
|
|
|
|
}
|
|
|
|
|
2017-06-28 21:13:10 +08:00
|
|
|
extension HTTPParserError {
|
2017-12-01 00:07:41 +08:00
|
|
|
/// Create a `HTTPParserError` from an error returned by `http_parser`.
|
|
|
|
///
|
|
|
|
/// - Parameter fromCHTTPParserErrno: The error from the underlying library.
|
|
|
|
/// - Returns: The corresponding `HTTPParserError`, or `nil` if there is no
|
|
|
|
/// corresponding error.
|
2017-06-28 21:13:10 +08:00
|
|
|
static func httpError(fromCHTTPParserErrno: http_errno) -> HTTPParserError? {
|
|
|
|
switch fromCHTTPParserErrno {
|
|
|
|
case HPE_INVALID_EOF_STATE:
|
|
|
|
return .invalidEOFState
|
|
|
|
case HPE_HEADER_OVERFLOW:
|
|
|
|
return .headerOverflow
|
|
|
|
case HPE_CLOSED_CONNECTION:
|
|
|
|
return .closedConnection
|
|
|
|
case HPE_INVALID_VERSION:
|
|
|
|
return .invalidVersion
|
|
|
|
case HPE_INVALID_STATUS:
|
|
|
|
return .invalidStatus
|
|
|
|
case HPE_INVALID_METHOD:
|
|
|
|
return .invalidMethod
|
|
|
|
case HPE_INVALID_URL:
|
|
|
|
return .invalidURL
|
|
|
|
case HPE_INVALID_HOST:
|
|
|
|
return .invalidHost
|
|
|
|
case HPE_INVALID_PORT:
|
|
|
|
return .invalidPort
|
|
|
|
case HPE_INVALID_PATH:
|
|
|
|
return .invalidPath
|
|
|
|
case HPE_INVALID_QUERY_STRING:
|
|
|
|
return .invalidQueryString
|
|
|
|
case HPE_INVALID_FRAGMENT:
|
|
|
|
return .invalidFragment
|
|
|
|
case HPE_LF_EXPECTED:
|
|
|
|
return .lfExpected
|
|
|
|
case HPE_INVALID_HEADER_TOKEN:
|
|
|
|
return .invalidHeaderToken
|
|
|
|
case HPE_INVALID_CONTENT_LENGTH:
|
|
|
|
return .invalidContentLength
|
|
|
|
case HPE_UNEXPECTED_CONTENT_LENGTH:
|
|
|
|
return .unexpectedContentLength
|
|
|
|
case HPE_INVALID_CHUNK_SIZE:
|
|
|
|
return .invalidChunkSize
|
|
|
|
case HPE_INVALID_CONSTANT:
|
|
|
|
return .invalidConstant
|
|
|
|
case HPE_STRICT:
|
|
|
|
return .strictModeAssertion
|
|
|
|
case HPE_PAUSED:
|
|
|
|
return .paused
|
|
|
|
case HPE_UNKNOWN:
|
|
|
|
return .unknown
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extension HTTPMethod {
|
2017-12-01 00:07:41 +08:00
|
|
|
/// Create a `HTTPMethod` from a given `http_method` produced by
|
|
|
|
/// `http_parser`.
|
|
|
|
///
|
|
|
|
/// - Parameter httpParserMethod: The method returned by `http_parser`.
|
|
|
|
/// - Returns: The corresponding `HTTPMethod`.
|
2017-11-02 20:50:46 +08:00
|
|
|
static func from(httpParserMethod: http_method) -> HTTPMethod {
|
2017-06-28 21:13:10 +08:00
|
|
|
switch httpParserMethod {
|
|
|
|
case HTTP_DELETE:
|
|
|
|
return .DELETE
|
|
|
|
case HTTP_GET:
|
|
|
|
return .GET
|
|
|
|
case HTTP_HEAD:
|
|
|
|
return .HEAD
|
|
|
|
case HTTP_POST:
|
|
|
|
return .POST
|
|
|
|
case HTTP_PUT:
|
|
|
|
return .PUT
|
|
|
|
case HTTP_CONNECT:
|
|
|
|
return .CONNECT
|
|
|
|
case HTTP_OPTIONS:
|
|
|
|
return .OPTIONS
|
|
|
|
case HTTP_TRACE:
|
|
|
|
return .TRACE
|
|
|
|
case HTTP_COPY:
|
|
|
|
return .COPY
|
|
|
|
case HTTP_LOCK:
|
|
|
|
return .LOCK
|
|
|
|
case HTTP_MKCOL:
|
|
|
|
return .MKCOL
|
|
|
|
case HTTP_MOVE:
|
|
|
|
return .MOVE
|
|
|
|
case HTTP_PROPFIND:
|
|
|
|
return .PROPFIND
|
|
|
|
case HTTP_PROPPATCH:
|
|
|
|
return .PROPPATCH
|
|
|
|
case HTTP_SEARCH:
|
|
|
|
return .SEARCH
|
|
|
|
case HTTP_UNLOCK:
|
|
|
|
return .UNLOCK
|
|
|
|
case HTTP_BIND:
|
|
|
|
return .BIND
|
|
|
|
case HTTP_REBIND:
|
|
|
|
return .REBIND
|
|
|
|
case HTTP_UNBIND:
|
|
|
|
return .UNBIND
|
|
|
|
case HTTP_ACL:
|
|
|
|
return .ACL
|
|
|
|
case HTTP_REPORT:
|
|
|
|
return .REPORT
|
|
|
|
case HTTP_MKACTIVITY:
|
|
|
|
return .MKACTIVITY
|
|
|
|
case HTTP_CHECKOUT:
|
|
|
|
return .CHECKOUT
|
|
|
|
case HTTP_MERGE:
|
|
|
|
return .MERGE
|
|
|
|
case HTTP_MSEARCH:
|
|
|
|
return .MSEARCH
|
|
|
|
case HTTP_NOTIFY:
|
|
|
|
return .NOTIFY
|
|
|
|
case HTTP_SUBSCRIBE:
|
|
|
|
return .SUBSCRIBE
|
|
|
|
case HTTP_UNSUBSCRIBE:
|
|
|
|
return .UNSUBSCRIBE
|
|
|
|
case HTTP_PATCH:
|
|
|
|
return .PATCH
|
|
|
|
case HTTP_PURGE:
|
|
|
|
return .PURGE
|
|
|
|
case HTTP_MKCALENDAR:
|
|
|
|
return .MKCALENDAR
|
|
|
|
case HTTP_LINK:
|
|
|
|
return .LINK
|
|
|
|
case HTTP_UNLINK:
|
|
|
|
return .UNLINK
|
|
|
|
default:
|
2017-11-02 20:50:46 +08:00
|
|
|
fatalError("Unexpected http_method \(httpParserMethod)")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|