-
Notifications
You must be signed in to change notification settings - Fork 1
/
nbio-listener.c
160 lines (135 loc) · 2.93 KB
/
nbio-listener.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/*
* This is the listener object it manages listening TCP sockets, for
* each new connection that comes in we spawn off a new proxy object.
*/
#include <compiler.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#define __USE_GNU
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <list.h>
#include <nbio.h>
#include <nbio-listener.h>
#include <os.h>
struct _listener {
struct nbio io;
listener_cbfn_t cbfn;
listener_oom_t oom;
void *priv;
};
void listener_wake(struct iothread *t, struct nbio *io)
{
nbio_wake(t, io, NBIO_READ);
}
static void listener_read(struct iothread *t, struct nbio *io)
{
struct _listener *l = (struct _listener *)io;
struct sockaddr_in sa;
socklen_t salen = sizeof(sa);
int fd;
again:
#if HAVE_ACCEPT4
fd = accept4(l->io.fd, (struct sockaddr *)&sa, &salen,
SOCK_NONBLOCK|SOCK_CLOEXEC);
#else
fd = accept(l->io.fd, (struct sockaddr *)&sa, &salen);
#endif
if ( fd < 0 ) {
switch(errno) {
case ENFILE:
case EMFILE:
case ENOMEM:
case ENOBUFS:
(*l->oom)(t, io);
break;
case EAGAIN:
nbio_inactive(t, &l->io, NBIO_READ);
break;
}
return;
}
#if !HAVE_ACCEPT4
if ( !fd_block(fd, 0) )
return;
#endif
//printf("Accepted connection from %s:%u\n",
// inet_ntoa(sa.sin_addr),
// htons(sa.sin_port));
(*l->cbfn)(t, fd, l->priv);
/* Make sure to service just-accepted connection before
* acccepting new ones Probably it's petty, sure it's a bit
* hacky, it relies on knowing that nbio_set_wait puts us at
* the end of the active queue
*/
nbio_set_wait(t, io, NBIO_READ);
goto again;
}
static void listener_dtor(struct iothread *t, struct nbio *io)
{
close(io->fd);
free(io);
}
static struct nbio_ops listener_ops = {
.read = listener_read,
.dtor = listener_dtor,
};
listener_t listener_inet(struct iothread *t, int type, int proto,
uint32_t addr, uint16_t port,
listener_cbfn_t cb, void *priv,
listener_oom_t oom)
{
struct sockaddr_in sa;
struct _listener *l;
l = calloc(1, sizeof(*l));
if ( l == NULL )
goto out;
INIT_LIST_HEAD(&l->io.list);
l->cbfn = cb;
l->oom = oom;
l->priv = priv;
l->io.fd = socket(PF_INET, type, proto);
if ( l->io.fd < 0 )
goto out_free;
#if 1
do{
int val = 1;
setsockopt(l->io.fd, SOL_SOCKET, SO_REUSEADDR,
&val, sizeof(val));
}while(0);
#endif
#ifdef TCP_FASTOPEN
do{
int q = 64;
setsockopt(l->io.fd, SOL_TCP, TCP_FASTOPEN,
&q, sizeof(q));
}while(0);
#endif
if ( !fd_block(l->io.fd, 0) )
goto out_close;
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = htonl(addr);
sa.sin_port = htons(port);
if ( bind(l->io.fd, (struct sockaddr *)&sa, sizeof(sa)) )
goto out_close;
if ( listen(l->io.fd, 64) )
goto out_close;
l->io.ops = &listener_ops;
nbio_add(t, &l->io, NBIO_READ);
/* success */
goto out;
out_close:
close(l->io.fd);
out_free:
free(l);
l = NULL;
out:
return l;
}