Skip to content
Permalink
Browse files

HTTP/1 headers simplification & cleanup (#857)

Motivation:

The HTTP/1 headers were quite complicated, CoW-boxed and exposed their
guts (being a ByteBuffer). In Swift 5 this is no longer necessary
because of native UTF-8 Strings.

Modifications:

- make headers simply `[(String, String)]`
- remove `HTTPHeaderIndex`, `HTTPListHeaderIterator`, etc

Result:

- simpler and more performant code
  • Loading branch information...
weissi committed Mar 6, 2019
1 parent 8335074 commit 22e6cd3c6753138f8e2746914739ee1dbe4b4c61
@@ -19,7 +19,7 @@ token=$(create_token)
start_server "$token"
do_curl "$token" -H "foo: bar" --http1.0 \
"http://foobar.com/dynamic/info" > "$tmp/out"
if ! grep -q '("foo", "bar")' "$tmp/out"; then
if ! grep -q '(name: "foo", value: "bar")' "$tmp/out"; then
fail "couldn't find header in response"
fi
stop_server "$token"
@@ -39,8 +39,35 @@ extension Sequence where Self.Element == UInt8 {
///
/// - Parameter bytes: The string constant in the form of a collection of `UInt8`
/// - Returns: Whether the collection contains **EXACTLY** this array or no, but by ignoring case.
internal func compareCaseInsensitiveASCIIBytes<T: Sequence>(to bytes: T) -> Bool
internal func compareCaseInsensitiveASCIIBytes<T: Sequence>(to: T) -> Bool
where T.Element == UInt8 {
return self.elementsEqual(bytes, by: {return ($0 & 0xdf) == ($1 & 0xdf)})
// fast path: we can get the underlying bytes of both
let maybeMaybeResult = self.withContiguousStorageIfAvailable { lhsBuffer -> Bool? in
to.withContiguousStorageIfAvailable { rhsBuffer in
if lhsBuffer.count != rhsBuffer.count {
return false
}

for idx in 0 ..< lhsBuffer.count {
// let's hope this gets vectorised ;)
if lhsBuffer[idx] & 0xdf != rhsBuffer[idx] & 0xdf {
return false
}
}
return true
}
}

if let maybeResult = maybeMaybeResult, let result = maybeResult {
return result
} else {
return self.elementsEqual(to, by: {return ($0 & 0xdf) == ($1 & 0xdf)})
}
}
}

extension String {
internal func isEqualCaseInsensitiveASCIIBytes(to: String) -> Bool {
return self.utf8.compareCaseInsensitiveASCIIBytes(to: to.utf8)
}
}
@@ -15,6 +15,13 @@
import NIO
import CNIOHTTPParser

private extension UnsafeMutablePointer where Pointee == http_parser {
/// Returns the `KeepAliveState` for the current message that is parsed.
var keepAliveState: KeepAliveState {
return c_nio_http_should_keep_alive(self) == 0 ? .close : .keepAlive
}
}

private enum HTTPDecodingState {
case beforeRequest
case beforeURL
@@ -98,7 +105,8 @@ private class BetterHTTPParser {
versionMinor: Int(opaque!.pointee.http_minor),
statusCode: Int(opaque!.pointee.status_code),
isUpgrade: opaque!.pointee.upgrade != 0,
method: http_method(rawValue: opaque!.pointee.method)) {
method: http_method(rawValue: opaque!.pointee.method),
keepAliveState: opaque!.keepAliveState) {
case .normal:
return 0
case .skipBody:
@@ -237,7 +245,8 @@ private class BetterHTTPParser {
versionMinor: Int,
statusCode: Int,
isUpgrade: Bool,
method: http_method) -> MessageContinuation {
method: http_method,
keepAliveState: KeepAliveState) -> MessageContinuation {
switch self.decodingState {
case .headerValue:
self.finish { delegate, bytes in
@@ -256,7 +265,8 @@ private class BetterHTTPParser {
versionMinor: versionMinor,
isUpgrade: isUpgrade,
method: method,
statusCode: statusCode)
statusCode: statusCode,
keepAliveState: keepAliveState)
guard success else {
return .error(HPE_INVALID_VERSION)
}
@@ -372,7 +382,8 @@ private protocol HTTPDecoderDelegate {
versionMinor: Int,
isUpgrade: Bool,
method: http_method,
statusCode: Int) -> Bool
statusCode: Int,
keepAliveState: KeepAliveState) -> Bool
mutating func didFinishMessage()
}

@@ -501,7 +512,12 @@ public class HTTPDecoder<In, Out>: ByteToMessageDecoder, HTTPDecoderDelegate {
self.url = String(decoding: bytes, as: Unicode.UTF8.self)
}

func didFinishHead(versionMajor: Int, versionMinor: Int, isUpgrade: Bool, method: http_method, statusCode: Int) -> Bool {
func didFinishHead(versionMajor: Int,
versionMinor: Int,
isUpgrade: Bool,
method: http_method,
statusCode: Int,
keepAliveState: KeepAliveState) -> Bool {
let message: NIOAny

guard versionMajor == 1 else {
@@ -515,12 +531,14 @@ public class HTTPDecoder<In, Out>: ByteToMessageDecoder, HTTPDecoderDelegate {
let reqHead = HTTPRequestHead(version: .init(major: versionMajor, minor: versionMinor),
method: HTTPMethod.from(httpParserMethod: method),
rawURI: .string(self.url!),
headers: HTTPHeaders(self.headers))
headers: HTTPHeaders(self.headers,
keepAliveState: keepAliveState))
message = NIOAny(HTTPServerRequestPart.head(reqHead))
case .response:
let resHead: HTTPResponseHead = HTTPResponseHead(version: .init(major: versionMajor, minor: versionMinor),
status: .init(statusCode: statusCode),
headers: HTTPHeaders(self.headers))
headers: HTTPHeaders(self.headers,
keepAliveState: keepAliveState))
message = NIOAny(HTTPClientResponsePart.head(resHead))
}
self.url = nil
Oops, something went wrong.

0 comments on commit 22e6cd3

Please sign in to comment.
You can’t perform that action at this time.