Commit Graph

18 Commits

Author SHA1 Message Date
scottmarchant 3f80264185 build: Elide NIOEmbedded for WASI platforms only (#3484)
### Motivation:

Packages that consume NIOEmbedded should be able to compile to WASI
platforms without special configurations. This change elides the
NIOEmbedded source from WASI platforms to simplify configuration.

Without this change:

```swift
dependencies: [
    // Without this PR, downstream packages must maintain exhaustive platform list to exclude `.wasi`:
    .product(
        name: "NIOEmbedded",
        package: "swift-nio",
        condition: .when(platforms: [
            .macOS,
            .macCatalyst,
            .iOS,
            .tvOS,
            .watchOS,
            .visionOS,
            .driverKit,
            .linux,
            .windows,
            .android,
            .openbsd,
            // .wasi // <-- Need to exclude this, because there is no exclusion list api for SPM conditionals
        ])
    ),
]
```

With this change:

```swift
dependencies: [
    // Without this PR, downstream packages can consume NIOEmbedded simply:
    .product(name: "NIOEmbedded", package: "swift-nio"),
]
```

### Modifications:

- Fix compiler directive using in AsyncTestingChannel.swift to include
an extension

### Result:

Packages can compile to wasm without manually excluding the NIOEmbedded
dependency.

### Testing performed

- Verified `swift build --swift-sdk wasm32-unknown-wasip1 --target
NIOEmbedded` compiles, which demonstrates proper elision of source files
in NIOEmbedded that aren't wasm-ready.
- Confirmed GitHub [checks
pass](https://github.com/PassiveLogic/swift-nio/actions/runs/21151287289).
2026-01-22 18:58:24 +00:00
aryan-25 5dc3b4ba4a Cancel pending in/outbound consumers in EmbeddedChannelCore upon channel close (#3464)
### Motivation:

Currently, pending consumer closures remain in the
`{in}{out}boundBufferConsumer` queues of `EmbeddedChannelCore` even
after the channel closes. It is also possible to enqueue consumers
*after* the channel closes. In these cases, the consumer closures will
never be invoked and this can lead to unfavourable behaviour, as
observed in `NIOAsyncTestingChannel`'s `waitFor{In}{Out}boundWrite`
methods (the only places these queues are currently used).

`NIOAsyncTestingChannel`'s `waitFor{In}{Out}boundWrite` methods complete
a continuation *inside* the consumer closure. In the cases described
above, the continuation never completes and therefore
`waitFor{In}{Out}boundWrite` never returns.

### Modifications:

- Updated the element type in `EmbeddedChannelCore`'s
`{in}{out}boundBufferConsumer` from `(NIOAny) -> Void` to
`(Result<NIOAny, Error>) -> Void`.
- This is so that the `.failure` case can be used to notify the consumer
closure that the channel has closed.
- Changed the visibility of the `{in}{out}boundBufferConsumer`
properties from `internal` to `private` in order to prevent the queues
from being accessed and being appended to without the call site
considering whether the channel has been closed.
- Added new `internal` methods named
`enqueue{In}{Out}boundBufferConsumer(_:)` which take the consumer
closure as an argument and only append to the corresponding queue if the
channel isn't closed.
- If the channel is closed, the consumer closure is invoked immediately
with `.failure(ChannelError.ioOnClosedChannel)`.
- Updated `EmbeddedChannelCore`'s `close0` method to return a
`.failure(ChannelError.ioOnClosedChannel)` result to each closure in
`{in}{out}boundBufferConsumer` and empty both buffers.
- Updated `NIOAsyncTestingChannel`'s `waitFor{In}{Out}boundWrite` to
throw an error in the continuation upon receiving a `.failure` result.
- Added associated test cases.

### Result:

`EmbeddedChannelCore`'s `{in}{out}boundBufferConsumer` queues can be
used more safely: all pending closures will be invoked upon channel
close. As a result, `NIOAsyncTestingChannel`'s
`waitFor{In}{Out}boundWrite` no longer indefinitely blocks when the
channel closes.
2026-01-06 17:52:06 +00:00
Rick Newton-Rogers 56724a2b6d NIOAsyncTestingChannel local/remote addrs on EmbeddedChannelCore (#3442)
### Motivation:

`NIOAsyncTestingChannel` stored its `localAddress` and `remoteAddress`
in a locked storage on itself for thread safety, however in doing so
left us open to bugs because a handler grabbing the addresses of the
context had no visibility of the values.

### Modifications:

Reach into `EmbeddedChannelCore` for the addresses instead of storing
them on the `NIOAsyncTestinghannel`. I also considered a delegate
approach where the `EmbeddedChannelCore` could offload the
responsibility for storing the values back to the
`NIOAsyncTestingChannel` but it was complicated and of questionable
value.

### Result:

* The correct address values are seen no matter how they are obtained.
* We probably take a performance hit locking the values in this way but
this is testing code so probably not the end of the world.
2025-11-11 15:08:15 +00:00
Klaas Pieter Annema 1c30f0f205 Support options on AsyncTestingChannel and EmbeddedChannel (#3308)
Support options on AsyncTestingChannel and EmbeddedChannel

Fixes #3305 

### Motivation:

The channels are intended for testing purposes so while most options
have no practical use setting and getting them can be useful for
testing.

For example a traceroute implementation will set TTL to the current hop
number. Testing such an implementation requires the channels to pretend
to support the TTL option.

### Modifications:

Added option storage to AsyncTestingChannel and EmbedededChannel and
made `getOptionSync` and `setOptionSync` read and write from that
storage.

### Result:

EmbeddedChannel and AsyncTestingChannel support changing their options.

---------

Co-authored-by: Cory Benfield <lukasa@apple.com>
2025-08-05 11:37:04 +01:00
hamzahrmalik 37094b1b42 Embedded channels should set local and remote address always (#3254)
Embedded channels should set local and remote address always

### Motivation:

Currently, if connect or bind are called without a promise, the
remote/local address does not get set because those were getting set in
the whenSuccess of the promise.

### Modifications:

If the user didn't provide a promise, create one ourselves, so we have
something to listen for.

### Result:

Calling connect/bind will always result in remote/local address getting
set, regardless of whether you pass a promise
2025-05-30 17:21:18 +01:00
Rick Newton-Rogers 57658e12ff Drop Swift 5.9 (#3228)
Motivation:

Swift 5.9 is no longer supported, we should bump the tools version and
remove it from our CI.

Modifications:

* Bump the Swift tools version to Swift 5.10
* Remove Swift 5.9 jobs where appropriate in main.yml, pull_request.yml

Result:

Code reflects our support window.
2025-05-07 12:24:44 +00:00
hamzahrmalik 329ef54dd8 Add channel initializer closure to NIOAsyncTestingChannel.init (#3053)
Since nio 2.78, adding handlers to a pipeline requires the handlers to
be sendable.

That makes the
[NIOAsyncTestingChannel.init(handlers)](https://swiftpackageindex.com/apple/swift-nio/2.78.0/documentation/nioembedded/nioasynctestingchannel/init(handlers))
function cumbersome, because you cannot create handlers and then call
the function (unless your handlers are Sendable) even if you never use
the handlers elsewhere.

This PR adds a new initializer which takes a closure. The closure is run
on-loop before the channel is registered. This means we can do:

```swift
let channel = try await NIOAsyncTestingChannel {
    let handler = MyUnsendableHandler()
    try $0.pipeline.syncOperations.addHandler(handler)
}
```
2025-01-13 15:21:42 +00:00
Cory Benfield 2d72ada999 Get NIOEmbedded clean under strict concurrency (#3030)
Motivation:

NIOEmbedded is used all over NIO-land for testing various pieces of the
infrastructure, and so requires a substantial audit for strict
concurrency.

Modifications:

- Mark a few things Sendable.
- Fix the tests, which actually did have some nasty bugs

Result:

Sendable-clean NIOEmbedded
2024-12-17 09:49:06 +00:00
Rick Newton-Rogers adfd61adc5 Use Swift 6.0 docs pipeline (#2966)
### Motivation:

Documentation checking catches more issues in Swift 6.0.

### Modifications:

Adopt the Swift 6.0 image and fix the errors.

### Result:

More accurate docs.
2024-11-07 13:03:47 +00:00
Cory Benfield de116b7eff Clean up Sendability for ChannelInvoker (#2955)
Motivation:

The ChannelInvoker protocols are an awkward beast. They aren't really
something that people can do generic programming against. Instead, they
were designed to do API sharing. Of course, they didn't do that very
well, and the strict concurrency checking world has revealed this.

Much of the API surface on ChannelInvoker is confused. There are
NIOAnys, which aren't Sendable. We allow sending user events without
requiring Sendable. And our two main conforming types are
ChannelPipeline and ChannelHandlerContext, two types with wildly
differing thread-safety semantics.

This PR aims to clean that up.

Modifications:

- Deprecated all API surface on ChannelInvoker protocols that uses
NIOAny.

    ChannelInvoker has to be assumed to be a cross-thread protocol,
    and that requires that it only use Sendable types. NIOAny isn't,
    so these methods are no longer sound.

- Re-add non-deprecated versions on ChannelHandlerContext.

    While it's not safe to use the NIOAny methods on Channel or
    ChannelPipeline, it's totally safe to use them on
    ChannelHandlerContext. So we keep those available and
    undeprecated.

- Provide typed generic replacements on ChannelPipeline and on Channel

    To replace the NIOAny methods on ChannelPipeline and Channel
    we can use some typed generic ones instead. These are not
    defined on ChannelInvoker, as the methods are useless on
    ChannelHandlerContext. This begins the acknowledgement that
    ChannelHandlerContext should not have conformed to these
    protocols at all.

- Add Sendable constraints to the user event witnesses on ChannelInvoker

    Again, these were missing, but must be there for Channel and
    ChannelPipeline.

- Provide non-Sendable overloads on ChannelHandlerContext

    ChannelHandlerContext is thread-bound, and so may safely pass
    non-Sendable user events.

Result:

One step closer to strict concurrency cleanliness for NIOCore.
2024-10-31 11:30:27 +00:00
John Zhou ac6d905654 Add new ChannelOption to get the amount of buffered outbound data in the Channel (#2849)
Add ability to get the amount of buffered outbound data from `Channel`

### Motivation:
Right now, SwiftNIO does not have the API to answer the question "how
much data is buffered in the Channel". Applications focusing on
performance may need to fine-tune the amount of outbound data that will
be sent to optimize data throughput, adjust sending rate to avoid
overflow, and potentially reduce latency.

SwiftNIO currently provides some backpressure mechanism. This new API
will be a good addition. By knowing how much data is buffered directly,
applications can make informed decision to adjust for optimal buffer
sizes and send rates.

### Modifications:
- Expose current buffer size through ChannelOptions so that users can
read the value out. StreamSocketChannel, DatagramSocketChannel,
EmbeddedChannel, and AsyncTestingChannel have the same API interface.
- Various modifications to the existing tests to make sure the new API
is working correctly.
- Add a new `so_sndbuf` socket option so that users can easily adjust
the send buffer size.

### Result:

Users can get the amount of outbound bytes currently buffered in the
`Channel` through the new `BufferedWritableBytesOption` channel option.

---------

Co-authored-by: Cory Benfield <lukasa@apple.com>
2024-09-23 10:12:48 +00:00
Max Desiatov 730713e47f Add support for WASILibc (#2671)
Dispatch is not supported on WASI, and only Unix domain sockets are
supported, which means we have to exclude those APIs on this platform.

There's work in progress to enable tests for this on CI, but nothing I
can provide for this PR at the current moment.

---------

Co-authored-by: Franz Busch <f.busch@apple.com>
2024-09-12 13:18:25 +01:00
Franz Busch c9756e1083 Adopt swift-format (#2794)
* Apply formatting

* Apply no block comments rule

* Apply OmitExplicitReturns

* Apple OnlyOneTrailingClosureArgument

* Apply NoAssignmentInExpressions

* Fix up DontRepeatTypeInStaticProperties lint errors

* Apply `OrderedImports`

* Apply `ReplaceForEachWithForLoop`

* format file

* Enable the formatting pipeline

* Adopt `AmbiguousTrailingClosureOverload`

* Fix license header

* Fix format check

* Fix `EndOfLineComment`

* Fix CI

* Adapt CI script to check if changes when running formatting

* Separate lint and format into to steps

* Fix format

* Adopt `UseEarlyExits`

* Revert "Adopt `UseEarlyExits`"

This reverts commit d1ac5bbe12.
2024-07-19 11:48:17 +02:00
Felix Schlegel 47b6289d93 Embedded: getOption(ChannelOptions.allowRemoteHalfClosure) should not fatalError (#2429)
* Embedded: getOption(.allowRemoteHalfClosure) -> OK

Motivation:

In `swift-nio-ssl`, I am currently working on allowing half-closures
which relies on querying the underlying channel if
`ChannelOptions.Types.AllowRemoteHalfClosureOption` is enabled. As a lot of
`swift-nio-ssl`'s tests rely on `EmbeddedChannel` and it did not support
this option, a lot of the tests failed.

Modifications:

* add a `public var allowRemoteHalfClosure` to `EmbeddedChannel`
* enable setting/getting
  `ChannelOptions.Types.AllowRemoteHalfClosureOption` in
`EmbeddedChannel` (only modifies the `allowRemoteHalfClosure` variable
* add test for new behaviour

* AsyncTestingChannel: getOption(.allowRemoteHalfClosure) -> OK

Motivation:

`AsyncTestingChannel` interface should be in step with `EmbeddedChannel`
interface. Therefore also add support for the
`AllowRemoteHalfClosureOption`

Modifications:

* add a `public var allowRemoteHalfClosure` to `AsyncTestingChannel`
* enable setting/getting
  `ChannelOptions.Types.AllowRemoteHalfClosureOption` in `AsyncTestingChannel`
  (only modifies the `allowRemoteHalfClosure` variable
* add tests for new behaviour

* Synchronize access to allowRemoteHalfClosure

Modifications:

* add `ManagedAtomic` property `_allowRemoteHalfClosure` to
  `EmbeddedChannelCore`
* make sure that access to `allowRemoteHalfClosure` from
  `AsyncTestingChannel` and `EmbeddedChannel` is synchronized by
  accessing underlying atomic value in `channelcore`

* Update allocation limits
2023-05-19 09:47:47 -07:00
David Nadoba 6e404d1614 Add NIOAsyncTestingChannel.waitForOut/InboundWrite() (#2307) 2022-11-08 11:45:24 +00:00
David Nadoba c7b4989b02 Remove #if compiler(>=5.5) (#2292)
### Motivation
We only support Swift 5.5.2+.

### Modification
Remove all `#if swift(>=5.5)` conditional compilation blocks.

### Result
less branching
2022-10-13 07:17:46 -07:00
Johannes Weiss 4ed8e1e228 rename class Lock to struct NIOLock (#2266) 2022-09-21 07:36:42 -07:00
Cory Benfield f5448fbbc2 Provide NIOAsyncTestingChannel (#2238)
Motivation

Testing versions of NIO code that involve interfacing with Swift
Concurrency is currently a difficult business. In particular,
EmbeddedChannel is not available in Swift concurrency, making it
difficult to write tests where you fully control the I/O.

To that end, we should provide a variation of EmbeddedChannel that makes
testing these things possible.

Modifications

Provide an implementation of NIOAsyncTestingChannel.

Results

Users can write tests confidently with async/await.
2022-08-10 11:09:56 +01:00