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