diff --git a/IntegrationTests/tests_01_http/test_23_repeated_reqs_with_half_closure.sh b/IntegrationTests/tests_01_http/test_23_repeated_reqs_with_half_closure.sh new file mode 100644 index 0000000000..d0ecf44485 --- /dev/null +++ b/IntegrationTests/tests_01_http/test_23_repeated_reqs_with_half_closure.sh @@ -0,0 +1,26 @@ +#!/bin/bash +##===----------------------------------------------------------------------===## +## +## 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 +## +##===----------------------------------------------------------------------===## + +source defines.sh + +token=$(create_token) +start_server "$token" +socket=$(get_socket "$token") +echo -ne 'HTTP/1.1 200 OK\r\ncontent-length: 12\r\n\r\nHello World!' > "$tmp/expected" +for f in $(seq 2000); do + echo -e 'GET / HTTP/1.1\r\n\r\n' | nc -w10 -U "$socket" > "$tmp/actual" + assert_equal_files "$tmp/expected" "$tmp/actual" +done +stop_server "$token" diff --git a/Sources/NIO/BaseSocketChannel.swift b/Sources/NIO/BaseSocketChannel.swift index 458e16f761..c80c66ce6b 100644 --- a/Sources/NIO/BaseSocketChannel.swift +++ b/Sources/NIO/BaseSocketChannel.swift @@ -808,6 +808,11 @@ class BaseSocketChannel: SelectableChannel, ChannelCore { assert(!self.lifecycleManager.hasSeenEOFNotification) self.lifecycleManager.hasSeenEOFNotification = true + // we can't be not active but still registered here; this would mean that we got a notification about a + // channel before we're ready to receive them. + assert(self.lifecycleManager.isActive || !self.lifecycleManager.isRegistered, + "illegal state: active: \(self.lifecycleManager.isActive), registered: \(self.lifecycleManager.isRegistered)") + self.readEOF0() assert(!self.interestedEvent.contains(.read)) diff --git a/Sources/NIO/SocketChannel.swift b/Sources/NIO/SocketChannel.swift index 61b5ae1f13..0c3dcedcda 100644 --- a/Sources/NIO/SocketChannel.swift +++ b/Sources/NIO/SocketChannel.swift @@ -453,13 +453,15 @@ final class ServerSocketChannel: BaseSocketChannel { assert(eventLoop.inEventLoop) let ch = data.forceAsOther() as SocketChannel - ch.register().thenThrowing { - guard ch.isOpen else { - throw ChannelError.ioOnClosedChannel + ch.eventLoop.execute { + ch.register().thenThrowing { + guard ch.isOpen else { + throw ChannelError.ioOnClosedChannel + } + ch.becomeActive0(promise: nil) + }.whenFailure { error in + ch.close(promise: nil) } - ch.becomeActive0(promise: nil) - }.whenFailure { error in - ch.close(promise: nil) } } diff --git a/Tests/NIOTests/EventLoopTest.swift b/Tests/NIOTests/EventLoopTest.swift index 591ddd4a77..449fb26c55 100644 --- a/Tests/NIOTests/EventLoopTest.swift +++ b/Tests/NIOTests/EventLoopTest.swift @@ -126,16 +126,23 @@ public class EventLoopTest : XCTestCase { // to cleanly shut down all the channels before it actually closes. We add a custom channel that we can use // to wedge the event loop in the "shutting down" state, ensuring that we have plenty of time to attempt the // registration. - class WedgeOpenHandler: ChannelOutboundHandler { + class WedgeOpenHandler: ChannelDuplexHandler { + typealias InboundIn = Any typealias OutboundIn = Any typealias OutboundOut = Any private let promiseRegisterCallback: (EventLoopPromise) -> Void var closePromise: EventLoopPromise? = nil + private let channelActivePromise: EventLoopPromise? - init(_ promiseRegisterCallback: @escaping (EventLoopPromise) -> Void) { + init(channelActivePromise: EventLoopPromise? = nil, _ promiseRegisterCallback: @escaping (EventLoopPromise) -> Void) { self.promiseRegisterCallback = promiseRegisterCallback + self.channelActivePromise = channelActivePromise + } + + func channelActive(ctx: ChannelHandlerContext) { + self.channelActivePromise?.succeed(result: ()) } func close(ctx: ChannelHandlerContext, mode: CloseMode, promise: EventLoopPromise?) { @@ -167,9 +174,10 @@ public class EventLoopTest : XCTestCase { } let loop = group.next() as! SelectableEventLoop + let serverChannelUp: EventLoopPromise = group.next().newPromise() let serverChannel = try ServerBootstrap(group: group) .childChannelInitializer { channel in - channel.pipeline.add(handler: WedgeOpenHandler { promise in + channel.pipeline.add(handler: WedgeOpenHandler(channelActivePromise: serverChannelUp) { promise in promiseQueue.sync { promises.append(promise) } }) } @@ -196,6 +204,8 @@ public class EventLoopTest : XCTestCase { // Wait for the connect to complete. XCTAssertNoThrow(try connectPromise.futureResult.wait()) + XCTAssertNoThrow(try serverChannelUp.futureResult.wait()) + // Now we're going to start closing the event loop. This should not immediately succeed. let loopCloseFut = loop.closeGently()