Skip to content

Commit 84846f7

Browse files
lorenteymilseman
authored andcommitted
Implement sendto/recvfrom; other minor changes
1 parent 898e0ef commit 84846f7

File tree

6 files changed

+186
-36
lines changed

6 files changed

+186
-36
lines changed

Sources/Samples/Listen.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,18 +94,21 @@ struct Listen: ParsableCommand {
9494
let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 1024, alignment: 1)
9595
defer { buffer.deallocate() }
9696

97+
var ancillary = SocketDescriptor.AncillaryMessageBuffer()
9798
try socket.closeAfter {
9899
if udp {
99100
while true {
100-
let (count, flags) = try socket.receive(into: buffer, sender: &client)
101+
let (count, flags) =
102+
try socket.receive(into: buffer, sender: &client, ancillary: &ancillary)
101103
print(prefix(client: client, flags: flags), terminator: "")
102104
try FileDescriptor.standardOutput.writeAll(buffer[..<count])
103105
}
104106
} else {
105107
let conn = try socket.accept(client: &client)
106108
complain("Connection from \(client.niceDescription)")
107109
while true {
108-
let (count, flags) = try conn.receive(into: buffer, sender: &client)
110+
let (count, flags) =
111+
try conn.receive(into: buffer, sender: &client, ancillary: &ancillary)
109112
guard count > 0 else { break }
110113
print(prefix(client: client, flags: flags), terminator: "")
111114
try FileDescriptor.standardOutput.writeAll(buffer[..<count])

Sources/System/Internals/Syscalls.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,37 @@ internal func system_recv(
158158
return recv(socket, buffer, len, flags)
159159
}
160160

161+
internal func system_sendto(
162+
_ socket: CInt,
163+
_ buffer: UnsafeRawPointer?,
164+
_ length: Int,
165+
_ flags: CInt,
166+
_ dest_addr: UnsafePointer<CInterop.SockAddr>?,
167+
_ dest_len: CInterop.SockLen
168+
) -> Int {
169+
#if ENABLE_MOCKING
170+
if mockingEnabled {
171+
return _mockInt(socket, buffer, length, flags, dest_addr, dest_len)
172+
}
173+
#endif
174+
return sendto(socket, buffer, length, flags, dest_addr, dest_len)
175+
}
176+
177+
internal func system_recvfrom(
178+
_ socket: CInt,
179+
_ buffer: UnsafeMutableRawPointer?,
180+
_ length: Int,
181+
_ flags: CInt,
182+
_ address: UnsafeMutablePointer<CInterop.SockAddr>?,
183+
_ addres_len: UnsafeMutablePointer<CInterop.SockLen>?
184+
) -> Int {
185+
#if ENABLE_MOCKING
186+
if mockingEnabled {
187+
return _mockInt(socket, buffer, length, flags, address, addres_len)
188+
}
189+
#endif
190+
return recvfrom(socket, buffer, length, flags, address, addres_len)
191+
}
161192

162193
internal func system_sendmsg(
163194
_ socket: CInt,

Sources/System/Sockets/SocketAddress+Local.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ extension SocketAddress {
6767
guard path != nil else { return nil }
6868
return Local(path!)
6969
}
70+
71+
/// Construct an address in the Local domain from the given file path.
72+
@_alwaysEmitIntoClient
73+
public init(local path: FilePath) {
74+
self.init(Local(path))
75+
}
7076
}
7177

7278
// @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)

Sources/System/Sockets/SocketMessages.swift

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ extension SocketDescriptor {
2121

2222
/// Initialize a new empty ancillary message buffer with no preallocated
2323
/// storage.
24-
internal init() {
24+
public init() {
2525
_buffer = _RawBuffer()
2626
_endOffset = 0
2727
}
@@ -394,8 +394,8 @@ extension SocketDescriptor {
394394
public func send(
395395
_ bytes: UnsafeRawBufferPointer,
396396
to recipient: SocketAddress? = nil,
397-
ancillary: AncillaryMessageBuffer? = nil,
398-
flags: MessageFlags = [],
397+
ancillary: AncillaryMessageBuffer,
398+
flags: MessageFlags = .none,
399399
retryOnInterrupt: Bool = true
400400
) throws -> Int {
401401
try _send(
@@ -451,27 +451,6 @@ extension SocketDescriptor {
451451

452452
// @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
453453
extension SocketDescriptor {
454-
/// Receive a message from a socket.
455-
///
456-
/// TODO: describe every parameter and option.
457-
///
458-
/// The corresponding C function is `recvmsg`.
459-
@_alwaysEmitIntoClient
460-
public func receive(
461-
into bytes: UnsafeMutableRawBufferPointer,
462-
sender: inout SocketAddress,
463-
flags: MessageFlags = [],
464-
retryOnInterrupt: Bool = true
465-
) throws -> (received: Int, flags: MessageFlags) {
466-
return try _receive(
467-
into: bytes,
468-
sender: &sender,
469-
ancillary: nil,
470-
flags: flags,
471-
retryOnInterrupt: retryOnInterrupt
472-
).get()
473-
}
474-
475454
/// Receive a message from a socket.
476455
///
477456
/// TODO: describe every parameter and option.

Sources/System/Sockets/SocketOperations.swift

Lines changed: 117 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ extension SocketDescriptor {
9191
/// - Returns: The number of bytes that were sent.
9292
///
9393
/// The corresponding C function is `send`
94+
@_alwaysEmitIntoClient
9495
public func send(
9596
_ buffer: UnsafeRawBufferPointer,
9697
flags: MessageFlags = .none,
@@ -110,6 +111,54 @@ extension SocketDescriptor {
110111
}
111112
}
112113

114+
/// Send a message from a socket
115+
///
116+
/// - Parameters:
117+
/// - buffer: The region of memory that contains the data being sent.
118+
/// - recipient: The socket address of the recipient.
119+
/// - flags: see `send(2)`
120+
/// - retryOnInterrupt: Whether to retry the send operation
121+
/// if it throws ``Errno/interrupted``.
122+
/// The default is `true`.
123+
/// Pass `false` to try only once and throw an error upon interruption.
124+
/// - Returns: The number of bytes that were sent.
125+
///
126+
/// The corresponding C function is `sendto`
127+
@_alwaysEmitIntoClient
128+
public func send(
129+
_ buffer: UnsafeRawBufferPointer,
130+
to recipient: SocketAddress,
131+
flags: MessageFlags = .none,
132+
retryOnInterrupt: Bool = true
133+
) throws -> Int {
134+
try _send(
135+
buffer,
136+
to: recipient,
137+
flags: flags,
138+
retryOnInterrupt: retryOnInterrupt
139+
).get()
140+
}
141+
142+
@usableFromInline
143+
internal func _send(
144+
_ buffer: UnsafeRawBufferPointer,
145+
to recipient: SocketAddress,
146+
flags: MessageFlags,
147+
retryOnInterrupt: Bool
148+
) throws -> Result<Int, Errno> {
149+
recipient.withUnsafeCInterop { adr, adrlen in
150+
valueOrErrno(retryOnInterrupt: retryOnInterrupt) {
151+
system_sendto(
152+
self.rawValue,
153+
buffer.baseAddress,
154+
buffer.count,
155+
flags.rawValue,
156+
adr,
157+
adrlen)
158+
}
159+
}
160+
}
161+
113162
/// Receive a message from a socket
114163
///
115164
/// - Parameters:
@@ -122,6 +171,7 @@ extension SocketDescriptor {
122171
/// - Returns: The number of bytes that were received.
123172
///
124173
/// The corresponding C function is `recv`
174+
@_alwaysEmitIntoClient
125175
public func receive(
126176
into buffer: UnsafeMutableRawBufferPointer,
127177
flags: MessageFlags = .none,
@@ -143,12 +193,69 @@ extension SocketDescriptor {
143193
}
144194
}
145195

196+
/// Receive a message from a socket
197+
///
198+
/// - Parameters:
199+
/// - buffer: The region of memory to receive into.
200+
/// - flags: see `recv(2)`
201+
/// - retryOnInterrupt: Whether to retry the receive operation
202+
/// if it throws ``Errno/interrupted``.
203+
/// The default is `true`.
204+
/// Pass `false` to try only once and throw an error upon interruption.
205+
/// - Returns: The number of bytes that were received.
206+
///
207+
/// The corresponding C function is `recvfrom`
208+
@_alwaysEmitIntoClient
209+
public func receive(
210+
into buffer: UnsafeMutableRawBufferPointer,
211+
sender: inout SocketAddress,
212+
flags: MessageFlags = .none,
213+
retryOnInterrupt: Bool = true
214+
) throws -> Int {
215+
try _receive(
216+
into: buffer,
217+
sender: &sender,
218+
flags: flags,
219+
retryOnInterrupt: retryOnInterrupt
220+
).get()
221+
}
222+
223+
@usableFromInline
224+
internal func _receive(
225+
into buffer: UnsafeMutableRawBufferPointer,
226+
sender: inout SocketAddress,
227+
flags: MessageFlags,
228+
retryOnInterrupt: Bool
229+
) throws -> Result<Int, Errno> {
230+
sender._withMutableCInterop(entireCapacity: true) { adr, adrlen in
231+
valueOrErrno(retryOnInterrupt: retryOnInterrupt) {
232+
system_recvfrom(
233+
self.rawValue,
234+
buffer.baseAddress,
235+
buffer.count,
236+
flags.rawValue,
237+
adr,
238+
&adrlen)
239+
}
240+
}
241+
}
242+
146243
/// Accept a connection on a socket.
147244
///
148245
/// The corresponding C function is `accept`.
149246
@_alwaysEmitIntoClient
150247
public func accept(retryOnInterrupt: Bool = true) throws -> SocketDescriptor {
151-
try _accept(nil, nil, retryOnInterrupt: retryOnInterrupt).get()
248+
try _accept(retryOnInterrupt: retryOnInterrupt).get()
249+
}
250+
251+
@usableFromInline
252+
internal func _accept(
253+
retryOnInterrupt: Bool
254+
) -> Result<SocketDescriptor, Errno> {
255+
let fd = valueOrErrno(retryOnInterrupt: retryOnInterrupt) {
256+
return system_accept(self.rawValue, nil, nil)
257+
}
258+
return fd.map { SocketDescriptor(rawValue: $0) }
152259
}
153260

154261
/// Accept a connection on a socket.
@@ -161,25 +268,25 @@ extension SocketDescriptor {
161268
///
162269
/// Having this as an inout parameter allows you to reuse the same address
163270
/// value across multiple connections, without reallocating it.
271+
@_alwaysEmitIntoClient
164272
public func accept(
165273
client: inout SocketAddress,
166274
retryOnInterrupt: Bool = true
167275
) throws -> SocketDescriptor {
168-
try client._withMutableCInterop(entireCapacity: true) { adr, adrlen in
169-
try _accept(adr, &adrlen, retryOnInterrupt: retryOnInterrupt).get()
170-
}
276+
try _accept(client: &client, retryOnInterrupt: retryOnInterrupt).get()
171277
}
172278

173279
@usableFromInline
174280
internal func _accept(
175-
_ address: UnsafeMutablePointer<CInterop.SockAddr>?,
176-
_ addressLength: UnsafeMutablePointer<CInterop.SockLen>?,
177-
retryOnInterrupt: Bool = true
281+
client: inout SocketAddress,
282+
retryOnInterrupt: Bool
178283
) -> Result<SocketDescriptor, Errno> {
179-
let fd = valueOrErrno(retryOnInterrupt: retryOnInterrupt) {
180-
return system_accept(self.rawValue, address, addressLength)
284+
client._withMutableCInterop(entireCapacity: true) { adr, adrlen in
285+
let fd = valueOrErrno(retryOnInterrupt: retryOnInterrupt) {
286+
return system_accept(self.rawValue, adr, &adrlen)
287+
}
288+
return fd.map { SocketDescriptor(rawValue: $0) }
181289
}
182-
return fd.map { SocketDescriptor(rawValue: $0) }
183290
}
184291

185292
// TODO: acceptAndSockaddr or something that (tries to) returns the sockaddr

Tests/SystemTests/SocketTest.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,35 @@ final class SocketTest: XCTestCase {
5757
_ = try socket.send(
5858
writeBuf, flags: .doNotRoute, retryOnInterrupt: retryOnInterrupt)
5959
},
60+
MockTestCase(
61+
name: "recvfrom", rawSocket, Wildcard(), Wildcard(), 42, Wildcard(), Wildcard(),
62+
interruptable: true
63+
) { retryOnInterrupt in
64+
var sender = SocketAddress()
65+
_ = try socket.receive(into: rawBuf,
66+
sender: &sender,
67+
flags: .init(rawValue: 42),
68+
retryOnInterrupt: retryOnInterrupt)
69+
},
70+
MockTestCase(
71+
name: "sendto", rawSocket, Wildcard(), Wildcard(), 42, Wildcard(), Wildcard(),
72+
interruptable: true
73+
) { retryOnInterrupt in
74+
let recipient = SocketAddress(ipv4: .loopback, port: 123)
75+
_ = try socket.send(UnsafeRawBufferPointer(rawBuf),
76+
to: recipient,
77+
flags: .init(rawValue: 42),
78+
retryOnInterrupt: retryOnInterrupt)
79+
},
6080
MockTestCase(
6181
name: "recvmsg", rawSocket, Wildcard(), 42,
6282
interruptable: true
6383
) { retryOnInterrupt in
6484
var sender = SocketAddress()
85+
var ancillary = SocketDescriptor.AncillaryMessageBuffer()
6586
_ = try socket.receive(into: rawBuf,
6687
sender: &sender,
88+
ancillary: &ancillary,
6789
flags: .init(rawValue: 42),
6890
retryOnInterrupt: retryOnInterrupt)
6991
},
@@ -72,8 +94,10 @@ final class SocketTest: XCTestCase {
7294
interruptable: true
7395
) { retryOnInterrupt in
7496
let recipient = SocketAddress(ipv4: .loopback, port: 123)
97+
let ancillary = SocketDescriptor.AncillaryMessageBuffer()
7598
_ = try socket.send(UnsafeRawBufferPointer(rawBuf),
7699
to: recipient,
100+
ancillary: ancillary,
77101
flags: .init(rawValue: 42),
78102
retryOnInterrupt: retryOnInterrupt)
79103
},

0 commit comments

Comments
 (0)