summaryrefslogtreecommitdiff
path: root/nuttx/netutils/webclient/webclient.c
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx/netutils/webclient/webclient.c')
-rw-r--r--nuttx/netutils/webclient/webclient.c535
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;
+}