Skip to content

Commit f28a800

Browse files
committed
avoid recv blocking when waiting for header
1 parent 4d20131 commit f28a800

File tree

5 files changed

+46
-12
lines changed

5 files changed

+46
-12
lines changed

proxyproto.nimble

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Package
22

3-
version = "0.1.3"
3+
version = "0.1.4"
44
author = "Huy Doan"
55
description = "PROXY Protocol enabler for aged programs"
66
license = "MIT"

src/nim.cfg

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
--app:lib
2+
--gc:regions

src/proxyproto.nim

+31-6
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,32 @@ proc pp_handshake*(fd: SocketHandle, sa: ptr SockAddr, sl: ptr Socklen): int =
5353
hdr: Header
5454
src: Sockaddr_in
5555
src6: Sockaddr_in6
56+
tv: Timeval
57+
tv_size = sizeof(tv).SockLen
58+
reset_tv = false
59+
flags = fcntl(fd, F_GETFL, 0)
60+
61+
# make sure fd is blocking
62+
if (flags and O_NONBLOCK) == 0:
63+
# get recv timeout, and set it if not set
64+
if getsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, addr tv, addr tv_size) >= 0 and tv.tv_sec.int == 0 and tv.tv_usec == 0:
65+
reset_tv = true
66+
tv.tv_usec = 500
67+
discard setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, addr tv, tv_size)
5668

5769
while true:
58-
result = recv(fd, addr hdr, sizeof(hdr), 0)
70+
result = recv(fd, addr hdr, sizeof(hdr), MSG_PEEK)
5971
if not (result == -1 and errno == EINTR):
6072
break
73+
6174
if result == -1:
62-
return if (errno == EAGAIN): 0 else: -1
75+
return if (errno == EAGAIN or errno == EWOULDBLOCK): 0 else: -1
76+
77+
# reset recv timeout
78+
if reset_tv:
79+
tv.tv_sec = 0.Time
80+
tv.tv_usec = 0
81+
discard setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, addr tv, tv_size)
6382

6483
if result >= 16 and hdr.v2.sig == v2sig and (hdr.v2.ver_cmd and 0xF0) == 0x20 and result >= 16 + ntohs(hdr.v2.length).int:
6584
case hdr.v2.ver_cmd and 0xF
@@ -145,22 +164,28 @@ proc pp_handshake*(fd: SocketHandle, sa: ptr SockAddr, sl: ptr Socklen): int =
145164
else: # Wrong protocol
146165
return -1
147166

167+
while true:
168+
result = recv(fd, addr hdr, result, 0)
169+
if not (result == -1 and errno == EINTR):
170+
break
171+
148172
when isMainModule:
149-
type AcceptProc = proc(a1: SocketHandle, a2: ptr SockAddr, a3: ptr Socklen): SocketHandle {.cdecl.}
150173
var
151174
RTLD_NEXT {.importc: "RTLD_NEXT", header: "<dlfcn.h>".}: LibHandle
152-
real_accept: AcceptProc
175+
sys_accept: proc(a1: SocketHandle, a2: ptr SockAddr, a3: ptr Socklen): SocketHandle {.cdecl.}
153176

154177
proc pp_accept*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr Socklen): SocketHandle {.exportc:"accept",cdecl.} =
155-
result = real_accept(a1, a2, a3)
178+
result = sys_accept(a1, a2, a3)
156179
if result.int != -1:
157180
if pp_handshake(result, a2, a3) <= 0:
158181
echo "[PROXY] connection 0x", $result.int, " invalid proxy-protocol header"
182+
discard close(result)
183+
errno = ECONNABORTED
159184
result = SocketHandle(-1)
160185

161186
let accept_ptr = symAddr(RTLD_NEXT, "accept")
162187
if accept_ptr == nil:
163188
quit "[PROXY] cannot find accept proc"
164189

165-
real_accept = cast[AcceptProc](accept_ptr)
190+
sys_accept = cast[sys_accept.type](accept_ptr)
166191
echo "[PROXY] hook accept OK"

tests/echo.nim

+8-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,14 @@ proc serve() {.async.} =
1818
server.listen()
1919

2020
while true:
21-
let client = await server.accept()
22-
discard processClient(client)
21+
var client: AsyncSocket
22+
try:
23+
client = await server.accept()
24+
except:
25+
echo "Accept error"
26+
if client != nil:
27+
discard processClient(client)
28+
2329

2430
asyncCheck serve()
2531
runForever()

tests/server.nim

+5-3
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ proc processClient(client: AsyncSocket) {.async.} =
99
slen: SockLen
1010
src: SockAddr_in
1111

12-
if getpeername(client.getFd(), cast[ptr SockAddr](addr src), addr slen) == 0:
13-
echo "remote ip ", inet_ntoa(src.sin_addr)
12+
#if getpeername(client.getFd(), cast[ptr SockAddr](addr src), addr slen) == 0:
13+
#echo "remote ", inet_ntoa(src.sin_addr), ":", htons(src.sin_port), " : ", line
14+
echo "remote ", client.getPeerAddr(), " : ", line
15+
1416

1517
proc serve() {.async.} =
1618
var server = newAsyncSocket()
17-
server.bindAddr(Port(6001))
19+
server.bindAddr(Port(4444))
1820
server.listen()
1921

2022
while true:

0 commit comments

Comments
 (0)