Skip to content

Commit feaa53c

Browse files
committed
fix for linux and refactor
1 parent dae7d95 commit feaa53c

File tree

11 files changed

+371
-27
lines changed

11 files changed

+371
-27
lines changed

Package.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ let package = Package(
2929
.product(name: "NIOConcurrencyHelpers", package: "swift-nio"),
3030
.product(name: "NIOWebSocket", package: "swift-nio"),
3131
.product(name: "NIOHTTP1", package: "swift-nio"),
32+
.product(name: "NIOFoundationCompat", package: "swift-nio"),
3233
.product(name: "NIOSSL", package: "swift-nio-ssl"),
3334
.product(
3435
name: "NIOTransportServices",
@@ -43,6 +44,14 @@ let package = Package(
4344
name: "LCLWebSocketTests",
4445
dependencies: ["LCLWebSocket"]
4546
),
47+
.executableTarget(
48+
name: "AutobahnClient",
49+
dependencies: ["LCLWebSocket"]
50+
),
51+
.executableTarget(
52+
name: "AutobahnServer",
53+
dependencies: ["LCLWebSocket"]
54+
),
4655
.executableTarget(name: "Client", dependencies: ["LCLWebSocket"]),
4756
.executableTarget(name: "Server", dependencies: ["LCLWebSocket"]),
4857
]
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//
2+
// This source file is part of the LCL open source project
3+
//
4+
// Copyright (c) 2021-2024 Local Connectivity Lab and the project authors
5+
// Licensed under Apache License v2.0
6+
//
7+
// See LICENSE for license information
8+
// See CONTRIBUTORS for the list of project authors
9+
//
10+
// SPDX-License-Identifier: Apache-2.0
11+
//
12+
13+
14+
import Foundation
15+
import LCLWebSocket
16+
import NIOCore
17+
import NIOPosix
18+
19+
@main
20+
struct TestWebSocketClient {
21+
22+
static let config = LCLWebSocket.Configuration(
23+
maxFrameSize: 1 << 16,
24+
autoPingConfiguration: .disabled,
25+
leftoverBytesStrategy: .forwardBytes
26+
)
27+
28+
static let serverAddress = "127.0.0.1"
29+
static let serverPort = 9001
30+
31+
public static func main() throws {
32+
33+
let elg = MultiThreadedEventLoopGroup(numberOfThreads: 1)
34+
var client = LCLWebSocket.client(on: elg)
35+
let totalTestCount = elg.any().makePromise(of: Int.self)
36+
37+
client.onText { websocket, text in
38+
guard let total = Int(text) else {
39+
fatalError()
40+
}
41+
totalTestCount.succeed(total)
42+
}
43+
44+
try client.connect(to: "ws://\(Self.serverAddress):\(Self.serverPort)/getCaseCount", configuration: Self.config).wait()
45+
46+
let total = try totalTestCount.futureResult.wait()
47+
print("Running total tests: \(total)")
48+
49+
for i in 1...total {
50+
var client = LCLWebSocket.client(on: elg)
51+
client.onText { ws, text in
52+
ws.send(.init(string: text), opcode: .text)
53+
}
54+
client.onBinary { ws, binary in
55+
ws.send(binary, opcode: .binary)
56+
}
57+
try client.connect(to: "ws://\(Self.serverAddress):\(Self.serverPort)/runCase?case=\(i)&agent={LCLWebSocketClient}", configuration: Self.config).wait()
58+
}
59+
60+
let closeClient = LCLWebSocket.client()
61+
try closeClient.connect(to: "ws://\(Self.serverAddress):\(Self.serverPort)/updateReports?agent={LCLWebSocketClient}", configuration: Self.config).wait()
62+
63+
}
64+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//
2+
// This source file is part of the LCL open source project
3+
//
4+
// Copyright (c) 2021-2024 Local Connectivity Lab and the project authors
5+
// Licensed under Apache License v2.0
6+
//
7+
// See LICENSE for license information
8+
// See CONTRIBUTORS for the list of project authors
9+
//
10+
// SPDX-License-Identifier: Apache-2.0
11+
//
12+
13+
import Foundation
14+
import LCLWebSocket
15+
import NIOCore
16+
import NIOPosix
17+
18+
@main
19+
struct AutohahnServer {
20+
21+
static let config = LCLWebSocket.Configuration(
22+
maxFrameSize: 1 << 16,
23+
autoPingConfiguration: .disabled,
24+
leftoverBytesStrategy: .forwardBytes
25+
)
26+
27+
static let serverAddress = "127.0.0.1"
28+
static let serverPort = 9000
29+
30+
static func main() throws {
31+
32+
let args = CommandLine.arguments
33+
var port: Int = Self.serverPort
34+
precondition(args.count == 1 || args.count == 3, "Usage: \(args[0]) [--port <port>]")
35+
let portCmdIndex = args.firstIndex(where: { $0 == "--port" })
36+
if let portCmdIndex = portCmdIndex {
37+
let portIndex = portCmdIndex + 1
38+
port = Int(args[portIndex]) ?? port
39+
}
40+
41+
var server = LCLWebSocket.server()
42+
43+
server.onBinary { ws, buffer in
44+
ws.send(buffer, opcode: .binary)
45+
}
46+
47+
server.onText { ws, text in
48+
ws.send(.init(string: text), opcode: .text)
49+
}
50+
51+
try server.listen(host: Self.serverAddress, port: port, configuration: Self.config).wait()
52+
}
53+
}
54+

Sources/LCLWebSocket/Client/WebSocketClient.swift

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,27 @@ public struct WebSocketClient: Sendable, LCLWebSocketListenable {
164164

165165
@Sendable
166166
func makeChannelInitializer(_ channel: Channel) -> EventLoopFuture<Void> {
167+
if self.eventloopGroup is MultiThreadedEventLoopGroup {
168+
if configuration.socketReuseAddress,
169+
let syncOptions = channel.syncOptions
170+
{
171+
do {
172+
try syncOptions.setOption(.socketOption(.so_reuseaddr), value: 1)
173+
} catch {
174+
return channel.eventLoop.makeFailedFuture(error)
175+
}
176+
}
177+
178+
if configuration.socketTcpNoDelay,
179+
let syncOptions = channel.syncOptions {
180+
do {
181+
try syncOptions.setOption(.socketOption(.tcp_nodelay), value: 1)
182+
} catch {
183+
return channel.eventLoop.makeFailedFuture(error)
184+
}
185+
}
186+
}
187+
167188
if let socketSendBufferSize = configuration.socketSendBufferSize,
168189
let syncOptions = channel.syncOptions
169190
{
@@ -341,8 +362,6 @@ extension WebSocketClient {
341362

342363
func makeClientBootstrap() -> EventLoopFuture<Channel> {
343364
return ClientBootstrap(group: self.eventloopGroup)
344-
.channelOption(.socketOption(.tcp_nodelay), value: 1)
345-
.channelOption(.socketOption(.so_reuseaddr), value: 1)
346365
.connectTimeout(configuration.connectionTimeout)
347366
.channelInitializer(channelInitializer)
348367
.connect(to: resolvedAddress)
@@ -352,7 +371,7 @@ extension WebSocketClient {
352371
func makeNIOTSConnectionBootstrap() -> EventLoopFuture<Channel> {
353372
let tcpOptions = NWProtocolTCP.Options()
354373
tcpOptions.connectionTimeout = Int(configuration.connectionTimeout.seconds)
355-
tcpOptions.noDelay = true
374+
tcpOptions.noDelay = configuration.socketTcpNoDelay
356375

357376
return NIOTSConnectionBootstrap(group: self.eventloopGroup)
358377
.tcpOptions(tcpOptions)
@@ -465,6 +484,27 @@ extension WebSocketClient {
465484

466485
@Sendable
467486
func makeChannelInitializer(_ channel: Channel) -> EventLoopFuture<Void> {
487+
if self.eventloopGroup is MultiThreadedEventLoopGroup {
488+
if configuration.socketReuseAddress,
489+
let syncOptions = channel.syncOptions
490+
{
491+
do {
492+
try syncOptions.setOption(.socketOption(.so_reuseaddr), value: 1)
493+
} catch {
494+
return channel.eventLoop.makeFailedFuture(error)
495+
}
496+
}
497+
498+
if configuration.socketTcpNoDelay,
499+
let syncOptions = channel.syncOptions {
500+
do {
501+
try syncOptions.setOption(.socketOption(.tcp_nodelay), value: 1)
502+
} catch {
503+
return channel.eventLoop.makeFailedFuture(error)
504+
}
505+
}
506+
}
507+
468508
if let socketSendBufferSize = configuration.socketSendBufferSize,
469509
let syncOptions = channel.syncOptions
470510
{

Sources/LCLWebSocket/Server/WebSocketServer.swift

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,29 @@ extension WebSocketServer {
244244

245245
func makeServerBootstrap() -> EventLoopFuture<Channel> {
246246
return ServerBootstrap(group: self.eventloopGroup)
247-
.serverChannelOption(.socketOption(.so_reuseaddr), value: 1)
248247
.serverChannelInitializer { channel in
249-
print("parent channel: \(channel)")
248+
logger.info("Server is listening on \(resolvedAddress)")
249+
if self.eventloopGroup is MultiThreadedEventLoopGroup {
250+
if configuration.socketReuseAddress,
251+
let syncOptions = channel.syncOptions
252+
{
253+
do {
254+
try syncOptions.setOption(.socketOption(.so_reuseaddr), value: 1)
255+
} catch {
256+
return channel.eventLoop.makeFailedFuture(error)
257+
}
258+
}
259+
260+
if configuration.socketTcpNoDelay,
261+
let syncOptions = channel.syncOptions {
262+
do {
263+
try syncOptions.setOption(.socketOption(.tcp_nodelay), value: 1)
264+
} catch {
265+
return channel.eventLoop.makeFailedFuture(error)
266+
}
267+
}
268+
}
269+
250270
if let socketSendBufferSize = configuration.socketSendBufferSize,
251271
let syncOptions = channel.syncOptions
252272
{
@@ -280,17 +300,20 @@ extension WebSocketServer {
280300

281301
return channel.eventLoop.makeSucceededVoidFuture()
282302
}
283-
.childChannelOption(.socketOption(.so_reuseaddr), value: 1)
284303
.childChannelInitializer(childChannelInitializer)
285304
.bind(to: resolvedAddress)
286305
}
287306

288307
#if canImport(Network)
289308
func makeNIOTSListenerBootstrap() -> EventLoopFuture<Channel> {
309+
310+
let tcpOptions = NWProtocolTCP.Options()
311+
tcpOptions.noDelay = configuration.socketTcpNoDelay
312+
290313
return NIOTSListenerBootstrap(group: self.eventloopGroup)
291-
.serverChannelOption(.socketOption(.so_reuseaddr), value: 1)
314+
.tcpOptions(tcpOptions)
292315
.serverChannelInitializer { channel in
293-
print("parent channel: \(channel)")
316+
logger.info("Server is listening on \(resolvedAddress)")
294317
if let socketSendBufferSize = configuration.socketSendBufferSize,
295318
let syncOptions = channel.syncOptions
296319
{
@@ -361,8 +384,29 @@ extension WebSocketServer {
361384
configuration: LCLWebSocket.Configuration
362385
) -> EventLoopFuture<Void> {
363386
self.makeBootstrapAndBind(with: configuration, resolvedAddress: address) { channel in
364-
// enable tls if configuration is provided
365387
logger.debug("child channel: \(channel)")
388+
389+
if self.eventloopGroup is MultiThreadedEventLoopGroup {
390+
if configuration.socketReuseAddress,
391+
let syncOptions = channel.syncOptions {
392+
do {
393+
try syncOptions.setOption(.socketOption(.so_reuseaddr), value: 1)
394+
} catch {
395+
return channel.eventLoop.makeFailedFuture(error)
396+
}
397+
}
398+
399+
if configuration.socketTcpNoDelay,
400+
let syncOptions = channel.syncOptions {
401+
do {
402+
try syncOptions.setOption(.socketOption(.tcp_nodelay), value: 1)
403+
} catch {
404+
return channel.eventLoop.makeFailedFuture(error)
405+
}
406+
}
407+
}
408+
409+
// enable tls if configuration is provided
366410
if let tlsConfiguration = configuration.tlsConfiguration {
367411
guard let sslContext = try? NIOSSLContext(configuration: tlsConfiguration) else {
368412
return channel.eventLoop.makeFailedFuture(LCLWebSocketError.tlsInitializationFailed)

0 commit comments

Comments
 (0)