@@ -218,8 +218,8 @@ func tcpCloseListener(ctx context.Context, rpc *msgpackrpc.Connection, params []
218218}
219219
220220func tcpRead (ctx context.Context , rpc * msgpackrpc.Connection , params []any ) (_result any , _err any ) {
221- if len (params ) != 2 {
222- return nil , []any {1 , "Invalid number of parameters, expected (connection ID, max bytes to read)" }
221+ if len (params ) != 2 && len ( params ) != 3 {
222+ return nil , []any {1 , "Invalid number of parameters, expected (connection ID, max bytes to read[, optional timeout in ms] )" }
223223 }
224224 id , ok := msgpackrpc .ToUint (params [0 ])
225225 if ! ok {
@@ -235,12 +235,22 @@ func tcpRead(ctx context.Context, rpc *msgpackrpc.Connection, params []any) (_re
235235 if ! ok {
236236 return nil , []any {1 , "Invalid parameter type, expected int for max bytes to read" }
237237 }
238+ var deadline time.Time // default value == no timeout
239+ if len (params ) == 2 {
240+ // It seems that there is no way to set a 0 ms timeout (immediate return) on a TCP connection.
241+ // Setting the read deadline to time.Now() will always returns an empty (zero bytes)
242+ // read, so we set it by default to a very short duration in the future (1 ms).
243+ deadline = time .Now ().Add (time .Millisecond )
244+ } else if ms , ok := msgpackrpc .ToInt (params [2 ]); ! ok {
245+ return nil , []any {1 , "Invalid parameter type, expected int for timeout in ms" }
246+ } else if ms > 0 {
247+ deadline = time .Now ().Add (time .Duration (ms ) * time .Millisecond )
248+ } else if ms == 0 {
249+ // No timeout
250+ }
238251
239252 buffer := make ([]byte , maxBytes )
240- // It seems that the only way to make a non-blocking read is to set a read deadline.
241- // BTW setting the read deadline to time.Now() will always returns an empty (zero bytes)
242- // read, so we set it to a very short duration in the future.
243- if err := conn .SetReadDeadline (time .Now ().Add (time .Millisecond )); err != nil {
253+ if err := conn .SetReadDeadline (deadline ); err != nil {
244254 return nil , []any {3 , "Failed to set read timeout: " + err .Error ()}
245255 }
246256 n , err := conn .Read (buffer )
@@ -411,8 +421,8 @@ func udpWrite(ctx context.Context, rpc *msgpackrpc.Connection, params []any) (_r
411421}
412422
413423func udpRead (ctx context.Context , rpc * msgpackrpc.Connection , params []any ) (_result any , _err any ) {
414- if len (params ) != 2 {
415- return nil , []any {1 , "Invalid number of parameters, expected (UDP connection ID, max bytes to read)" }
424+ if len (params ) != 2 && len ( params ) != 3 {
425+ return nil , []any {1 , "Invalid number of parameters, expected (UDP connection ID, max bytes to read[, optional timeout in ms] )" }
416426 }
417427 id , ok := msgpackrpc .ToUint (params [0 ])
418428 if ! ok {
@@ -428,10 +438,26 @@ func udpRead(ctx context.Context, rpc *msgpackrpc.Connection, params []any) (_re
428438 if ! ok {
429439 return nil , []any {1 , "Invalid parameter type, expected uint for max bytes to read" }
430440 }
441+ var deadline time.Time // default value == no timeout
442+ if len (params ) == 2 {
443+ // No timeout
444+ } else if ms , ok := msgpackrpc .ToInt (params [2 ]); ! ok {
445+ return nil , []any {1 , "Invalid parameter type, expected int for timeout in ms" }
446+ } else if ms > 0 {
447+ deadline = time .Now ().Add (time .Duration (ms ) * time .Millisecond )
448+ } else if ms == 0 {
449+ // No timeout
450+ }
431451
452+ if err := udpConn .SetReadDeadline (deadline ); err != nil {
453+ return nil , []any {3 , "Failed to set read deadline: " + err .Error ()}
454+ }
432455 buffer := make ([]byte , maxBytes )
433-
434456 n , addr , err := udpConn .ReadFrom (buffer )
457+ if errors .Is (err , os .ErrDeadlineExceeded ) {
458+ // timeout
459+ return nil , []any {5 , "Timeout" }
460+ }
435461 if err != nil {
436462 return nil , []any {3 , "Failed to read from UDP connection: " + err .Error ()}
437463 }
0 commit comments