From 3ec3bfdcc88d14c062dfe3bd99a54daa307e8f7f Mon Sep 17 00:00:00 2001 From: patacongo Date: Sun, 2 Aug 2009 23:35:27 +0000 Subject: [PATCH] accept() now supports non-blocking operations git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@2011 42af7a65-404d-4744-a932-0658087f49c3 --- ChangeLog | 7 ++- Documentation/NuttX.html | 7 ++- net/accept.c | 12 ++++ net/recvfrom.c | 4 +- netutils/thttpd/config.h | 10 ++++ netutils/thttpd/libhttpd.c | 101 ++++++++++++++++----------------- netutils/thttpd/thttpd.c | 112 +++++++++++++++++++++---------------- 7 files changed, 147 insertions(+), 106 deletions(-) diff --git a/ChangeLog b/ChangeLog index 72c1f89e7b..f26fecc8cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -828,9 +828,10 @@ * configs/ntosd-dm320/thttpd: A build configuration for THTTPD on the Neuros DM320 platform. * lib/: Added strstr() and strpbrk(). - * net/recvfrom.c: Sockets now support some non-blocking operations -- - specifically only for TCP/IP read operations when read-ahead buffering - is enabled. + * net/recvfrom.c and net/accept(): Sockets now support some non-blocking + operations, specifically for (1) TCP/IP read operations when read-ahead + buffering is enabled, and (2) TCP/IP accept() operations when TCP/IP + connection backlog is enabled. * fs/fs_fcntl.c and net/net_vfcntl.c: Minimal support provided for fcntl(). It can, at least, be used to mark sockets as blocking or non-blocking. * net/net_close.c: Fix bug in close(). If reference count not set to zero diff --git a/Documentation/NuttX.html b/Documentation/NuttX.html index 53b35cbfa8..fb2ec50676 100644 --- a/Documentation/NuttX.html +++ b/Documentation/NuttX.html @@ -1501,9 +1501,10 @@ nuttx-0.4.10 2009-xx-xx Gregory Nutt <spudmonkey@racsa.co.cr> * configs/ntosd-dm320/thttpd: A build configuration for THTTPD on the Neuros DM320 platform. * lib/: Added strstr() and strpbrk(). - * net/recvfrom.c: Sockets now support some non-blocking operations -- - specifically only for TCP/IP read operations when read-ahead buffering - is enabled. + * net/recvfrom.c and net/accept(): Sockets now support some non-blocking + operations, specifically for (1) TCP/IP read operations when read-ahead + buffering is enabled, and (2) TCP/IP accept() operations when TCP/IP + connection backlog is enabled. * fs/fs_fcntl.c and net/net_vfcntl.c: Minimal support provided for fcntl(). It can, at least, be used to mark sockets as blocking or non-blocking. * net/net_close.c: Fix bug in close(). If reference count not set to zero diff --git a/net/accept.c b/net/accept.c index 81ac8dbfff..28ea7e237b 100644 --- a/net/accept.c +++ b/net/accept.c @@ -356,6 +356,18 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) nvdbg("Pending conn=%p\n", state.acpt_newconn); accept_tcpsender(state.acpt_newconn, inaddr); } + + /* In general, this uIP-based implementation will not support non-blocking + * socket operations... except in a few cases: Here for TCP accept with backlog + * enabled. If this socket is configured as non-blocking then return EAGAIN + * if there is no pending connection in the backlog. + */ + + else if (_SS_ISNONBLOCK(psock->s_flags)) + { + err = EAGAIN; + goto errout_with_irq; + } else #endif { diff --git a/net/recvfrom.c b/net/recvfrom.c index 345ec380a7..e5fc27ac6a 100644 --- a/net/recvfrom.c +++ b/net/recvfrom.c @@ -882,8 +882,8 @@ static ssize_t tcp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, recvfrom_readahead(&state); - /* In general, this uI-based implementation will not support non-blocking - * socket operations... except in this one case: TCP receive with read-ahead + /* In general, this uIP-based implementation will not support non-blocking + * socket operations... except in a few cases: Here for TCP receive with read-ahead * enabled. If this socket is configured as non-blocking then return EAGAIN * if no data was obtained from the read-ahead buffers. */ diff --git a/netutils/thttpd/config.h b/netutils/thttpd/config.h index 63b76a06db..2fe70539a8 100644 --- a/netutils/thttpd/config.h +++ b/netutils/thttpd/config.h @@ -131,10 +131,20 @@ # define CONFIG_THTTPD_CHARSET "iso-8859-1" # endif +/* Initial buffer size, buffer reallocation increment, maximum buffer size */ + # ifndef CONFIG_THTTPD_IOBUFFERSIZE # define CONFIG_THTTPD_IOBUFFERSIZE 256 # endif +# ifndef CONFIG_THTTPD_REALLOCINCR +# define CONFIG_THTTPD_REALLOCINCR 128 +# endif + +# ifndef CONFIG_THTTPD_MAXREALLOC +# define CONFIG_THTTPD_MAXREALLOC 4096 +# endif + # if CONFIG_THTTPD_IOBUFFERSIZE > 65535 # error "Can't use uint16 for buffer" # endif diff --git a/netutils/thttpd/libhttpd.c b/netutils/thttpd/libhttpd.c index fa294f3925..68e1e5c489 100644 --- a/netutils/thttpd/libhttpd.c +++ b/netutils/thttpd/libhttpd.c @@ -3469,7 +3469,7 @@ void httpd_realloc_str(char **strP, size_t * maxsizeP, size_t size) { if (*maxsizeP == 0) { - *maxsizeP = MAX(200, size + 100); + *maxsizeP = MAX(CONFIG_THTTPD_IOBUFFERSIZE, size + CONFIG_THTTPD_REALLOCINCR); *strP = NEW(char, *maxsizeP + 1); ++str_alloc_count; str_alloc_size += *maxsizeP; @@ -3559,7 +3559,7 @@ int httpd_get_conn(httpd_server *hs, int listen_fd, httpd_conn *hc) if (!hc->initialized) { hc->read_size = 0; - httpd_realloc_str(&hc->read_buf, &hc->read_size, 500); + httpd_realloc_str(&hc->read_buf, &hc->read_size, CONFIG_THTTPD_IOBUFFERSIZE); hc->maxdecodedurl = hc->maxorigfilename = hc->maxexpnfilename = hc->maxencodings = hc->maxpathinfo = hc->maxquery = hc->maxaccept = @@ -3567,7 +3567,7 @@ int httpd_get_conn(httpd_server *hs, int listen_fd, httpd_conn *hc) hc->maxremoteuser = 0; #ifdef CONFIG_THTTPD_TILDE_MAP2 hc->maxaltdir = 0; -#endif /*CONFIG_THTTPD_TILDE_MAP2 */ +#endif httpd_realloc_str(&hc->decodedurl, &hc->maxdecodedurl, 1); httpd_realloc_str(&hc->origfilename, &hc->maxorigfilename, 1); httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, 0); @@ -3587,6 +3587,7 @@ int httpd_get_conn(httpd_server *hs, int listen_fd, httpd_conn *hc) /* Accept the new connection. */ + nvdbg("accept() new connection on listen_fd %d\n", listen_fd); sz = sizeof(sa); hc->conn_fd = accept(listen_fd, (struct sockaddr*)&sa, &sz); if (hc->conn_fd < 0) @@ -3613,54 +3614,56 @@ int httpd_get_conn(httpd_server *hs, int listen_fd, httpd_conn *hc) hc->hs = hs; (void)memset(&hc->client_addr, 0, sizeof(hc->client_addr)); (void)memmove(&hc->client_addr, &sa, sockaddr_len(&sa)); - hc->read_idx = 0; - hc->checked_idx = 0; - hc->checked_state = CHST_FIRSTWORD; - hc->method = METHOD_UNKNOWN; - hc->status = 0; - hc->bytes_to_send = 0; - hc->bytes_sent = 0; - hc->encodedurl = ""; - hc->decodedurl[0] = '\0'; - hc->protocol = "UNKNOWN"; - hc->origfilename[0] = '\0'; - hc->expnfilename[0] = '\0'; - hc->encodings[0] = '\0'; - hc->pathinfo[0] = '\0'; - hc->query[0] = '\0'; - hc->referer = ""; - hc->useragent = ""; - hc->accept[0] = '\0'; - hc->accepte[0] = '\0'; - hc->acceptl = ""; - hc->cookie = ""; - hc->contenttype = ""; - hc->reqhost[0] = '\0'; - hc->hdrhost = ""; - hc->hostdir[0] = '\0'; - hc->authorization = ""; - hc->remoteuser[0] = '\0'; - hc->buffer[0] = '\0'; + hc->read_idx = 0; + hc->checked_idx = 0; + hc->checked_state = CHST_FIRSTWORD; + hc->method = METHOD_UNKNOWN; + hc->status = 0; + hc->bytes_to_send = 0; + hc->bytes_sent = 0; + hc->encodedurl = ""; + hc->decodedurl[0] = '\0'; + hc->protocol = "UNKNOWN"; + hc->origfilename[0] = '\0'; + hc->expnfilename[0] = '\0'; + hc->encodings[0] = '\0'; + hc->pathinfo[0] = '\0'; + hc->query[0] = '\0'; + hc->referer = ""; + hc->useragent = ""; + hc->accept[0] = '\0'; + hc->accepte[0] = '\0'; + hc->acceptl = ""; + hc->cookie = ""; + hc->contenttype = ""; + hc->reqhost[0] = '\0'; + hc->hdrhost = ""; + hc->hostdir[0] = '\0'; + hc->authorization = ""; + hc->remoteuser[0] = '\0'; + hc->buffer[0] = '\0'; #ifdef CONFIG_THTTPD_TILDE_MAP2 - hc->altdir[0] = '\0'; + hc->altdir[0] = '\0'; #endif hc->buflen = 0; hc->if_modified_since = (time_t) - 1; - hc->range_if = (time_t) - 1; - hc->contentlength = -1; + hc->range_if = (time_t)-1; + hc->contentlength = -1; hc->type = ""; #ifdef CONFIG_THTTPD_VHOST - hc->vhostname = NULL; + hc->vhostname = NULL; #endif - hc->mime_flag = TRUE; - hc->one_one = FALSE; - hc->got_range = FALSE; - hc->tildemapped = FALSE; - hc->range_start = 0; - hc->range_end = -1; - hc->keep_alive = FALSE; - hc->should_linger = FALSE; - hc->file_fd = -1; + hc->mime_flag = TRUE; + hc->one_one = FALSE; + hc->got_range = FALSE; + hc->tildemapped = FALSE; + hc->range_start = 0; + hc->range_end = -1; + hc->keep_alive = FALSE; + hc->should_linger = FALSE; + hc->file_fd = -1; + + nvdbg("New connection accepted on %d\n", hc->conn_fd); return GC_OK; } @@ -4067,14 +4070,13 @@ int httpd_parse_request(httpd_conn *hc) cp += strspn(cp, " \t"); if (hc->accept[0] != '\0') { - if (strlen(hc->accept) > 5000) + if (strlen(hc->accept) > CONFIG_THTTPD_MAXREALLOC) { ndbg("%s way too much Accept: data\n", httpd_ntoa(&hc->client_addr)); continue; } - httpd_realloc_str(&hc->accept, &hc->maxaccept, - strlen(hc->accept) + 2 + strlen(cp)); + httpd_realloc_str(&hc->accept, &hc->maxaccept, strlen(hc->accept) + 2 + strlen(cp)); (void)strcat(hc->accept, ", "); } else @@ -4089,14 +4091,13 @@ int httpd_parse_request(httpd_conn *hc) cp += strspn(cp, " \t"); if (hc->accepte[0] != '\0') { - if (strlen(hc->accepte) > 5000) + if (strlen(hc->accepte) > CONFIG_THTTPD_MAXREALLOC) { ndbg("%s way too much Accept-Encoding: data\n", httpd_ntoa(&hc->client_addr)); continue; } - httpd_realloc_str(&hc->accepte, &hc->maxaccepte, - strlen(hc->accepte) + 2 + strlen(cp)); + httpd_realloc_str(&hc->accepte, &hc->maxaccepte, strlen(hc->accepte) + 2 + strlen(cp)); (void)strcat(hc->accepte, ", "); } else diff --git a/netutils/thttpd/thttpd.c b/netutils/thttpd/thttpd.c index 259f88e1b0..d89d6dd06a 100644 --- a/netutils/thttpd/thttpd.c +++ b/netutils/thttpd/thttpd.c @@ -195,37 +195,42 @@ static int handle_newconnect(struct timeval *tv, int listen_fd) struct connect_s *conn; ClientData client_data; - /* This loops until the accept() fails, trying to start new connections as - * fast as possible so we don't overrun the listen queue. + /* This loops until the accept() fails, trying to start new connections as + * fast as possible so we don't overrun the listen queue. */ + nvdbg("New connection(s) on listen_fd %d\n", listen_fd); for (;;) { /* Is there room in the connection table? */ + if (num_connects >= AVAILABLE_FDS) { /* Out of connection slots. Run the timers, then the existing * connections, and maybe we'll free up a slot by the time we get - * back here. */ + * back here. + */ + ndbg("too many connections!\n"); tmr_run(tv); - return 0; + return -1; } /* Get the first free connection entry off the free list */ +#ifdef CONFIG_DEBUG if (first_free_connect == -1 || connects[first_free_connect].conn_state != CNST_FREE) { ndbg("the connects free list is messed up\n"); exit(1); } - +#endif conn = &connects[first_free_connect]; /* Make the httpd_conn if necessary */ - if (conn->hc == (httpd_conn *) 0) + if (!conn->hc) { conn->hc = NEW(httpd_conn, 1); if (conn->hc == (httpd_conn *) 0) @@ -233,8 +238,9 @@ static int handle_newconnect(struct timeval *tv, int listen_fd) ndbg("out of memory allocating an httpd_conn\n"); exit(1); } + conn->hc->initialized = 0; - ++httpd_conn_count; + httpd_conn_count++; } /* Get the connection */ @@ -247,37 +253,43 @@ static int handle_newconnect(struct timeval *tv, int listen_fd) case GC_FAIL: tmr_run(tv); - return 0; + return -1; /* No more connections to accept for now */ case GC_NO_MORE: - return 1; + return 0; + + default: + break; } + nvdbg("New connection fd %d\n", conn->hc->conn_fd); + conn->conn_state = CNST_READING; /* Pop it off the free list */ - first_free_connect = conn->next_free_connect; + first_free_connect = conn->next_free_connect; conn->next_free_connect = -1; - ++num_connects; - client_data.p = conn; - conn->active_at = tv->tv_sec; - conn->wakeup_timer = (Timer *) 0; - conn->linger_timer = (Timer *) 0; - conn->offset = 0; + num_connects++; + client_data.p = conn; + conn->active_at = tv->tv_sec; + conn->wakeup_timer = NULL; + conn->linger_timer = NULL; + conn->offset = 0; /* Set the connection file descriptor to no-delay mode */ httpd_set_ndelay(conn->hc->conn_fd); - fdwatch_add_fd(fw, conn->hc->conn_fd, conn, FDW_READ); #if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET) ++stats_connections; if (num_connects > stats_simultaneous) - stats_simultaneous = num_connects; + { + stats_simultaneous = num_connects; + } #endif } } @@ -293,11 +305,11 @@ static void handle_read(struct connect_s *conn, struct timeval *tv) if (hc->read_idx >= hc->read_size) { - if (hc->read_size > 5000) + if (hc->read_size > CONFIG_THTTPD_MAXREALLOC) { goto errout_with_400; } - httpd_realloc_str(&hc->read_buf, &hc->read_size, hc->read_size + 1000); + httpd_realloc_str(&hc->read_buf, &hc->read_size, hc->read_size + CONFIG_THTTPD_REALLOCINCR); } /* Read some more bytes */ @@ -880,7 +892,12 @@ int thttpd_main(int argc, char **argv) if (num_ready < 0) { if (errno == EINTR || errno == EAGAIN) - continue; /* try again */ + { + /* Not errors... try again */ + + continue; + } + ndbg("fdwatch: %d\n", errno); exit(1); } @@ -897,10 +914,9 @@ int thttpd_main(int argc, char **argv) /* Is it a new connection? */ - if (hs != (httpd_server *) 0 && hs->listen_fd != -1 && - fdwatch_check_fd(fw, hs->listen_fd)) + if (fdwatch_check_fd(fw, hs->listen_fd)) { - if (handle_newconnect(&tv, hs->listen_fd)) + if (!handle_newconnect(&tv, hs->listen_fd)) { /* Go around the loop and do another fdwatch, rather than * dropping through and processing existing connections. New @@ -913,33 +929,33 @@ int thttpd_main(int argc, char **argv) /* Find the connections that need servicing */ - while ((conn = - (struct connect_s*)fdwatch_get_next_client_data(fw)) != - (struct connect_s*)- 1) + while ((conn = (struct connect_s*)fdwatch_get_next_client_data(fw)) != (struct connect_s*)-1) { - if (conn == (struct connect_s *) 0) - continue; - - hc = conn->hc; - if (!fdwatch_check_fd(fw, hc->conn_fd)) + if (conn) { - /* Something went wrong */ - - clear_connection(conn, &tv); - } - else - { - switch (conn->conn_state) + hc = conn->hc; + if (!fdwatch_check_fd(fw, hc->conn_fd)) { - case CNST_READING: - handle_read(conn, &tv); - break; - case CNST_SENDING: - handle_send(conn, &tv); - break; - case CNST_LINGERING: - handle_linger(conn, &tv); - break; + /* Something went wrong */ + + nvdbg("Clearing connection\n"); + clear_connection(conn, &tv); + } + else + { + nvdbg("Handle conn_state %d\n", conn->conn_state); + switch (conn->conn_state) + { + case CNST_READING: + handle_read(conn, &tv); + break; + case CNST_SENDING: + handle_send(conn, &tv); + break; + case CNST_LINGERING: + handle_linger(conn, &tv); + break; + } } } }