From 23bb449f09e8044736e257cde80e07473216a30e Mon Sep 17 00:00:00 2001 From: patacongo Date: Sat, 22 Sep 2012 12:23:35 +0000 Subject: More webserver updates from Kate git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5173 42af7a65-404d-4744-a932-0658087f49c3 --- apps/netutils/webserver/httpd.c | 213 ++++++++++++++++++++++++++++++---------- 1 file changed, 161 insertions(+), 52 deletions(-) (limited to 'apps/netutils') diff --git a/apps/netutils/webserver/httpd.c b/apps/netutils/webserver/httpd.c index 29438c77a..ebcf300b6 100644 --- a/apps/netutils/webserver/httpd.c +++ b/apps/netutils/webserver/httpd.c @@ -118,8 +118,6 @@ * Private Data ****************************************************************************/ -static const char g_httpcmdget[] = "GET "; - /**************************************************************************** * Private Functions ****************************************************************************/ @@ -179,7 +177,7 @@ static void httpd_dumpbuffer(FAR const char *msg, FAR const char *buffer, unsign /* CONFIG_DEBUG, CONFIG_DEBUG_VERBOSE, and CONFIG_DEBUG_NET have to be * defined or the following does nothing. */ - + nvdbgdumpbuffer(msg, (FAR const uint8_t*)buffer, nbytes); } #else @@ -194,8 +192,10 @@ static void httpd_dumppstate(struct httpd_state *pstate, const char *msg) nvdbg(" filename: [%s]\n", pstate->ht_filename); nvdbg(" htfile len: %d\n", pstate->ht_file.len); nvdbg(" sockfd: %d\n", pstate->ht_sockfd); +#ifndef CONFIG_NETUTILS_HTTPD_SCRIPT_DISABLE nvdbg(" scriptptr: %p\n", pstate->ht_scriptptr); nvdbg(" scriptlen: %d\n", pstate->ht_scriptlen); +#endif nvdbg(" sndlen: %d\n", pstate->ht_sndlen); #endif } @@ -373,6 +373,11 @@ static int send_headers(struct httpd_state *pstate, int status, int len) (void) snprintf(cl, sizeof cl, "Content-Length: %d\r\n", len); } + if (status == 413) + { + /* TODO: here we "SHOULD" include a Retry-After header */ + } + i = snprintf(s, sizeof s, "HTTP/1.0 %d %s\r\n" #ifndef CONFIG_NETUTILS_HTTPD_SERVERHEADER_DISABLE @@ -498,73 +503,167 @@ done: return ret; } -static inline int httpd_cmd(struct httpd_state *pstate) +static inline int httpd_parse(struct httpd_state *pstate) { - ssize_t recvlen; - int i; + char *o; - /* Get the next HTTP command. We will handle only GET */ + enum + { + STATE_METHOD, + STATE_HEADER, + STATE_BODY + } state; - recvlen = recv(pstate->ht_sockfd, pstate->ht_buffer, HTTPD_IOBUFFER_SIZE, 0); -#if CONFIG_NETUTILS_HTTPD_TIMEOUT > 0 - if (recvlen < 0 && errno == EWOULDBLOCK) + state = STATE_METHOD; + o = pstate->ht_buffer; + + do { - ndbg("[%d] recv timeout\n"); - return httpd_senderror(pstate, 408); - } - else + char *start; + char *end; + + if (o == pstate->ht_buffer + sizeof pstate->ht_buffer) + { + ndbg("[%d] ht_buffer overflow\n"); + return 413; + } + + { + ssize_t r; + + r = recv(pstate->ht_sockfd, o, + sizeof pstate->ht_buffer - (o - pstate->ht_buffer), 0); + if (r == 0) + { + ndbg("[%d] connection lost\n", pstate->ht_sockfd); + return ERROR; + } + +#if CONFIG_NETUTILS_HTTPD_TIMEOUT > 0 + if (r == -1 && errno == EWOULDBLOCK) + { + ndbg("[%d] recv timeout\n"); + return 408; + } #endif - if (recvlen < 0) - { - ndbg("[%d] recv failed: %d\n", pstate->ht_sockfd, errno); - return httpd_senderror(pstate, 400); - } - else if (recvlen == 0) - { - ndbg("[%d] connection lost\n", pstate->ht_sockfd); - return httpd_senderror(pstate, 400); - } + if (r == -1) + { + ndbg("[%d] recv failed: %d\n", pstate->ht_sockfd, errno); + return 400; + } - httpd_dumpbuffer("Incoming buffer", pstate->ht_buffer, recvlen); + o += r; + } - /* We will handle only GET */ + /* Here o marks the end of the total block currently awaiting processing. + * There may be multiple lines in a block; next we deal with each in turn. + */ - if (strncmp(pstate->ht_buffer, g_httpcmdget, strlen(g_httpcmdget)) != 0) - { - ndbg("[%d] Unsupported command\n", pstate->ht_sockfd); - return httpd_senderror(pstate, 405); - } + for (start = pstate->ht_buffer; + (end = memchr(start, '\r', o - start)), end != NULL; + start = end) + { + *end = '\0'; + end++; - /* Get the name of the file to provide */ + /* Here start and end are a single line within the current block */ - if (pstate->ht_buffer[4] != ISO_slash) - { - ndbg("[%d] Missing path\n", pstate->ht_sockfd); - return httpd_senderror(pstate, 400); + httpd_dumpbuffer("Incoming HTTP line", start, end - start); + + if (*end != '\n') + { + ndbg("[%d] expected CRLF\n"); + return 400; + } + + end++; + + switch (state) + { + char *v; + + case STATE_METHOD: + if (0 != strncmp(start, "GET ", 4)) + { + ndbg("[%d] method not supported\n"); + return 501; + } + + start += 4; + v = start + strcspn(start, " "); + + if (0 != strcmp(v, " HTTP/1.0") && 0 != strcmp(v, " HTTP/1.1")) + { + ndbg("[%d] HTTP/%d.%d not supported\n", major, minor); + return 505; + } + + /* TODO: url decoding */ + + if (v - start >= sizeof pstate->ht_filename) + { + ndbg("[%d] ht_filename overflow\n"); + return 414; + } + + *v = '\0'; + (void) strcpy(pstate->ht_filename, start); + state = STATE_HEADER; + break; + + case STATE_HEADER: + if (*start == '\0') + { + state = STATE_BODY; + break; + } + + v = start + strcspn(start, ":"); + if (*v != '\0') + { + *v = '\0', v++; + v += strspn(v, ": "); + } + + if (*start == '\0' || *v == '\0') + { + ndbg("[%d] header parse error\n"); + return 400; + } + + nvdbg("[%d] Request header %s: %s\n", pstate->ht_sockfd, start, v); + + if (0 == strcasecmp(start, "Content-Length") && 0 != atoi(v)) + { + ndbg("[%d] non-zero request length\n"); + return 413; + } + + break; + + case STATE_BODY: + /* Not implemented */ + break; + } + } + + /* Shuffle down for the next block */ + + memmove(pstate->ht_buffer, start, o - start); + o -= start; } + while (state != STATE_BODY); + #if !defined(CONFIG_NETUTILS_HTTPD_SENDFILE) && !defined(CONFIG_NETUTILS_HTTPD_MMAP) - else if (pstate->ht_buffer[5] == ISO_space) + if (0 == strcmp(pstate->ht_filename, "/") { strncpy(pstate->ht_filename, "/" CONFIG_NETUTILS_HTTPD_INDEX, strlen("/" CONFIG_NETUTILS_HTTPD_INDEX)); } #endif - else - { - for (i = 0; - i < (HTTPD_MAX_FILENAME-1) && pstate->ht_buffer[i+4] != ISO_space; - i++) - { - pstate->ht_filename[i] = pstate->ht_buffer[i+4]; - } - - pstate->ht_filename[i]='\0'; - } nvdbg("[%d] Filename: %s\n", pstate->ht_sockfd, pstate->ht_filename); - /* Then send the file */ - - return httpd_sendfile(pstate); + return 200; } /**************************************************************************** @@ -589,6 +688,8 @@ static void *httpd_handler(void *arg) if (pstate) { + int status; + /* Re-initialize the thread state structure */ memset(pstate, 0, sizeof(struct httpd_state)); @@ -596,7 +697,15 @@ static void *httpd_handler(void *arg) /* Then handle the next httpd command */ - ret = httpd_cmd(pstate); + status = httpd_parse(pstate); + if (status >= 400) + { + ret = httpd_senderror(pstate, status); + } + else + { + ret = httpd_sendfile(pstate); + } /* End of command processing -- Clean up and exit */ -- cgit v1.2.3