@@ -20,6 +20,7 @@ from lightbug_http.libc import (
20
20
AF_INET ,
21
21
AF_INET6 ,
22
22
SOCK_STREAM ,
23
+ SOCK_DGRAM ,
23
24
SOL_SOCKET ,
24
25
SO_REUSEADDR ,
25
26
SO_REUSEPORT ,
@@ -71,7 +72,7 @@ trait Connection(Movable):
71
72
fn teardown (mut self ) raises :
72
73
...
73
74
74
- fn local_addr (mut self ) -> TCPAddr:
75
+ fn local_addr (self ) -> TCPAddr:
75
76
...
76
77
77
78
fn remote_addr (self ) -> TCPAddr:
@@ -135,12 +136,8 @@ struct ListenConfig:
135
136
fn __init__ (out self , keep_alive : Duration = default_tcp_keep_alive):
136
137
self ._keep_alive = keep_alive
137
138
138
- fn listen [network : NetworkType, address_family : Int = AF_INET ](mut self , address : String) raises -> NoTLSListener:
139
+ fn listen [address_family : Int = AF_INET ](mut self , address : String) raises -> NoTLSListener:
139
140
constrained[address_family in [AF_INET , AF_INET6 ], " Address family must be either AF_INET or AF_INET6." ]()
140
- constrained[
141
- network in NetworkType.SUPPORTED_TYPES ,
142
- " Unsupported network type for internet address resolution. Unix addresses are not supported yet." ,
143
- ]()
144
141
var local = parse_address(address)
145
142
var addr = TCPAddr(local[0 ], local[1 ])
146
143
var socket : Socket[TCPAddr]
@@ -196,18 +193,18 @@ struct ListenConfig:
196
193
return listener^
197
194
198
195
199
- struct TCPConnection ( Connection ) :
196
+ struct TCPConnection :
200
197
var socket : Socket[TCPAddr]
201
198
202
- fn __init__ (inout self , owned socket : Socket[TCPAddr]):
199
+ fn __init__ (out self , owned socket : Socket[TCPAddr]):
203
200
self .socket = socket^
204
201
205
- fn __moveinit__ (inout self , owned existing : Self):
202
+ fn __moveinit__ (out self , owned existing : Self):
206
203
self .socket = existing.socket^
207
204
208
205
fn read (self , mut buf : Bytes) raises -> Int:
209
206
try :
210
- return self .socket.receive_into (buf)
207
+ return self .socket.receive (buf)
211
208
except e:
212
209
if str (e) == " EOF" :
213
210
raise e
@@ -237,13 +234,101 @@ struct TCPConnection(Connection):
237
234
fn is_closed (self ) -> Bool:
238
235
return self .socket._closed
239
236
240
- fn local_addr (mut self ) -> TCPAddr:
237
+ # TODO : Switch to property or return ref when trait supports attributes.
238
+ fn local_addr (self ) -> TCPAddr:
241
239
return self .socket.local_address()
242
240
243
241
fn remote_addr (self ) -> TCPAddr:
244
242
return self .socket.remote_address()
245
243
246
244
245
+ struct UDPConnection :
246
+ var socket : Socket[UDPAddr]
247
+
248
+ fn __init__ (out self , owned socket : Socket[UDPAddr]):
249
+ self .socket = socket^
250
+
251
+ fn __moveinit__ (out self , owned existing : Self):
252
+ self .socket = existing.socket^
253
+
254
+ fn read_from (mut self , size : Int = default_buffer_size) raises -> (Bytes, String, UInt16):
255
+ """ Reads data from the underlying file descriptor.
256
+
257
+ Args:
258
+ size: The size of the buffer to read data into.
259
+
260
+ Returns:
261
+ The number of bytes read, or an error if one occurred.
262
+
263
+ Raises:
264
+ Error: If an error occurred while reading data.
265
+ """
266
+ return self .socket.receive_from(size)
267
+
268
+ fn read_from (mut self , mut dest : Bytes) raises -> (UInt, String, UInt16):
269
+ """ Reads data from the underlying file descriptor.
270
+
271
+ Args:
272
+ dest: The buffer to read data into.
273
+
274
+ Returns:
275
+ The number of bytes read, or an error if one occurred.
276
+
277
+ Raises:
278
+ Error: If an error occurred while reading data.
279
+ """
280
+ return self .socket.receive_from(dest)
281
+
282
+ fn write_to (mut self , src : Span[Byte], address : UDPAddr) raises -> Int:
283
+ """ Writes data to the underlying file descriptor.
284
+
285
+ Args:
286
+ src: The buffer to read data into.
287
+ address: The remote peer address.
288
+
289
+ Returns:
290
+ The number of bytes written, or an error if one occurred.
291
+
292
+ Raises:
293
+ Error: If an error occurred while writing data.
294
+ """
295
+ return self .socket.send_to(src, address.ip, address.port)
296
+
297
+ fn write_to (mut self , src : Span[Byte], host : String, port : UInt16) raises -> Int:
298
+ """ Writes data to the underlying file descriptor.
299
+
300
+ Args:
301
+ src: The buffer to read data into.
302
+ host: The remote peer address in IPv4 format.
303
+ port: The remote peer port.
304
+
305
+ Returns:
306
+ The number of bytes written, or an error if one occurred.
307
+
308
+ Raises:
309
+ Error: If an error occurred while writing data.
310
+ """
311
+ return self .socket.send_to(src, host, port)
312
+
313
+ fn close (mut self ) raises :
314
+ self .socket.close()
315
+
316
+ fn shutdown (mut self ) raises :
317
+ self .socket.shutdown()
318
+
319
+ fn teardown (mut self ) raises :
320
+ self .socket.teardown()
321
+
322
+ fn is_closed (self ) -> Bool:
323
+ return self .socket._closed
324
+
325
+ fn local_addr (self ) -> ref [self .socket._local_address] UDPAddr:
326
+ return self .socket.local_address()
327
+
328
+ fn remote_addr (self ) -> ref [self .socket._remote_address] UDPAddr:
329
+ return self .socket.remote_address()
330
+
331
+
247
332
@value
248
333
@register_passable (" trivial" )
249
334
struct addrinfo_macos (AnAddrInfo ):
@@ -261,12 +346,19 @@ struct addrinfo_macos(AnAddrInfo):
261
346
var ai_addr : UnsafePointer[sockaddr]
262
347
var ai_next : OpaquePointer
263
348
264
- fn __init__ (out self , ai_flags : c_int = 0 , ai_family : c_int = 0 , ai_socktype : c_int = 0 , ai_protocol : c_int = 0 ):
265
- self .ai_flags = 0
266
- self .ai_family = 0
267
- self .ai_socktype = 0
268
- self .ai_protocol = 0
269
- self .ai_addrlen = 0
349
+ fn __init__ (
350
+ out self ,
351
+ ai_flags : c_int = 0 ,
352
+ ai_family : c_int = 0 ,
353
+ ai_socktype : c_int = 0 ,
354
+ ai_protocol : c_int = 0 ,
355
+ ai_addrlen : socklen_t = 0 ,
356
+ ):
357
+ self .ai_flags = ai_flags
358
+ self .ai_family = ai_family
359
+ self .ai_socktype = ai_socktype
360
+ self .ai_protocol = ai_protocol
361
+ self .ai_addrlen = ai_addrlen
270
362
self .ai_canonname = UnsafePointer[c_char]()
271
363
self .ai_addr = UnsafePointer[sockaddr]()
272
364
self .ai_next = OpaquePointer()
@@ -314,12 +406,19 @@ struct addrinfo_unix(AnAddrInfo):
314
406
var ai_canonname : UnsafePointer[c_char]
315
407
var ai_next : OpaquePointer
316
408
317
- fn __init__ (out self , ai_flags : c_int = 0 , ai_family : c_int = 0 , ai_socktype : c_int = 0 , ai_protocol : c_int = 0 ):
409
+ fn __init__ (
410
+ out self ,
411
+ ai_flags : c_int = 0 ,
412
+ ai_family : c_int = 0 ,
413
+ ai_socktype : c_int = 0 ,
414
+ ai_protocol : c_int = 0 ,
415
+ ai_addrlen : socklen_t = 0 ,
416
+ ):
318
417
self .ai_flags = ai_flags
319
418
self .ai_family = ai_family
320
419
self .ai_socktype = ai_socktype
321
420
self .ai_protocol = ai_protocol
322
- self .ai_addrlen = 0
421
+ self .ai_addrlen = ai_addrlen
323
422
self .ai_addr = UnsafePointer[sockaddr]()
324
423
self .ai_canonname = UnsafePointer[c_char]()
325
424
self .ai_next = OpaquePointer()
@@ -395,10 +494,10 @@ struct TCPAddr(Addr):
395
494
fn network (self ) -> String:
396
495
return NetworkType.tcp.value
397
496
398
- fn __eq__ (self , other : TCPAddr ) -> Bool:
497
+ fn __eq__ (self , other : Self ) -> Bool:
399
498
return self .ip == other.ip and self .port == other.port and self .zone == other.zone
400
499
401
- fn __ne__ (self , other : TCPAddr ) -> Bool:
500
+ fn __ne__ (self , other : Self ) -> Bool:
402
501
return not self == other
403
502
404
503
fn __str__ (self ) -> String:
@@ -413,6 +512,140 @@ struct TCPAddr(Addr):
413
512
writer.write(" TCPAddr(" , " ip=" , repr (self .ip), " , port=" , str (self .port), " , zone=" , repr (self .zone), " )" )
414
513
415
514
515
+ @value
516
+ struct UDPAddr (Addr ):
517
+ alias _type = " UDPAddr"
518
+ var ip : String
519
+ var port : UInt16
520
+ var zone : String # IPv6 addressing zone
521
+
522
+ fn __init__ (out self ):
523
+ self .ip = " 127.0.0.1"
524
+ self .port = 8000
525
+ self .zone = " "
526
+
527
+ fn __init__ (out self , ip : String = " 127.0.0.1" , port : UInt16 = 8000 ):
528
+ self .ip = ip
529
+ self .port = port
530
+ self .zone = " "
531
+
532
+ fn network (self ) -> String:
533
+ return NetworkType.udp.value
534
+
535
+ fn __eq__ (self , other : Self) -> Bool:
536
+ return self .ip == other.ip and self .port == other.port and self .zone == other.zone
537
+
538
+ fn __ne__ (self , other : Self) -> Bool:
539
+ return not self == other
540
+
541
+ fn __str__ (self ) -> String:
542
+ if self .zone != " " :
543
+ return join_host_port(self .ip + " %" + self .zone, str (self .port))
544
+ return join_host_port(self .ip, str (self .port))
545
+
546
+ fn __repr__ (self ) -> String:
547
+ return String.write(self )
548
+
549
+ fn write_to [W : Writer, //](self , mut writer : W):
550
+ writer.write(" UDPAddr(" , " ip=" , repr (self .ip), " , port=" , str (self .port), " , zone=" , repr (self .zone), " )" )
551
+
552
+
553
+ fn listen_udp (local_address : UDPAddr) raises -> UDPConnection:
554
+ """ Creates a new UDP listener.
555
+
556
+ Args:
557
+ local_address: The local address to listen on.
558
+
559
+ Returns:
560
+ A UDP connection.
561
+
562
+ Raises:
563
+ Error: If the address is invalid or failed to bind the socket.
564
+ """
565
+ socket = Socket[UDPAddr](socket_type = SOCK_DGRAM )
566
+ socket.bind(local_address.ip, local_address.port)
567
+ return UDPConnection(socket^ )
568
+
569
+
570
+ fn listen_udp (local_address : String) raises -> UDPConnection:
571
+ """ Creates a new UDP listener.
572
+
573
+ Args:
574
+ local_address: The address to listen on. The format is "host:port".
575
+
576
+ Returns:
577
+ A UDP connection.
578
+
579
+ Raises:
580
+ Error: If the address is invalid or failed to bind the socket.
581
+ """
582
+ var address = parse_address(local_address)
583
+ return listen_udp(UDPAddr(address[0 ], address[1 ]))
584
+
585
+
586
+ fn listen_udp (host : String, port : UInt16) raises -> UDPConnection:
587
+ """ Creates a new UDP listener.
588
+
589
+ Args:
590
+ host: The address to listen on in ipv4 format.
591
+ port: The port number.
592
+
593
+ Returns:
594
+ A UDP connection.
595
+
596
+ Raises:
597
+ Error: If the address is invalid or failed to bind the socket.
598
+ """
599
+ return listen_udp(UDPAddr(host, port))
600
+
601
+
602
+ fn dial_udp (local_address : UDPAddr) raises -> UDPConnection:
603
+ """ Connects to the address on the named network. The network must be "udp", "udp4", or "udp6".
604
+
605
+ Args:
606
+ local_address: The local address.
607
+
608
+ Returns:
609
+ The UDP connection.
610
+
611
+ Raises:
612
+ Error: If the network type is not supported or failed to connect to the address.
613
+ """
614
+ return UDPConnection(Socket(local_address = local_address, socket_type = SOCK_DGRAM ))
615
+
616
+
617
+ fn dial_udp (local_address : String) raises -> UDPConnection:
618
+ """ Connects to the address on the named network. The network must be "udp", "udp4", or "udp6".
619
+
620
+ Args:
621
+ local_address: The local address.
622
+
623
+ Returns:
624
+ The UDP connection.
625
+
626
+ Raises:
627
+ Error: If the network type is not supported or failed to connect to the address.
628
+ """
629
+ var address = parse_address(local_address)
630
+ return dial_udp(UDPAddr(address[0 ], address[1 ]))
631
+
632
+
633
+ fn dial_udp (host : String, port : UInt16) raises -> UDPConnection:
634
+ """ Connects to the address on the named network. The network must be "udp", "udp4", or "udp6".
635
+
636
+ Args:
637
+ host: The host to connect to.
638
+ port: The port to connect on.
639
+
640
+ Returns:
641
+ The UDP connection.
642
+
643
+ Raises:
644
+ Error: If the network type is not supported or failed to connect to the address.
645
+ """
646
+ return dial_udp(UDPAddr(host, port))
647
+
648
+
416
649
# TODO : Support IPv6 long form.
417
650
fn join_host_port (host : String, port : String) -> String:
418
651
if host.find(" :" ) != - 1 : # must be IPv6 literal
0 commit comments