summaryrefslogtreecommitdiff
path: root/src/libevent/event_tcp.c (plain)
blob: 679e06b06b9c9e04f25104466317a67d553aead8
1/***********************************************************************
2*
3* event_tcp.c -- implementation of event-driven socket I/O.
4*
5* Copyright (C) 2001 Roaring Penguin Software Inc.
6*
7* This program may be distributed according to the terms of the GNU
8* General Public License, version 2 or (at your option) any later version.
9*
10* LIC: GPL
11*
12***********************************************************************/
13
14static char const RCSID[] =
15"$Id$";
16
17#include "event_tcp.h"
18#include <unistd.h>
19#include <fcntl.h>
20#include <sys/types.h>
21#include <stdlib.h>
22#include <errno.h>
23#include <string.h>
24
25static void free_state(EventTcpState *state);
26
27typedef struct EventTcpConnectState_t {
28 int fd;
29 EventHandler *conn;
30 EventTcpConnectFunc f;
31 void *data;
32} EventTcpConnectState;
33
34/**********************************************************************
35* %FUNCTION: handle_accept
36* %ARGUMENTS:
37* es -- event selector
38* fd -- socket
39* flags -- ignored
40* data -- the accept callback function
41* %RETURNS:
42* Nothing
43* %DESCRIPTION:
44* Calls accept; if a connection arrives, calls the accept callback
45* function with connected descriptor
46***********************************************************************/
47static void
48handle_accept(EventSelector *es,
49 int fd,
50 unsigned int flags,
51 void *data)
52{
53 int conn;
54 EventTcpAcceptFunc f;
55
56 EVENT_DEBUG(("tcp_handle_accept(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data));
57 conn = accept(fd, NULL, NULL);
58 if (conn < 0) return;
59 f = (EventTcpAcceptFunc) data;
60
61 f(es, conn);
62}
63
64/**********************************************************************
65* %FUNCTION: handle_connect
66* %ARGUMENTS:
67* es -- event selector
68* fd -- socket
69* flags -- ignored
70* data -- the accept callback function
71* %RETURNS:
72* Nothing
73* %DESCRIPTION:
74* Calls accept; if a connection arrives, calls the accept callback
75* function with connected descriptor
76***********************************************************************/
77static void
78handle_connect(EventSelector *es,
79 int fd,
80 unsigned int flags,
81 void *data)
82{
83 int error = 0;
84 socklen_t len = sizeof(error);
85 EventTcpConnectState *state = (EventTcpConnectState *) data;
86
87 EVENT_DEBUG(("tcp_handle_connect(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data));
88
89 /* Cancel writable event */
90 Event_DelHandler(es, state->conn);
91 state->conn = NULL;
92
93 /* Timeout? */
94 if (flags & EVENT_FLAG_TIMEOUT) {
95 errno = ETIMEDOUT;
96 state->f(es, fd, EVENT_TCP_FLAG_TIMEOUT, state->data);
97 free(state);
98 return;
99 }
100
101 /* Check for pending error */
102 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
103 state->f(es, fd, EVENT_TCP_FLAG_IOERROR, state->data);
104 free(state);
105 return;
106 }
107 if (error) {
108 errno = error;
109 state->f(es, fd, EVENT_TCP_FLAG_IOERROR, state->data);
110 free(state);
111 return;
112 }
113
114 /* It looks cool! */
115 state->f(es, fd, EVENT_TCP_FLAG_COMPLETE, state->data);
116 free(state);
117}
118
119/**********************************************************************
120* %FUNCTION: EventTcp_CreateAcceptor
121* %ARGUMENTS:
122* es -- event selector
123* socket -- listening socket
124* f -- function to call when a connection is accepted
125* data -- extra data to pass to f.
126* %RETURNS:
127* An event handler on success, NULL on failure.
128* %DESCRIPTION:
129* Sets up an accepting socket and calls "f" whenever a new
130* connection arrives.
131***********************************************************************/
132EventHandler *
133EventTcp_CreateAcceptor(EventSelector *es,
134 int socket,
135 EventTcpAcceptFunc f)
136{
137 int flags;
138
139 EVENT_DEBUG(("EventTcp_CreateAcceptor(es=%p, socket=%d)\n", es, socket));
140 /* Make sure socket is non-blocking */
141 flags = fcntl(socket, F_GETFL, 0);
142 if (flags == -1) {
143 return NULL;
144 }
145 if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) {
146 return NULL;
147 }
148
149 return Event_AddHandler(es, socket, EVENT_FLAG_READABLE,
150 handle_accept, (void *) f);
151
152}
153
154/**********************************************************************
155* %FUNCTION: free_state
156* %ARGUMENTS:
157* state -- EventTcpState to free
158* %RETURNS:
159* Nothing
160* %DESCRIPTION:
161* Frees all state associated with the TcpEvent.
162***********************************************************************/
163static void
164free_state(EventTcpState *state)
165{
166 if (!state) return;
167 EVENT_DEBUG(("tcp_free_state(state=%p)\n", state));
168 if (state->buf) free(state->buf);
169 if (state->eh) Event_DelHandler(state->es, state->eh);
170 free(state);
171}
172
173/**********************************************************************
174* %FUNCTION: handle_readable
175* %ARGUMENTS:
176* es -- event selector
177* fd -- the readable socket
178* flags -- ignored
179* data -- the EventTcpState object
180* %RETURNS:
181* Nothing
182* %DESCRIPTION:
183* Continues to fill buffer. Calls callback when done.
184***********************************************************************/
185static void
186handle_readable(EventSelector *es,
187 int fd,
188 unsigned int flags,
189 void *data)
190{
191 EventTcpState *state = (EventTcpState *) data;
192 int done = state->cur - state->buf;
193 int togo = state->len - done;
194 int nread = 0;
195 int flag;
196
197 EVENT_DEBUG(("tcp_handle_readable(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data));
198
199 /* Timed out? */
200 if (flags & EVENT_FLAG_TIMEOUT) {
201 errno = ETIMEDOUT;
202 (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_TIMEOUT,
203 state->data);
204 free_state(state);
205 return;
206 }
207 if (state->delim < 0) {
208 /* Not looking for a delimiter */
209 /* togo had better not be zero here! */
210 nread = read(fd, state->cur, togo);
211 if (nread <= 0) {
212 /* Change connection reset to EOF if we have read at least
213 one char */
214 if (nread < 0 && errno == ECONNRESET && done > 0) {
215 nread = 0;
216 }
217 flag = (nread) ? EVENT_TCP_FLAG_IOERROR : EVENT_TCP_FLAG_EOF;
218 /* error or EOF */
219 (state->f)(es, state->socket, state->buf, done, flag, state->data);
220 free_state(state);
221 return;
222 }
223 state->cur += nread;
224 done += nread;
225 if (done >= state->len) {
226 /* Read enough! */
227 (state->f)(es, state->socket, state->buf, done,
228 EVENT_TCP_FLAG_COMPLETE, state->data);
229 free_state(state);
230 return;
231 }
232 } else {
233 /* Looking for a delimiter */
234 while ( (togo > 0) && (nread = read(fd, state->cur, 1)) == 1) {
235 togo--;
236 done++;
237 state->cur++;
238 if (*(state->cur - 1) == state->delim) break;
239 }
240
241 if (nread <= 0) {
242 /* Error or EOF -- check for EAGAIN */
243 if (nread < 0 && errno == EAGAIN) return;
244 }
245
246 /* Some other error, or EOF, or delimiter, or read enough */
247 if (nread < 0) {
248 flag = EVENT_TCP_FLAG_IOERROR;
249 } else if (nread == 0) {
250 flag = EVENT_TCP_FLAG_EOF;
251 } else {
252 flag = EVENT_TCP_FLAG_COMPLETE;
253 }
254 (state->f)(es, state->socket, state->buf, done, flag, state->data);
255 free_state(state);
256 return;
257 }
258}
259
260/**********************************************************************
261* %FUNCTION: handle_writeable
262* %ARGUMENTS:
263* es -- event selector
264* fd -- the writeable socket
265* flags -- ignored
266* data -- the EventTcpState object
267* %RETURNS:
268* Nothing
269* %DESCRIPTION:
270* Continues to fill buffer. Calls callback when done.
271***********************************************************************/
272static void
273handle_writeable(EventSelector *es,
274 int fd,
275 unsigned int flags,
276 void *data)
277{
278 EventTcpState *state = (EventTcpState *) data;
279 int done = state->cur - state->buf;
280 int togo = state->len - done;
281 int n;
282
283 /* Timed out? */
284 if (flags & EVENT_FLAG_TIMEOUT) {
285 errno = ETIMEDOUT;
286 (state->f)(es, state->socket, state->buf, done, EVENT_TCP_FLAG_TIMEOUT,
287 state->data);
288 free_state(state);
289 return;
290 }
291
292 /* togo had better not be zero here! */
293 n = write(fd, state->cur, togo);
294
295 EVENT_DEBUG(("tcp_handle_writeable(es=%p, fd=%d, flags=%u, data=%p)\n", es, fd, flags, data));
296 if (n <= 0) {
297 /* error */
298 if (state->f) {
299 (state->f)(es, state->socket, state->buf, done,
300 EVENT_TCP_FLAG_IOERROR,
301 state->data);
302 } else {
303 close(fd);
304 }
305 free_state(state);
306 return;
307 }
308 state->cur += n;
309 done += n;
310 if (done >= state->len) {
311 /* Written enough! */
312 if (state->f) {
313 (state->f)(es, state->socket, state->buf, done,
314 EVENT_TCP_FLAG_COMPLETE, state->data);
315 } else {
316 close(fd);
317 }
318 free_state(state);
319 return;
320 }
321
322}
323
324/**********************************************************************
325* %FUNCTION: EventTcp_ReadBuf
326* %ARGUMENTS:
327* es -- event selector
328* socket -- socket to read from
329* len -- maximum number of bytes to read
330* delim -- delimiter at which to stop reading, or -1 if we should
331* read exactly len bytes
332* f -- function to call on EOF or when all bytes have been read
333* timeout -- if non-zero, timeout in seconds after which we cancel
334* operation.
335* data -- extra data to pass to function f.
336* %RETURNS:
337* A new EventTcpState token or NULL on error
338* %DESCRIPTION:
339* Sets up a handler to fill a buffer from a socket.
340***********************************************************************/
341EventTcpState *
342EventTcp_ReadBuf(EventSelector *es,
343 int socket,
344 int len,
345 int delim,
346 EventTcpIOFinishedFunc f,
347 int timeout,
348 void *data)
349{
350 EventTcpState *state;
351 int flags;
352 struct timeval t;
353
354 EVENT_DEBUG(("EventTcp_ReadBuf(es=%p, socket=%d, len=%d, delim=%d, timeout=%d)\n", es, socket, len, delim, timeout));
355 if (len <= 0) return NULL;
356 if (socket < 0) return NULL;
357
358 /* Make sure socket is non-blocking */
359 flags = fcntl(socket, F_GETFL, 0);
360 if (flags == -1) {
361 return NULL;
362 }
363 if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) {
364 return NULL;
365 }
366
367 state = malloc(sizeof(EventTcpState));
368 if (!state) return NULL;
369
370 memset(state, 0, sizeof(EventTcpState));
371
372 state->socket = socket;
373
374 state->buf = malloc(len);
375 if (!state->buf) {
376 free_state(state);
377 return NULL;
378 }
379
380 state->cur = state->buf;
381 state->len = len;
382 state->f = f;
383 state->es = es;
384
385 if (timeout <= 0) {
386 t.tv_sec = -1;
387 t.tv_usec = -1;
388 } else {
389 t.tv_sec = timeout;
390 t.tv_usec = 0;
391 }
392
393 state->eh = Event_AddHandlerWithTimeout(es, socket, EVENT_FLAG_READABLE,
394 t, handle_readable,
395 (void *) state);
396 if (!state->eh) {
397 free_state(state);
398 return NULL;
399 }
400 state->data = data;
401 state->delim = delim;
402 EVENT_DEBUG(("EventTcp_ReadBuf() -> %p\n", state));
403
404 return state;
405}
406
407/**********************************************************************
408* %FUNCTION: EventTcp_WriteBuf
409* %ARGUMENTS:
410* es -- event selector
411* socket -- socket to read from
412* buf -- buffer to write
413* len -- number of bytes to write
414* f -- function to call on EOF or when all bytes have been read
415* timeout -- timeout after which to cancel operation
416* data -- extra data to pass to function f.
417* %RETURNS:
418* A new EventTcpState token or NULL on error
419* %DESCRIPTION:
420* Sets up a handler to fill a buffer from a socket.
421***********************************************************************/
422EventTcpState *
423EventTcp_WriteBuf(EventSelector *es,
424 int socket,
425 char *buf,
426 int len,
427 EventTcpIOFinishedFunc f,
428 int timeout,
429 void *data)
430{
431 EventTcpState *state;
432 int flags;
433 struct timeval t;
434
435 EVENT_DEBUG(("EventTcp_WriteBuf(es=%p, socket=%d, len=%d, timeout=%d)\n", es, socket, len, timeout));
436 if (len <= 0) return NULL;
437 if (socket < 0) return NULL;
438
439 /* Make sure socket is non-blocking */
440 flags = fcntl(socket, F_GETFL, 0);
441 if (flags == -1) {
442 return NULL;
443 }
444 if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) == -1) {
445 return NULL;
446 }
447
448 state = malloc(sizeof(EventTcpState));
449 if (!state) return NULL;
450
451 memset(state, 0, sizeof(EventTcpState));
452
453 state->socket = socket;
454
455 state->buf = malloc(len);
456 if (!state->buf) {
457 free_state(state);
458 return NULL;
459 }
460 memcpy(state->buf, buf, len);
461
462 state->cur = state->buf;
463 state->len = len;
464 state->f = f;
465 state->es = es;
466
467 if (timeout <= 0) {
468 t.tv_sec = -1;
469 t.tv_usec = -1;
470 } else {
471 t.tv_sec = timeout;
472 t.tv_usec = 0;
473 }
474
475 state->eh = Event_AddHandlerWithTimeout(es, socket, EVENT_FLAG_WRITEABLE,
476 t, handle_writeable,
477 (void *) state);
478 if (!state->eh) {
479 free_state(state);
480 return NULL;
481 }
482
483 state->data = data;
484 state->delim = -1;
485 EVENT_DEBUG(("EventTcp_WriteBuf() -> %p\n", state));
486 return state;
487}
488
489/**********************************************************************
490* %FUNCTION: EventTcp_Connect
491* %ARGUMENTS:
492* es -- event selector
493* fd -- descriptor to connect
494* addr -- address to connect to
495* addrlen -- length of address
496* f -- function to call with connected socket
497* data -- extra data to pass to f
498* %RETURNS:
499* Nothing
500* %DESCRIPTION:
501* Does a non-blocking connect on fd
502***********************************************************************/
503void
504EventTcp_Connect(EventSelector *es,
505 int fd,
506 struct sockaddr const *addr,
507 socklen_t addrlen,
508 EventTcpConnectFunc f,
509 int timeout,
510 void *data)
511{
512 int flags;
513 int n;
514 EventTcpConnectState *state;
515 struct timeval t;
516
517 /* Make sure socket is non-blocking */
518 flags = fcntl(fd, F_GETFL, 0);
519 if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
520 f(es, fd, EVENT_TCP_FLAG_IOERROR, data);
521 return;
522 }
523
524 n = connect(fd, addr, addrlen);
525 if (n < 0) {
526 if (errno != EINPROGRESS) {
527 f(es, fd, EVENT_TCP_FLAG_IOERROR, data);
528 return;
529 }
530 }
531
532 if (n == 0) { /* Connect succeeded immediately */
533 f(es, fd, EVENT_TCP_FLAG_COMPLETE, data);
534 return;
535 }
536
537 state = malloc(sizeof(*state));
538 if (!state) {
539 f(es, fd, EVENT_TCP_FLAG_IOERROR, data);
540 return;
541 }
542 state->f = f;
543 state->fd = fd;
544 state->data = data;
545
546 if (timeout <= 0) {
547 t.tv_sec = -1;
548 t.tv_usec = -1;
549 } else {
550 t.tv_sec = timeout;
551 t.tv_usec = 0;
552 }
553
554 state->conn = Event_AddHandlerWithTimeout(es, fd, EVENT_FLAG_WRITEABLE,
555 t, handle_connect,
556 (void *) state);
557 if (!state->conn) {
558 free(state);
559 f(es, fd, EVENT_TCP_FLAG_IOERROR, data);
560 return;
561 }
562}
563
564/**********************************************************************
565* %FUNCTION: EventTcp_CancelPending
566* %ARGUMENTS:
567* s -- an EventTcpState
568* %RETURNS:
569* Nothing
570* %DESCRIPTION:
571* Cancels the pending event handler
572***********************************************************************/
573void
574EventTcp_CancelPending(EventTcpState *s)
575{
576 free_state(s);
577}
578