diff options
Diffstat (limited to 'nuttx/netutils/webclient/webclient.c')
-rw-r--r-- | nuttx/netutils/webclient/webclient.c | 535 |
1 files changed, 231 insertions, 304 deletions
diff --git a/nuttx/netutils/webclient/webclient.c b/nuttx/netutils/webclient/webclient.c index 3703aed9b..d00fd29b7 100644 --- a/nuttx/netutils/webclient/webclient.c +++ b/nuttx/netutils/webclient/webclient.c @@ -2,7 +2,7 @@ * netutils/webclient/webclient.c * Implementation of the HTTP client. * - * Copyright (C) 2007 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2009 Gregory Nutt. All rights reserved. * Author: Gregory Nutt <spudmonkey@racsa.co.cr> * * Based on uIP which also has a BSD style license: @@ -41,27 +41,29 @@ /* This example shows a HTTP client that is able to download web pages * and files from web servers. It requires a number of callback * functions to be implemented by the module that utilizes the code: - * webclient_datahandler(), webclient_connected(), - * webclient_timedout(), webclient_aborted(), webclient_closed(). + * webclient_datahandler(). */ /**************************************************************************** * Included Files ****************************************************************************/ +#include <nuttx/config.h> +#include <nuttx/compiler.h> + #include <sys/types.h> -#include <string.h> #include <sys/socket.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <debug.h> -#include <nuttx/compiler.h> +#include <netinet/in.h> #include <net/uip/uip.h> -#include <net/uip/resolv.h> - -#include <net/uip/uip-arch.h> #include <net/uip/uip-lib.h> - -#include "webclient.h" +#include <net/uip/resolv.h> +#include <net/uip/webclient.h> /**************************************************************************** * Definitions @@ -79,26 +81,58 @@ #define HTTPFLAG_MOVED 2 #define HTTPFLAG_ERROR 3 - #define ISO_nl 0x0a #define ISO_cr 0x0d #define ISO_space 0x20 /**************************************************************************** - * Private Data + * Private Types ****************************************************************************/ -static uint8 g_return; /* Kludge for now */ -static struct webclient_state s; +struct wget_s +{ + ubyte state; + ubyte httpflag; + + /* These describe the just-received buffer of data */ + + FAR char *buffer; /* user-provided buffer */ + int buflen; /* Length of the user provided buffer */ + int offset; /* Offset to the beginning of interesting data */ + int datend; /* Offset+1 to the last valid byte of data in the buffer */ + + /* Buffer HTTP header data and parse line at a time */ + + char line[CONFIG_NETUTILS_WEBCLIENT_MAXHTTPLINE]; + int ndx; + +#ifdef CONFIG_WEBCLIENT_GETMIMETYPE + char mimetype[CONFIG_NETUTILS_WEBCLIENT_MAXMIMESIZE]; +#endif +#ifdef CONFIG_WEBCLIENT_GETHOST + char host[CONFIG_NETUTILS_WEBCLIENT_MAXHOSTNAME]; +#endif +}; + + +/**************************************************************************** + * Private Data + ****************************************************************************/ static const char g_http10[] = "HTTP/1.0"; static const char g_http11[] = "HTTP/1.1"; +#ifdef CONFIG_WEBCLIENT_GETMIMETYPE static const char g_httpcontenttype[] = "content-type: "; +#endif static const char g_httphost[] = "host: "; +#ifdef CONFIG_WEBCLIENT_GETHOST static const char g_httplocation[] = "location: "; +#endif static const char g_httpget[] = "GET "; +#ifdef CONFIG_WEBCLIENT_GETHOST static const char g_httphttp[] = "http://"; +#endif static const char g_httpuseragentfields[] = "Connection: close\r\n" @@ -114,332 +148,177 @@ static const char g_httpcrnl[] = "\r\n"; * Private Functions ****************************************************************************/ -static void init_connection(void) -{ - s.state = WEBCLIENT_STATE_STATUSLINE; - - s.getrequestleft = strlen(g_httpget) - 1 + 1 + - strlen(g_http10) - 1 + - strlen(g_httpcrnl) - 1 + - strlen(g_httphost) - 1 + - strlen(g_httpcrnl) - 1 + - strlen(g_httpuseragentfields) + - strlen(s.file) + strlen(s.host); - s.getrequestptr = 0; - - s.httpheaderlineptr = 0; -} +/**************************************************************************** + * Private Functions + ****************************************************************************/ -static char *copy_string(char *dest, const char *src, int len) +/**************************************************************************** + * Name: wget_strcpy + ****************************************************************************/ + +static char *wget_strcpy(char *dest, const char *src) { - strncpy(dest, src, len); + int len = strlen(src); + memcpy(dest, src, len); return dest + len; } -static void senddata(struct uip_driver_s *dev, struct uip_conn *conn) -{ - uint16 len; - char *getrequest; - char *cptr; - - if (s.getrequestleft > 0) { - cptr = getrequest = (char *)dev->d_appdata; - - cptr = copy_string(cptr, g_httpget, strlen(g_httpget) - 1); - cptr = copy_string(cptr, s.file, strlen(s.file)); - *cptr++ = ISO_space; - cptr = copy_string(cptr, g_http10, strlen(g_http10) - 1); - - cptr = copy_string(cptr, g_httpcrnl, strlen(g_httpcrnl) - 1); - - cptr = copy_string(cptr, g_httphost, strlen(g_httphost) - 1); - cptr = copy_string(cptr, s.host, strlen(s.host)); - cptr = copy_string(cptr, g_httpcrnl, strlen(g_httpcrnl) - 1); - - cptr = copy_string(cptr, g_httpuseragentfields, - strlen(g_httpuseragentfields)); - - len = s.getrequestleft > uip_mss(conn)? - uip_mss(conn): - s.getrequestleft; - uip_send(dev, &(getrequest[s.getrequestptr]), len); - } -} +/**************************************************************************** + * Name: wget_parsestatus + ****************************************************************************/ -static void acked(struct uip_conn *conn) +static inline int wget_parsestatus(struct wget_s *ws) { - uint16 len; - - if (s.getrequestleft > 0) { - len = s.getrequestleft > uip_mss(conn)? - uip_mss(conn): - s.getrequestleft; - s.getrequestleft -= len; - s.getrequestptr += len; - } -} + int offset; + int ndx; + char *dest; -static uint16 parse_statusline(struct uip_driver_s *dev, uint16 len) -{ - char *cptr; + offset = ws->offset; + ndx = ws->ndx; - while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) + while (offset < ws->datend) { - char *pappdata = (char*)dev->d_appdata; - s.httpheaderline[s.httpheaderlineptr] = *pappdata++; - dev->d_appdata = (void*)pappdata; - len--; - - if (s.httpheaderline[s.httpheaderlineptr] == ISO_nl) + ws->line[ndx] = ws->buffer[offset]; + if (ws->line[ndx] == ISO_nl) { - if ((strncmp(s.httpheaderline, g_http10, strlen(g_http10) - 1) == 0) || - (strncmp(s.httpheaderline, g_http11, strlen(g_http11) - 1) == 0)) + ws->line[ndx] = '\0'; + if ((strncmp(ws->line, g_http10, strlen(g_http10)) == 0) || + (strncmp(ws->line, g_http11, strlen(g_http11)) == 0)) { - cptr = &(s.httpheaderline[9]); - s.httpflag = HTTPFLAG_NONE; - if (strncmp(cptr, g_http200, strlen(g_http200) - 1) == 0) + dest = &(ws->line[9]); + ws->httpflag = HTTPFLAG_NONE; + + /* Check for 200 OK */ + + if (strncmp(dest, g_http200, strlen(g_http200)) == 0) { - /* 200 OK */ - s.httpflag = HTTPFLAG_OK; + ws->httpflag = HTTPFLAG_OK; } - else if (strncmp(cptr, g_http301, strlen(g_http301) - 1) == 0 || - strncmp(cptr, g_http302, strlen(g_http302) - 1) == 0) - { - /* 301 Moved permanently or 302 Found. Location: header line - * will contain thw new location. - */ - s.httpflag = HTTPFLAG_MOVED; - } - else + /* Check for 301 Moved permanently or 302 Found. Location: header line + * will contain the new location. + */ + + else if (strncmp(dest, g_http301, strlen(g_http301)) == 0 || + strncmp(dest, g_http302, strlen(g_http302)) == 0) { - s.httpheaderline[s.httpheaderlineptr - 1] = 0; + + ws->httpflag = HTTPFLAG_MOVED; } } else { - g_return |= UIP_ABORT; - webclient_aborted(); - return 0; + return - ECONNABORTED; } - /* We're done parsing the status line, so we reset the pointer - * and start parsing the HTTP headers. - */ + /* We're done parsing the status line, so start parsing the HTTP headers. */ - s.httpheaderlineptr = 0; - s.state = WEBCLIENT_STATE_HEADERS; + ws->state = WEBCLIENT_STATE_HEADERS; break; } else { - ++s.httpheaderlineptr; + offset++; + ndx++; } } - return len; -} - -static char casecmp(char *str1, const char *str2, char len) -{ - static char c; - while(len > 0) { - c = *str1; - /* Force lower-case characters. */ - if (c & 0x40) { - c |= 0x20; - } - if (*str2 != c) { - return 1; - } - ++str1; - ++str2; - --len; - } - return 0; + ws->offset = offset; + ws->ndx = ndx; + return OK; } -static uint16 parse_headers(struct uip_driver_s *dev, uint16 len) +/**************************************************************************** + * Name: wget_parsestatus + ****************************************************************************/ + +static inline int wget_parseheaders(struct wget_s *ws) { - char *cptr; - static unsigned char i; + int offset; + int ndx; + char *dest; - while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) - { - char *pappdata = (char*)dev->d_appdata; - s.httpheaderline[s.httpheaderlineptr] = *pappdata++; - dev->d_appdata = (void*)pappdata; - len--; + offset = ws->offset; + ndx = ws->ndx; - if (s.httpheaderline[s.httpheaderlineptr] == ISO_nl) + while (offset < ws->datend) + { + ws->line[ndx] = ws->buffer[offset]; + if (ws->line[ndx] == ISO_nl) { - /* We have an entire HTTP header line in s.httpheaderline, so + /* We have an entire HTTP header line in s.line, so * we parse it. */ - if (s.httpheaderline[0] == ISO_cr) + if (ws->line[0] == ISO_cr) { /* This was the last header line (i.e., and empty "\r\n"), so * we are done with the headers and proceed with the actual * data. */ - s.state = WEBCLIENT_STATE_DATA; - return len; + ws->state = WEBCLIENT_STATE_DATA; + return OK; } - s.httpheaderline[s.httpheaderlineptr - 1] = 0; + ws->line[ndx] = '\0'; /* Check for specific HTTP header fields. */ - if (casecmp(s.httpheaderline, g_httpcontenttype, strlen(g_httpcontenttype) - 1) == 0) + +#ifdef CONFIG_WEBCLIENT_GETMIMETYPE + if (strncasecmp(ws->line, g_httpcontenttype, strlen(g_httpcontenttype)) == 0) { /* Found Content-type field. */ - cptr = strchr(s.httpheaderline, ';'); - if (cptr != NULL) + dest = strchr(ws->line, ';'); + if (dest != NULL) { - *cptr = 0; + *dest = 0; } - strncpy(s.mimetype, s.httpheaderline + strlen(g_httpcontenttype) - 1, sizeof(s.mimetype)); + strncpy(ws->mimetype, ws->line + strlen(g_httpcontenttype) - 1, sizeof(ws->mimetype)); } - else if (casecmp(s.httpheaderline, g_httplocation, strlen(g_httplocation) - 1) == 0) +# ifdef CONFIG_WEBCLIENT_GETHOST + else +# endif +#endif +#ifdef CONFIG_WEBCLIENT_GETHOST + if (strncasecmp(ws->line, g_httplocation, strlen(g_httplocation)) == 0) { - cptr = s.httpheaderline + strlen(g_httplocation) - 1; + dest = ws->line + strlen(g_httplocation) - 1; - if (strncmp(cptr, g_httphttp, strlen(g_httphttp)) == 0) + if (strncmp(dest, g_httphttp, strlen(g_httphttp)) == 0) { - cptr += 7; - for(i = 0; i < s.httpheaderlineptr - 7; ++i) + dest += 7; + for(i = 0; i < ws->ndx - 7; ++i) { - if (*cptr == 0 || *cptr == '/' || *cptr == ' ' || *cptr == ':') + if (*dest == 0 || *dest == '/' || *dest == ' ' || *dest == ':') { - s.host[i] = 0; + ws->host[i] = 0; break; } - s.host[i] = *cptr; - ++cptr; + ws->host[i] = *dest; + ++dest; } } - strncpy(s.file, cptr, sizeof(s.file)); + strncpy(ws->file, dest, sizeof(ws->file)); } +#endif /* We're done parsing, so we reset the pointer and start the * next line. */ - s.httpheaderlineptr = 0; + ndx = 0; } else { - ++s.httpheaderlineptr; + ndx++; + offset++; } } - return len; -} - -static void newdata(struct uip_driver_s *dev) -{ - uint16 len; - - len = dev->d_len; - - if (s.state == WEBCLIENT_STATE_STATUSLINE) { - len = parse_statusline(dev, len); - } - - if (s.state == WEBCLIENT_STATE_HEADERS && len > 0) { - len = parse_headers(dev, len); - } - if (len > 0 && s.state == WEBCLIENT_STATE_DATA && - s.httpflag != HTTPFLAG_MOVED) { - webclient_datahandler((char *)dev->d_appdata, len); - } -} - -/* This function is called by the UIP interrupt handling logic whenevent an - * event of interest occurs. - */ - -uint8 uip_interrupt_event(struct uip_driver_s *dev, struct uip_conn *conn, uint8 flags) -{ -#ifdef CONFIG_CPP_HAVE_WARNING -# warning OBSOLETE -- needs to be redesigned -#endif - g_return = flags; - - if ((flags & UIP_CONNECTED) != 0) - { - s.timer = 0; - s.state = WEBCLIENT_STATE_STATUSLINE; - senddata(dev, conn); - webclient_connected(); - return g_return; - } - - if (s.state == WEBCLIENT_STATE_CLOSE) - { - webclient_closed(); - return UIP_ABORT; - } - - if ((flags & UIP_ABORT) != 0) - { - webclient_aborted(); - } - - if ((flags & UIP_TIMEDOUT) != 0) - { - webclient_timedout(); - } - - if ((flags & UIP_ACKDATA) != 0) - { - s.timer = 0; - acked(conn); - } - - if ((flags & UIP_NEWDATA) != 0) - { - s.timer = 0; - newdata(dev); - } - - if ((flags & UIP_REXMIT) != 0 || (flags & UIP_NEWDATA) != 0 || (flags & UIP_ACKDATA) != 0) - { - senddata(dev, conn); - } - else if ((flags & UIP_POLL) != 0) - { - ++s.timer; - if (s.timer == WEBCLIENT_TIMEOUT) - { - webclient_timedout(); - return UIP_ABORT; - } - } - - if ((flags & UIP_CLOSE) != 0) - { - if (s.httpflag != HTTPFLAG_MOVED) - { - /* Send NULL data to signal EOF. */ - webclient_datahandler(NULL, 0); - } - else - { -#ifdef CONFIG_NET_IPv6 - struct sockaddr_in6 addr; -#else - struct sockaddr_in addr; -#endif - if (resolv_query(s.host, &addr) < 0) - { - return g_return; - } - webclient_get(s.host, s.port, s.file); - } - } - return g_return; + ws->offset = offset; + ws->ndx = ndx; + return OK; } /**************************************************************************** @@ -447,22 +326,19 @@ uint8 uip_interrupt_event(struct uip_driver_s *dev, struct uip_conn *conn, uint8 ****************************************************************************/ /**************************************************************************** - * Name: webclient_init + * Name: wget ****************************************************************************/ -void webclient_init(void) -{ -} - -/**************************************************************************** - * Name: webclient_get - ****************************************************************************/ - -unsigned char webclient_get(FAR const char *host, uint16 port, FAR char *file) +int wget(FAR const char *host, uint16 port, FAR const char *file, + FAR char *buffer, int buflen, wget_callback_t callback) { static uip_ipaddr_t addr; struct sockaddr_in server; + struct wget_s ws; + char *dest; int sockfd; + int len; + int ret = OK; /* First check if the host is an IP address. */ @@ -487,6 +363,9 @@ unsigned char webclient_get(FAR const char *host, uint16 port, FAR char *file) sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { + /* socket failed. It will set the errno appropriately */ + + ndbg("socket failed: %d\n", errno); return ERROR; } @@ -497,43 +376,91 @@ unsigned char webclient_get(FAR const char *host, uint16 port, FAR char *file) server.sin_family = AF_INET; memcpy(&server.sin_addr.s_addr, &host, sizeof(in_addr_t)); - server.sin_port = htons(port); + server.sin_port = htons(port); - if (connect(sockfd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) + ret = connect(sockfd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)); + if (ret < 0) { - return ERROR; + ndbg("connect failed: %d\n", errno); + goto errout; } - s.port = port; - strncpy(s.file, file, sizeof(s.file)); - strncpy(s.host, host, sizeof(s.host)); + /* Send the GET request */ + + dest = (char *)buffer; + dest = wget_strcpy(dest, g_httpget); + dest = wget_strcpy(dest, file); + *dest++ = ISO_space; + dest = wget_strcpy(dest, g_http10); + dest = wget_strcpy(dest, g_httpcrnl); + dest = wget_strcpy(dest, g_httphost); + dest = wget_strcpy(dest, host); + dest = wget_strcpy(dest, g_httpcrnl); + dest = wget_strcpy(dest, g_httpuseragentfields); + len = dest - buffer; + + ret = send(sockfd, buffer, len, 0); + if (ret < 0) + { + ndbg("send failed: %d\n", errno); + goto errout; + } - init_connection(); - return OK; -} + /* Now get the response */ -void webclient_close(void) -{ - s.state = WEBCLIENT_STATE_CLOSE; -} + memset(&ws, 0, sizeof(struct wget_s)); + ws.state = WEBCLIENT_STATE_STATUSLINE; + ws.buffer = buffer; + ws.buflen = buflen; + + for (;;) + { + ws.datend = recv(sockfd, ws.buffer, ws.buflen, 0); + if (ws.datend < 0) + { + ndbg("recv failed: %d\n", errno); + ret = ws.datend; + goto errout; + } + else if (ret == 0) + { + break; + } + + ws.offset = 0; + if (ws.state == WEBCLIENT_STATE_STATUSLINE) + { + ret = wget_parsestatus(&ws); + if (ret < 0) + { + goto errout_with_errno; + } + } -char *webclient_mimetype(void) -{ - return s.mimetype; -} + if (ws.state == WEBCLIENT_STATE_HEADERS) + { + ret = wget_parseheaders(&ws); + if (ret < 0) + { + goto errout_with_errno; + } + } -char *webclient_filename(void) -{ - return s.file; -} + /* Let the client decide what to do with the received file */ -char *webclient_hostname(void) -{ - return s.host; -} + if (ws.state == WEBCLIENT_STATE_DATA && ws.httpflag != HTTPFLAG_MOVED) + { + callback(&ws.buffer, ws.offset, ws.datend, &buflen); + } + } -unsigned shortwebclient_port(void) -{ - return s.port; -} +okout: + close(sockfd); + return OK; +errout_with_errno: + errno = -ret; +errout: + close(sockfd); + return ERROR; +} |