Commit Graph

337 Commits

Author SHA1 Message Date
Cory Benfield d6ade1424e
Forbid HTTP protocols other than 1. (#283)
Motivation:

Our HTTP code handles only HTTP/1.X. There is no reason to support
HTTP/0.9, and we cannot safely handle a major protocol higher than 1 in
this code, so we should simply treat requests/responses claiming to be
of those protocols as errors.

Modifications:

HTTPDecoder now checks the major version is equal to 1 before it
continues with parsing. If it hits an error, that error will be propagated
out to the user.

Result:

Better resilience against bad HTTP messages.
2018-04-06 11:26:32 +01:00
Cory Benfield c3e7057174
Remove unsafe cross-thread communication from lifecycle test (#284)
Motivation:

Thread safety is a good idea.

Modifications:

Make things thread safe.

Result:

Fewer crashes.
2018-04-06 10:06:01 +01:00
Karim ElNaggar d58fa23e56 Add EventLoopFuture fold function (#269)
Motivation:

Add primitive for folding futuristic values.

Modifications:

Add a new EventLoopFuture extension containing the implementation of
`fold<U>(futures:combiningFunction)`.

Result:

An API for foding futures.
2018-04-05 09:47:35 +01:00
Cory Benfield 85b835863f Wait for test cleanup. (#280)
Motivation:

The current test function does not wait for the futures from close to
return, and so they may in principle be leaked. This obscures errors that
the test might hit with precondition failures.

Modifications:

Changed the defer statements to wait for the futures.

Result:

Fewer spurious test failures.
2018-04-04 20:17:02 +02:00
Johannes Weiß 2af28a75b8 use @convention(c) for malloc/realloc/free in ByteBuffer (#267)
Motivation:

Adding @convention(c) is beneficial for performance of variables bound
to C functions so we should do that in ByteBuffer.

Modifications:

Adds @convention(c) to the malloc/realloc/free constants in ByteBuffer

Result:

Slightly better performance.
2018-04-03 21:00:01 +02:00
Cory Benfield 20988526a3
Don't accidentally fail leak detection tests. (#272)
Motivation:

The channel leak detection test currently gates its progress based on
the firing of the promise passed to close(). That promise fires way, way
before the channel stops scheduling work, which means that it's highly
likely the event loop will still be holding a ref to the channel shortly
after that promise fires.

That causes the test to fail. Failing tests are bad.

Modifications:

Wait for the closeFuture instead. This fires at the end of the last
callback a socket channel ever schedules, so will fire just before the
channel drops out of scope.

Result:

Less flaky tests.
2018-04-03 17:39:43 +01:00
Norman Maurer 2a684fc1dc Don't re-arm timerfd each epoll_wait (#264)
Motivation:

When calling Selector.whenReady(...) and using a SelectorStrategy.blockUntil(...) we may end up re-arm the timerfd each time even if the wakeup time did not change.
This is expensive and can be avoided in most situations.

Modifications:

Keep track of the earliest scheduled timer and only schedule a new one if it would fire sooner.

Result:

Less overhead when using epoll and timers.
2018-04-03 09:53:19 +01:00
Johannes Weiß 23744213e6 make Channel lifecycle statemachine explicit (#220)
Motivation:

We had a lot of problems with the Channel lifecycle statemachine as it
wasn't explicit, this fixes this. Additionally, it asserts a lot more.

Modifications:

- made Channel lifecycle statemachine explicit
- lots of asserts

Result:

- hopefully the state machine works better
- the asserts should guide our way to work on in corner cases as well
2018-03-29 19:50:07 +02:00
Norman Maurer b1f948a115
Set hints for getaddrinfo to restrict results (#221)
Motivation:

We only use our GetaddrinfoResolver for SocketChannels (TCP stream channels), so we should set some hints for getaddrinfo.

Modifications:

Set proto and socktype hints.

Result:

More correct usage of getaddrinfo. Fixes https://github.com/apple/swift-nio/issues/201.
2018-03-28 10:49:05 +02:00
Johannes Weiß c9c3974276 bring PriorityQueue/Heap implementations inline (#245)
Motivation:

We triggered extra allocations for the generics on PriorityQueue/Heap.
They unfortunately can't be solved using `@_specialize` as the element
type is `ScheduledTask`.
Fortunately we never exposed those types externally so we can just
inline without breaking the public API.

Modifications:

Moved everything from the `NIOPriorityQueue` module into the `NIO`
module.

Result:

Less allocations, more happiness.
2018-03-27 19:04:33 +02:00
Norman Maurer 5eb21324fc
Update EmbeddedChannel state before notify connect and close promise. (#241)
Motivation:

We need to update isActive before notify the promises of connect and close to correctly refect Channel.isActive in callbacks.

Modifications:

- Correctly update isActive before notify promises.
- Add unit test.

Result:

Correct Channel state in callbacks.
2018-03-27 15:18:23 +02:00
Norman Maurer c3fc78fe00 Take closeFuture into account when closing EventLoopGroup (#231)
Motiviation:

When the Selector is closed we also close the registered Channels and notify the future once all the close operations complete. The problem here is that while the close operation may be complete already there may still be events flowing through the ChannelPipeline. The only way to ensure this not happens anymore is to take the closeFuture into account of each Channel (as the closeFuture will only be notified once all is done for a Channel).

Modification:

- Also take the closeFuture into account per Channel
- Add test

Result:

Safer shutdown of EventLoopGroup.
2018-03-26 12:07:28 +01:00
Norman Maurer 1bcf85dd24 Correctly fail EventLoopFuture for unsupported operations on ServerSocketChannel (#226)
Motivation:

ServerSocketChannel should just fail the EventLoopFuture for unsupported operations and not crash.

Modifications:

- Correctly fail the EventLoopFuture for unsupported operations
- Add unit test.

Result:

No more crashes when users invoke unsupported operations.
2018-03-23 15:02:31 +00:00
Norman Maurer eb60e8978f
Dissallow access and removal of Head and Tail ChannelHandlers (#225)
Motivation:

The Head and Tail ChannelHandlers should not be accessible as these are implementation details of the ChannelPipeline and removal of these will even make the whole ChannelPipeline not work anymore.

Modifications:

- Make it impossible to access / removal the Head and Tail ChannelHandlers
- Exclude the Head and Tail ChannelHandler from the debug String
- Add unit tests.

Result:

More robust ChannelPipeline.
2018-03-23 15:02:16 +01:00
Tibor Bödecs 1f83a1d5cb Changed EventLoop{Future,Promise}<()> occurances to EventLoop{Future,Promise}<Void> (#211) (#219)
Motivation:

Fix inconsistency of <Void> and <()> occurances in the codebase.

Modifications:

Changed EventLoop{Future,Promise}<()> occurances to EventLoop{Future,Promise}<Void>

Result:

Consistent code style for EventLoopPromise and EventLoopFuture.
2018-03-22 17:36:10 +01:00
Norman Maurer 536298389f Remove unsafe assert on Linux (#218)
Motivation:

b8055f7467 added some code to guard against the case when a fd is deregistered while processing ready events. On Linux it also added an assert which tried to verify that the fd in question is not registered anymore via epoll. The problem here is that it may also be closed which will have epoll_ctl fail with EBADF

Modifications:

Remove assert and add test-case which would have triggered the assert.

Result:

No more assertion error on linux.
2018-03-22 13:56:00 +00:00
Johannes Weiß 24fbb1ba1b channelActive needs to happen before channelRead (#204)
Motivation:

`readIfNeeded` was triggered straight after the channel was registered
but _before_ `channelActive` was called which is wrong.

Modifications:

Moved the calls to `readIfNeeded` into `becomeActive0` and removed them
everywhere else (except some error path).

Result:

handle ChannelHandler lifecycle more correctly.
2018-03-21 17:03:46 +00:00
Norman Maurer b8055f7467
Guard against the case when a Selectable is deregistered during Selector.whenReady(...) processing. (#210)
Motivation:

It's possible someone deregistered a Selectable while Selector.whenReady(...) is processed and an even for the now deregistered Selectable is ready. In this case we crashed (due a force-unwrap) on linux.

Modifications:

- Skip the processing of the Selectable if deregistered (by replacing force-unwrap with an if let).
- Add unit test.

Result:

No more crashes caused by this on Linux
2018-03-21 15:32:15 +01:00
Norman Maurer 8bf013d7bd If connectSocket() completes directly we need to mark channel as active (#205)
Motivation:

If connectSocket() completes directly we failed to mark the Channel active by calling becomeActive0(...).

Modifications:

Correctly call becomeActive0(...)

Result:

Mark Channel active if connect succeed directly.
2018-03-21 10:12:24 +00:00
Norman Maurer 17d5da5a2c
Return failed EventLoopFuture when getOption(...) / setOption(...) is called on a closed Channel (#198)
Motivation:

We should return a failed EventLoopFuture when getOption(...) / setOption(...) is called on a closed Channel as otherwise it may not be safe to modify the Channel ioptions after its closed.

Modifications:

- Add guards that check if the Channel is still open and if not fail the operation.
- Add testcase

Result:

Calling getOption(...) / setOption(...) on a closed Channel fails.
2018-03-20 17:38:24 +01:00
Cory Benfield fef728cab0
HappyEyeballs should tolerate uncancellable tasks. (#191)
Motivation:

The HappyEyeballs state machine incorrectly assumed that it would always
be able to cancel any scheduled task that would execute on the event loop
that the state machine itself was running on. This is not accurate: if
the scheduled task is scheduled to execute at roughly the same time as
another callback into the state machine then it may be uncancellable.

Modifications:

Allowed the three scheduled tasks (connect timeout, connect delay, and
resolver delay) to fire in completed.

Result:

No fatalErrors in window conditions under high load
2018-03-21 01:14:47 +09:00
Cory Benfield 4a23d33c97
Ensure we wait for the promise to succeed. (#200)
Motivation:

If we don't wait for the promise to succeed it may not be succeded
before we tear the loop down. This causes precondition failures.

Modifications:

We now always wait for the promise to succeed.

Result:

My tests will run.
2018-03-20 23:58:59 +09:00
Norman Maurer c2a31f7a02 Ensure fireChannelActive(...) is always called before fireChannelRead(...) (#195)
Motivation:

fireChannelActive(...) must always be called before fireChannelRead(...) which was not the cause all of the times as once ChannelOptions were applied to the accepted SocketChannel it was possible that read0() was called during this. This would then lead to an assert error as it tried to reregister on the Selector before we ever registered.

Modifications:

- Ensure we only try to reregister once we have registered to the Selector
- Only trigger read0() or pauseRead0() if the autoRead value actually changed
- Add unit test

Result:

Correct order of events and no crashes.
2018-03-20 21:44:45 +09:00
Norman Maurer b6dbaa584e
Fix warnings in tests. (#193)
Motivation:

When running swift test two warnings were printed.

Modifications:

- Signal that we want to ignore the return value
- Use boolean test in the while loop

Result:

Cleaner build
2018-03-19 19:36:10 +01:00
Bas Broek 1e7410aab8 Conform to the Swift bool naming guidelines
Motivation:

Changes the Boolean `fulfilled` to `isFulfilled` to make it more clear it is in fact a Boolean.

Also changes a `func isRecoverable()` to `var isRecoverable`.

Both of these were non-public, so this is no breaking change.

Modifications:

Renamed one Boolean and changed one function to a variable.

Result:

No use of unncessary (and potentially misinforming) functions, as well as adhering to the Swift boolean naming guidelines.
2018-03-19 14:55:53 +01:00
Bas Broek a095decbc2 Prefer if case over switch
Motivation:

There are quite a few `switch`es that cover just two cases. Explicitly state this via an `if case` + `else` structure.

Modifications:

Changes something like:

switch a {
case .b:
  return c
default:
  return d
}

to

if case .b = a {
  return c
} else {
  return d
}

Result:

This should prevent misinterpretation and thus potential bugs in the future.
2018-03-19 14:45:32 +01:00
Bas Broek 4c89a4e675 Give symbols some breathing room
Motivation:

Adds spacing around a `+` and `{`.
2018-03-19 14:44:08 +01:00
Bas Broek b180113d81 Improve switch readability
Motivation:
This changes two things:
- it removes the negation of non-used associated types (the removal of `(_)`).
- it prefers exhausting `switch`es over `default`ing.

This change does not impact bahavior; it is pure refactoring.

Modifications:

Omitting needless `(_)`'s and explicitly exhausting switches.

Result:

This change is pure refactoring.
2018-03-19 14:43:38 +01:00
Norman Maurer edfbe50fc8 Ensure we always update the promise before calling fire* methods and also udate channel state before. (#181)
Motivation:

We not always ensure correct ordering which made it quite hard to reason about things. We should always first notify the promise before we call the fire* method that belongs to the event. Beside this we sometimes fired events or notified promised before correctly update the active state / addresses of a Channel which could result in unexpected results when query the Channel during execution of callbacks and handlers.

Modifications:

- Ensure we always notify promise first
- Always correctly update channel state before notify promise
- Add test to verify notification order.

Result:

Correct ordering of events which makes things easier to reason about and which follows what netty is doing.
2018-03-19 20:52:31 +09:00
Cory Benfield 54c68db6fb
Make EmbeddedEventLoop behave more like the regular one. (#189)
Motivation:

Testing specific ordering behaviours (e.g. cancelling tasks) requires
that EmbeddedEventLoop behave as similarly as possible to the full
event loop.

Modifications:

Adopt the batched scheduled task dispatch logic from the regular
event loop.

Result:

Easier to test subtle behaviours around task dispatch.
2018-03-19 20:41:35 +09:00
Norman Maurer 06161713b1 Correctly shutdown EventLoopGroup during tests. (#190)
Motivation:

320561f437 added a few tests which missed to correctly shutdown the EventLoopGroup

Modifications:

Add code to shutdown group

Result:

Correctly release resources during tests.
2018-03-19 20:24:38 +09:00
Norman Maurer b91090f563 Fix possible endless loop when shutdown SelectableEventLoop (#188)
Motivation:

When the SelectableEventLoop is shutdown it will process a defer block in which we tried to fail all scheduled tasks that were not processed yet. The problem here was that we incorrectly not dropped the processed task and so ended in an endless loop which prevent the defer block to complete.

Beside this how we processed the scheduled tasks that were ready for execution was quite in-efficient as we always called removeFirst() which will most likely case a memory copy to move the elements around.

Modifications:

- Correctly fail all scheduled tasks without end up in an endless loop
- Use a for each loop to reduce overhead of processing ready tasks.

Result:

It's always possible to shutdown a SelectableEventLoop and less overhead when processing ready tasks.
2018-03-19 19:53:41 +09:00
Cory Benfield 28fe6e4abd Support hopping event loops with EventLoopFuture (#177)
Motivation:

A somewhat common requirement when working with chains of futures
is needing to hop from one event loop to another. This is particularly
common when relying on the fact that EventLoopFuture will synchronize
with the event loop that created it: you often want to rely on that
implicit locking, rather than use Dispatch or Lock to achieve the
same effect.

While doing this hop requires relatively little code, it's not
necessarily totally apparent to new users how they would do it.
Additionally, the most naive implementation incurs the overhead of
allocations and reference counting in cases where it's not necessary
(e.g. when you have only one event loop, or when both work items are
being scheduled on the same event loop).

For this reason, we should have a nice concise way for a user to
request this behaviour and get a relatively performant implementation
of the behaviour.

Modifications:

Added EventLoopFuture<T>.on(eventLoop:).

Changed AcceptHandler to use the new method rather than its (slower)
alternative.

Result:

Users will have an easier time working with EventLoopFutures.
2018-03-16 14:42:24 +01:00
Johannes Weiß 320561f437 fix EventLoopFuture.and's serious threading issues (#176)
Motivation:

EventLoopFuture.and had serious threading issues if the EventLoops
weren't the same.

Modifications:

Fixed the threading issues and tested them properly.

Result:

Hopefully `and` and `andAll` now don't crash if you use them across
EventLoops.
2018-03-16 12:44:49 +01:00
Norman Maurer ff8d5edb47
Also verify that fireErrorCaught(...) was called for "recoverable" errors. (#174)
Motivation:

We did not verify that fireErrorCaught(...) was called for recoverable errors while accept new sockets.

Modifications:

Verify errorCaught is called.

Result:

Better test.
2018-03-16 10:32:15 +01:00
Norman Maurer 2c4d81dd79
Don't close DatagramChannel when recvfrom fails with ECONNREFUSED / ENOMEM (#171)
Motivation:

We should not close the DatagramChannel when recvfrom fails with ECONNREFUSED / ENOMEM. The former may be caused by a previous sendto and the other may be recoverable by just stop reading for some time.

Modifications:

- Special handle ECONNREFUSED and ENOMEN to not close the channel.
- Add unit tests

Result:

More robust datagram support.
2018-03-16 10:10:50 +01:00
Johannes Weiß f10d71b379 fix closed accepting Channels in closed ServerSocketChannels (#170)
Motivation:

For servers NIO had a race where when you start tearing down the
`ServerSocketChannel` and at roughly the same time a client connects, we
would not fully bring that channel up, nor tear it down. That lead to
open channels being deallocated which leads to a crash as that's an
illegal state.

Modifications:

Made sure newly accepted channels get closed if the
`ServerSocketChannel` is already clsing.

Result:

No crashes anymore when a client connects to a closing server.
2018-03-16 17:49:19 +09:00
Johannes Weiß 7e090d622d port numbers sin[6]_port are in network byte order (#167)
Motivation:

We returned port numbers directly from the sockaddr structures but
they're in network byte order. Our code only read them correctly when
presenting, oops.

Modifications:

Corrected the port number reading and tested that.

Result:

Port numbers should now be correct.
2018-03-16 17:36:17 +09:00
Norman Maurer 22d3439c4b
Add handler that provides some backoff for accepts if these failed because of an IOError. (#126)
Motivation:

Often accept errors are recoverable over time as for example you may just run out of file descriptors. Because of this it may be useful to just "delay" accepts a bit to recover.

Modifications:

Add ChannelHandler which will backoff accepts.

Result:

Be able to recover from accept errors gracefully.
2018-03-15 21:23:19 +01:00
Norman Maurer b98c4eb48d
channelRead should never be triggered if autoRead is false. (#164)
Motivation:

At the moment its possible that channelRead is triggered even if autoRead is never changed to true and no read() is triggered.

Modifications:

- Always register with .none on the Selector and then just call readIfNeeded0()
- Only register for EPOLLRDHUP if .read, .write or .all is used.
- Add test case.

Result:

Fixes https://github.com/apple/swift-nio/issues/160.
2018-03-15 18:53:45 +01:00
Cory Benfield 4c0ed16cde
Add new, better helper method for configuring HTTP servers. (#159)
Motivation:

The current HTTP server helpers are not modular, meaning that each
time we add a new first-party HTTP handler we need to add a brand new
method. Additionally, they do not currently compose, so if you want
assistance with HTTP pipelining *and* to support HTTP upgrade you are
out of luck.

Modifications:

Deprecate the two existing server helpers.

Add a new server helper that can be extended to support all of the
possible features that users may want in their HTTP pipelines.

Result:

Users will have a single place to go that can be used to configure
their HTTP server pipeline, and that understands how the different
handlers fit together in a complete pipeline.
2018-03-16 02:13:13 +09:00
Norman Maurer c40c6e1222
Add support to obtain the current EventLoop (#120)
Motivation:

In certain cases it would be useful to be able to access the current EventLoop (which is tied to a thread).

Modifications:

- Add MultiThreadEventLoopGroup.currentEventLoop
- Add tests

Result:

Fixes [#59]
2018-03-15 16:05:35 +01:00
Semen Zhydenko 73e89833fc Fixed typos in comments 2018-03-15 22:11:46 +09:00
Cory Benfield 79a87ab76a
Let Upgrade handler remove other HTTP handlers. (#144)
Motivation:

The HTTP pipeline may contain other HTTP protocol specific handlers,
beyond just the ones that the upgrade handler knows explicitly. It should
be possible to get the upgrade handler to remove those at the right time.

Modifications:

Add extra support for removing handlers.

Result:

It'll be possible for the HTTP upgrade logic to remove things like the
pipelining handler at upgrade time.
2018-03-15 01:34:03 +09:00
Cory Benfield 25dd80aa9f
Add support for getting a handler of a given type. (#155)
Motivation:

It is often useful to find a handler of a given type even when you
aren't holding a reference to that handler. This patch allows us to
do that.

Modifications:

Add context(handlerType:) function to the channel pipeline.

Result:

Users can find handlers of a given type in a pipeline.
2018-03-14 23:20:29 +09:00
Norman Maurer 5d4da32505
Use correct errno value (ENOMEM) in testAcceptFailsWithENOMEM (#148)
Motivation:

We didnt use ENOMEM as errno value in testAcceptFailsWithENOMEM.

Modifications:

Use ENOMEM

Result:

Correct test.
2018-03-14 08:41:26 +01:00
Johannes Weiß 2cac305e3e work bug in latest Swift 4.1 with closures taking `()` (SR-7191) (#149)
Motivation:

In a few instances, we have code that ignores a `()` argument in a
closure with this syntax:

    { (_: Void) in

whilst that's a bit odd, it shouldn't be wrong and Swift 4.0.x is also
totally happy with it. The latest Swift 4.1 candidates however are not
and also there's a better way of spelling this:

    { () in

and in some cases even just

    {

Modifications:

Removed instances of `{ (_: Void) in`

Result:

Compiles with `swift-4.1-DEVELOPMENT-SNAPSHOT-2018-03-12-a`
2018-03-14 08:41:03 +01:00
Cory Benfield b542775605 Add initial websocket codec. (#109)
Motivation:

Websockets is a major protocol in use on the web today, and is
particularly valuable in applications that use asynchronous I/O
as it allows servers to keep connections open for long periods of
time for fully-duplex communication.

Users of NIO should be able to build websocket clients and servers
without too much difficulty.

Modifications:

Provided a WebsocketFrameEncoder and Decoder that can serialize and
deserialize Websocket frames.

Result:

Easier use of websockets.
2018-03-13 17:24:54 +01:00
Cory Benfield 860a7d40a3 Add channel handler for server side pipelining. (#62)
Motivation:

HTTP pipelining can be tricky to handle properly on the server side.
In particular, it's very easy to write out of order or inconsistently
mutate state. Users often need help to handle this appropriately.

Modifications:

Added a HTTPServerPipelineHandler that only lets one request through
at a time.

Result:

Better servers that are more able to handle HTTP pipelining
2018-03-13 15:21:12 +00:00
Norman Maurer 005a3b702b Don't close ServerSocketChannel when accept fails with ECONNABORTED / EMFILE / ENFILE, ENOBUFS. ENOMEM. (#125)
Motivation:

We should not close the server socket when accept fails with ECONNABORTED / EMFILE / ENFILE, ENOBUFS. ENOMEM as generally speaking these may be "recoverable" once some existing connections were closed for example. It's possible to implement a backoff strategy for these with a ChannelHandler.

Modifications:

- Special handle of ECONNABORTED / EMFILE / ENFILE, ENOBUFS. ENOMEM for ServerSocketChannel.

Result:

Not close servet socket for errors from which it is possible to recover.
2018-03-13 15:09:13 +00:00