1 | module net |
2 | |
3 | import strings |
4 | |
5 | const ( |
6 | crlf = '\r\n' |
7 | msg_peek = 0x02 |
8 | max_read = 400 |
9 | max_read_line_len = 1048576 |
10 | ) |
11 | |
12 | // get_blocking returns whether the connection is in a blocking state, |
13 | // that is calls to .read_line, C.recv etc will block till there is new |
14 | // data arrived, instead of returning immediately. |
15 | pub fn (mut con TcpConn) get_blocking() bool { |
16 | // flags := C.fcntl(con.sock.handle, C.F_GETFL, 0) |
17 | // return 0 == flags & C.O_NONBLOCK |
18 | return con.is_blocking |
19 | } |
20 | |
21 | // set_blocking will change the state of the connection to either blocking, |
22 | // when state is true, or non blocking (false). |
23 | // The default for `net` tcp connections is the non blocking mode. |
24 | // Calling .read_line will set the connection to blocking mode. |
25 | pub fn (mut con TcpConn) set_blocking(state bool) ! { |
26 | con.is_blocking = state |
27 | $if windows { |
28 | mut t := u32(0) |
29 | if !con.is_blocking { |
30 | t = 1 |
31 | } |
32 | socket_error(C.ioctlsocket(con.sock.handle, fionbio, &t))! |
33 | } $else { |
34 | mut flags := C.fcntl(con.sock.handle, C.F_GETFL, 0) |
35 | if state { |
36 | flags &= ~C.O_NONBLOCK |
37 | } else { |
38 | flags |= C.O_NONBLOCK |
39 | } |
40 | socket_error(C.fcntl(con.sock.handle, C.F_SETFL, flags))! |
41 | } |
42 | } |
43 | |
44 | // read_line is a *simple*, *non customizable*, blocking line reader. |
45 | // It will return a line, ending with LF, or just '', on EOF. |
46 | // Note: if you want more control over the buffer, please use a buffered IO |
47 | // reader instead: `io.new_buffered_reader({reader: io.make_reader(con)})` |
48 | pub fn (mut con TcpConn) read_line() string { |
49 | return con.read_line_max(net.max_read_line_len) |
50 | } |
51 | |
52 | // read_line_max is a *simple*, *non customizable*, blocking line reader. |
53 | // It will return a line, ending with LF, '' on EOF. |
54 | // It stops reading, when the result line length exceeds max_line_len. |
55 | [manualfree] |
56 | pub fn (mut con TcpConn) read_line_max(max_line_len int) string { |
57 | if !con.is_blocking { |
58 | con.set_blocking(true) or {} |
59 | } |
60 | mut buf := [net.max_read]u8{} // where C.recv will store the network data |
61 | mut res := strings.new_builder(net.max_read) // The final result, including the ending \n. |
62 | defer { |
63 | unsafe { res.free() } |
64 | } |
65 | bstart := unsafe { &buf[0] } |
66 | for { |
67 | n := C.recv(con.sock.handle, bstart, net.max_read - 1, net.msg_peek | msg_nosignal) |
68 | if n <= 0 { |
69 | return res.str() |
70 | } |
71 | buf[n] = `\0` |
72 | mut eol_idx := -1 |
73 | mut lend := n |
74 | for i in 0 .. n { |
75 | if buf[i] == `\n` { |
76 | eol_idx = i |
77 | lend = i + 1 |
78 | buf[lend] = `\0` |
79 | break |
80 | } |
81 | } |
82 | if eol_idx > 0 { |
83 | // At this point, we are sure that recv returned valid data, |
84 | // that contains *at least* one line. |
85 | // Ensure that the block till the first \n (including it) |
86 | // is removed from the socket's receive queue, so that it does |
87 | // not get read again. |
88 | C.recv(con.sock.handle, bstart, lend, msg_nosignal) |
89 | unsafe { res.write_ptr(bstart, lend) } |
90 | break |
91 | } |
92 | // recv returned a buffer without \n in it, just store it for now: |
93 | C.recv(con.sock.handle, bstart, n, msg_nosignal) |
94 | unsafe { res.write_ptr(bstart, lend) } |
95 | if res.len > max_line_len { |
96 | break |
97 | } |
98 | } |
99 | return res.str() |
100 | } |