v / vlib / net / tcp_read_line.c.v
89 lines · 83 sloc · 2.86 KB · 008aaad99981918c51194d7aaaaaccb4c258f244
Raw
1module net
2
3import strings
4
5const crlf = '\r\n'
6const msg_peek = 0x02
7const max_read = 400
8const 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.
13pub 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.
25pub 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)})`
37pub 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]
45pub 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