Skip to content

Commit f1d8754

Browse files
authored
Add support for Ubuntu 16.04's packaged nghttp2. (#29)
Motivation: We'd done all our development against newer copies of nghttp2, that behaved a bit differently. We should support Ubuntu's copy, at least while nghttp2 is relevant to us. Modifications: - Shimmed over a version difference. - Fixed the tests to stop relying on differences. - Removed the Swift 4.0.3 dockerfiles as we don't support Swift 4.0 anyway. Result: Users can build against Ubuntu 16.04's nghttp2 package.
1 parent 38b8235 commit f1d8754

File tree

8 files changed

+81
-56
lines changed

8 files changed

+81
-56
lines changed

Sources/CNIONghttp2/empty.c

Lines changed: 0 additions & 13 deletions
This file was deleted.

Sources/CNIONghttp2/include/c_nio_nghttp2.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,21 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
//
1313
//===----------------------------------------------------------------------===//
14-
#ifndef C_NIO_NGHTTP2_H
15-
#define C_NIO_NGHTTP2_H
14+
#ifndef CNIONGHTTP2_H
15+
#define CNIONGHTTP2_H
1616

1717
#include <nghttp2/nghttp2.h>
1818

19+
// There are some shims we need to provide here.
20+
typedef int (*CNIONghttp2_nghttp2_error_callback)(nghttp2_session *session,
21+
const char *msg,
22+
size_t len,
23+
void *user_data);
24+
25+
void CNIONghttp2_nghttp2_session_callbacks_set_error_callback(
26+
nghttp2_session_callbacks *cbs,
27+
CNIONghttp2_nghttp2_error_callback error_callback);
28+
29+
int CNIONghttp2_nghttp2_version_number(void);
30+
1931
#endif

Sources/CNIONghttp2/shims.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftNIO open source project
4+
//
5+
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
#include <c_nio_nghttp2.h>
15+
16+
// This file provides an implementation of a number of shim functions to
17+
// handle different versions of nghttp2.
18+
19+
// This shim only works on recent versions of nghttp2, otherwise it does
20+
// nothing.
21+
void CNIONghttp2_nghttp2_session_callbacks_set_error_callback(
22+
nghttp2_session_callbacks *cbs,
23+
CNIONghttp2_nghttp2_error_callback error_callback) {
24+
#if NGHTTP2_VERSION_NUM >= 0x010900
25+
return nghttp2_session_callbacks_set_error_callback(cbs, error_callback);
26+
#endif
27+
}
28+
29+
// This shim turns the macro into something we can see.
30+
int CNIONghttp2_nghttp2_version_number(void) {
31+
return NGHTTP2_VERSION_NUM;
32+
}

Sources/NIOHTTP2/NGHTTP2Session.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ private func withCallbacks<T>(fn: (OpaquePointer) throws -> T) rethrows -> T {
3939
nghttp2_session_callbacks_del(nghttp2Callbacks)
4040
}
4141

42-
nghttp2_session_callbacks_set_error_callback(nghttp2Callbacks, errorCallback)
42+
CNIONghttp2_nghttp2_session_callbacks_set_error_callback(nghttp2Callbacks, errorCallback)
4343
nghttp2_session_callbacks_set_on_frame_recv_callback(nghttp2Callbacks, onFrameRecvCallback)
4444
nghttp2_session_callbacks_set_on_begin_frame_callback(nghttp2Callbacks, onBeginFrameCallback)
4545
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(nghttp2Callbacks, onDataChunkRecvCallback)

Tests/NIOHTTP2Tests/SimpleClientServerTests.swift

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import XCTest
1616
import NIO
1717
import NIOHTTP1
18+
import CNIONghttp2
1819
@testable import NIOHTTP2
1920

2021
/// A channel handler that passes writes through but fires EOF once the first one hits.
@@ -275,9 +276,8 @@ class SimpleClientServerTests: XCTestCase {
275276
self.serverChannel.writeAndFlush(goAwayFrame, promise: nil)
276277
self.interactInMemory(self.clientChannel, self.serverChannel)
277278

278-
// The client should not receive this GOAWAY frame, as it has shut down.
279-
self.clientChannel.assertNoFramesReceived()
280-
279+
// In some nghttp2 versions the client will receive a GOAWAY, in others
280+
// it will not. There is no meaningful assertion to apply here.
281281
// All should be good.
282282
self.serverChannel.assertNoFramesReceived()
283283
XCTAssertNoThrow(try self.clientChannel.finish())
@@ -969,9 +969,8 @@ class SimpleClientServerTests: XCTestCase {
969969
// The data frame write should have exploded. nghttp2 synthesises an error code for this.
970970
XCTAssertEqual((writeError as? NIOHTTP2Errors.StreamClosed)?.streamID, clientStreamID)
971971

972-
// No other frames should be emitted.
973-
self.clientChannel.assertNoFramesReceived()
974-
self.serverChannel.assertNoFramesReceived()
972+
// No other frames should be emitted, but we're ok if they were: depending on performance
973+
// we may see a WINDOW_UPDATE or two here depending on timings.
975974
XCTAssertNoThrow(try self.clientChannel.finish())
976975
XCTAssertNoThrow(try self.serverChannel.finish())
977976
}
@@ -1000,7 +999,18 @@ class SimpleClientServerTests: XCTestCase {
1000999
let respFrame = HTTP2Frame(streamID: serverStreamID, payload: .headers(responseHeaders))
10011000
var respTrailersFrame = HTTP2Frame(streamID: serverStreamID, payload: .headers(trailers))
10021001
respTrailersFrame.flags.insert(.endStream)
1003-
try self.assertFramesRoundTrip(frames: [respFrame, respTrailersFrame], sender: self.serverChannel, receiver: self.clientChannel)
1002+
1003+
// v1.11.0 onwards don't send a 0-length DATA frame here, but earlier ones do. We send it explicitly to get
1004+
// the output to match on all platforms.
1005+
let expectedFrames: [HTTP2Frame]
1006+
if CNIONghttp2_nghttp2_version_number() < 0x011100 {
1007+
let emptyDataFrame = HTTP2Frame(streamID: serverStreamID, payload: .data(.byteBuffer(self.serverChannel.allocator.buffer(capacity: 0))))
1008+
expectedFrames = [respFrame, emptyDataFrame, respTrailersFrame]
1009+
} else {
1010+
expectedFrames = [respFrame, respTrailersFrame]
1011+
}
1012+
1013+
try self.assertFramesRoundTrip(frames: expectedFrames, sender: self.serverChannel, receiver: self.clientChannel)
10041014

10051015
XCTAssertNoThrow(try self.clientChannel.finish())
10061016
XCTAssertNoThrow(try self.serverChannel.finish())
@@ -1026,16 +1036,30 @@ class SimpleClientServerTests: XCTestCase {
10261036

10271037
let serverStreamID = try self.assertFramesRoundTrip(frames: [reqFrame, reqBodyFrame], sender: self.clientChannel, receiver: self.serverChannel).first!.streamID
10281038

1029-
// Now we can send the next trailers.
1030-
XCTAssertNoThrow(try self.assertFramesRoundTrip(frames: [trailerFrame], sender: self.clientChannel, receiver: self.serverChannel))
1039+
// Now we can send the next trailers. Again, old versions of nghttp2 send an empty data frame here too.
1040+
var expectedFrames: [HTTP2Frame]
1041+
if CNIONghttp2_nghttp2_version_number() < 0x011100 {
1042+
let emptyDataFrame = HTTP2Frame(streamID: clientStreamID, payload: .data(.byteBuffer(self.clientChannel.allocator.buffer(capacity: 0))))
1043+
expectedFrames = [emptyDataFrame, trailerFrame]
1044+
} else {
1045+
expectedFrames = [trailerFrame]
1046+
}
1047+
XCTAssertNoThrow(try self.assertFramesRoundTrip(frames: expectedFrames, sender: self.clientChannel, receiver: self.serverChannel))
10311048

10321049
// Let's send a quick response back. This response should also contain trailers.
10331050
let responseHeaders = HTTPHeaders([(":status", "200"), ("content-length", "0")])
10341051
let respFrame = HTTP2Frame(streamID: serverStreamID, payload: .headers(responseHeaders))
10351052
var respTrailersFrame = HTTP2Frame(streamID: serverStreamID, payload: .headers(trailers))
10361053
respTrailersFrame.flags.insert(.endStream)
10371054
XCTAssertNoThrow(try self.assertFramesRoundTrip(frames: [respFrame], sender: self.serverChannel, receiver: self.clientChannel))
1038-
XCTAssertNoThrow(try self.assertFramesRoundTrip(frames: [respTrailersFrame], sender: self.serverChannel, receiver: self.clientChannel))
1055+
1056+
if CNIONghttp2_nghttp2_version_number() < 0x011100 {
1057+
let emptyDataFrame = HTTP2Frame(streamID: serverStreamID, payload: .data(.byteBuffer(self.serverChannel.allocator.buffer(capacity: 0))))
1058+
expectedFrames = [emptyDataFrame, respTrailersFrame]
1059+
} else {
1060+
expectedFrames = [respTrailersFrame]
1061+
}
1062+
XCTAssertNoThrow(try self.assertFramesRoundTrip(frames: expectedFrames, sender: self.serverChannel, receiver: self.clientChannel))
10391063

10401064
XCTAssertNoThrow(try self.clientChannel.finish())
10411065
XCTAssertNoThrow(try self.serverChannel.finish())

docker/docker-compose.ubuntu-1404.403.yaml

Lines changed: 0 additions & 15 deletions
This file was deleted.

docker/docker-compose.ubuntu-1604.403.yaml

Lines changed: 0 additions & 14 deletions
This file was deleted.

docker/docker-compose.ubuntu-1604.41.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ services:
88
args:
99
ubuntu_version : "16.04"
1010
swift_version : "4.1"
11-
install_nghttp2_from_source: "true"
1211

1312
test:
1413
image: swift-nio-http2:ubuntu-16.04-4.1

0 commit comments

Comments
 (0)