summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2023-11-23 21:47:33 +0700
committerShulhan <ms@kilabit.info>2023-11-25 01:16:35 +0700
commitc64476c10a8a6d331278fdc96f896559d3939f17 (patch)
tree45e397329595b15a66e454da5cfa79c90846a5dc
parenta23642f407b380693f38d9be647f1d2e60b428d4 (diff)
downloadpakakeh.go-c64476c10a8a6d331278fdc96f896559d3939f17.tar.xz
lib/net: add method Read
The Read method read packet from raw connection. If the conn parameter is nil it will return [net.ErrClosed]. The bufsize parameter set the size of buffer for each read operation, default to 1024 if not set or invalid (less than 0 or greater than 65535). The timeout parameter set how long to wait for data before considering it as failed. If its not set, less or equal to 0, it will wait forever. If no data received and timeout is set, it will return [ErrReadTimeout]. If there is data received and connection closed at the same time, it will return the data first without error. The subsequent Read will return empty packet with [ErrClosed]. Signed-off-by: Shulhan <ms@kilabit.info>
-rw-r--r--lib/net/net.go79
1 files changed, 75 insertions, 4 deletions
diff --git a/lib/net/net.go b/lib/net/net.go
index 5cc2f223..53ab4bd2 100644
--- a/lib/net/net.go
+++ b/lib/net/net.go
@@ -8,6 +8,7 @@ package net
import (
"errors"
"fmt"
+ "math"
"net"
"strings"
"time"
@@ -17,10 +18,12 @@ const (
maxPort = (1 << 16) - 1
)
-// List of error messages.
-var (
- ErrHostAddress = errors.New("invalid host address")
-)
+// ErrHostAddress define an error if address of connection is invalid.
+var ErrHostAddress = errors.New("invalid host address")
+
+// ErrReadTimeout define an error when [Read] operation receive no data
+// after waiting for specific duration.
+var ErrReadTimeout = errors.New(`read timeout`)
// Type of network.
type Type uint16
@@ -100,6 +103,74 @@ func IsTypeTransport(t Type) bool {
return IsTypeTCP(t) || IsTypeUDP(t)
}
+// Read packet from network.
+//
+// If the conn parameter is nil it will return [net.ErrClosed].
+//
+// The bufsize parameter set the size of buffer for each read operation,
+// default to 1024 if not set or invalid (less than 0 or greater than
+// 65535).
+//
+// The timeout parameter set how long to wait for data before considering
+// it as failed.
+// If its not set, less or equal to 0, it will wait forever.
+// If no data received and timeout is set, it will return [ErrReadTimeout].
+//
+// If there is data received and connection closed at the same time, it will
+// return the data first without error.
+// The subsequent Read will return empty packet with [ErrClosed].
+func Read(conn net.Conn, bufsize int, timeout time.Duration) (packet []byte, err error) {
+ var logp = `Read`
+
+ if conn == nil {
+ return nil, fmt.Errorf(`%s: %w`, logp, net.ErrClosed)
+ }
+ if bufsize <= 0 || bufsize > math.MaxUint16 {
+ bufsize = 1024
+ }
+
+ var (
+ buf []byte = make([]byte, bufsize)
+ n int
+ )
+
+ if timeout > 0 {
+ err = conn.SetReadDeadline(time.Now().Add(timeout))
+ if err != nil {
+ return nil, fmt.Errorf(`%s: %w`, logp, err)
+ }
+ }
+ for {
+ n, err = conn.Read(buf)
+ if err != nil {
+ var (
+ neterr net.Error
+ ok bool
+ )
+ neterr, ok = err.(net.Error)
+ if ok && neterr.Timeout() {
+ return nil, ErrReadTimeout
+ }
+ return nil, fmt.Errorf(`%s: %w`, logp, err)
+ }
+ if n == 0 {
+ // Connection closed by peer.
+ break
+ }
+
+ packet = append(packet, buf[:n]...)
+ if n < len(buf) {
+ break
+ }
+ // Keep reading if we read full buffer.
+ }
+ if len(packet) == 0 {
+ return nil, net.ErrClosed
+ }
+ return packet, nil
+
+}
+
// ToDotIPv6 convert the IPv6 address format from "::1" format into
// "0.0.0.0 ... 0.0.0.1".
//