# apps/webclient/webclient.c

Go to the documentation of this file.

    00001 /**
    00002  * \addtogroup apps
    00003  * @{
    00004  */
    00005
    00006 /**
    00007  * \defgroup webclient Web client
    00008  * @{
    00009  *
    00010  * This example shows a HTTP client that is able to download web pages
    00011  * and files from web servers. It requires a number of callback
    00012  * functions to be implemented by the module that utilizes the code:
    00013  * webclient_datahandler(), webclient_connected(),
    00014  * webclient_timedout(), webclient_aborted(), webclient_closed().
    00015  */
    00016
    00017 /**
    00018  * \file
    00019  * Implementation of the HTTP client.
    00020  * \author Adam Dunkels <adam@dunkels.com>
    00021  */
    00022
    00023 /*
    00024  * Copyright (c) 2002, Adam Dunkels.
    00025  * All rights reserved.
    00026  *
    00027  * Redistribution and use in source and binary forms, with or without
    00028  * modification, are permitted provided that the following conditions
    00029  * are met:
    00030  * 1. Redistributions of source code must retain the above copyright
    00031  *    notice, this list of conditions and the following disclaimer.
    00032  * 2. Redistributions in binary form must reproduce the above
    00033  *    copyright notice, this list of conditions and the following
    00034  *    disclaimer in the documentation and/or other materials provided
    00035  *    with the distribution.
    00036  * 3. The name of the author may not be used to endorse or promote
    00037  *    products derived from this software without specific prior
    00038  *    written permission.
    00039  *
    00040  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
    00041  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    00042  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    00043  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
    00044  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    00045  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
    00046  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    00047  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
    00048  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    00049  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    00050  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    00051  *
    00052  * This file is part of the uIP TCP/IP stack.
    00053  *
    00054  * $Id: webclient.c,v 1.2 2006/06/11 21:46:37 adam Exp $
    00055  *
    00056  */
    00057
    00058 #include "uip.h"
    00059 #include "uiplib.h"
    00060 #include "webclient.h"
    00061 #include "resolv.h"
    00062
    00063 #include <string.h>
    00064
    00065 #define WEBCLIENT_TIMEOUT 100
    00066
    00067 #define WEBCLIENT_STATE_STATUSLINE 0
    00068 #define WEBCLIENT_STATE_HEADERS    1
    00069 #define WEBCLIENT_STATE_DATA       2
    00070 #define WEBCLIENT_STATE_CLOSE      3
    00071
    00072 #define HTTPFLAG_NONE   0
    00073 #define HTTPFLAG_OK     1
    00074 #define HTTPFLAG_MOVED  2
    00075 #define HTTPFLAG_ERROR  3
    00076
    00077
    00078 #define ISO_nl       0x0a
    00079 #define ISO_cr       0x0d
    00080 #define ISO_space    0x20
    00081
    00082
    00083 static struct webclient_state s;
    00084
    00085 /*-----------------------------------------------------------------------------------*/
    00086 char *
    00087 webclient_mimetype(void)
    00088 {
    00089   return s.mimetype;
    00090 }
    00091 /*-----------------------------------------------------------------------------------*/
    00092 char *
    00093 webclient_filename(void)
    00094 {
    00095   return s.file;
    00096 }
    00097 /*-----------------------------------------------------------------------------------*/
    00098 char *
    00099 webclient_hostname(void)
    00100 {
    00101   return s.host;
    00102 }
    00103 /*-----------------------------------------------------------------------------------*/
    00104 unsigned short
    00105 webclient_port(void)
    00106 {
    00107   return s.port;
    00108 }
    00109 /*-----------------------------------------------------------------------------------*/
    00110 void
    00111 webclient_init(void)
    00112 {
    00113
    00114 }
    00115 /*-----------------------------------------------------------------------------------*/
    00116 static void
    00117 init_connection(void)
    00118 {
    00119   s.state = WEBCLIENT_STATE_STATUSLINE;
    00120
    00121   s.getrequestleft = sizeof(http_get) - 1 + 1 +
    00122     sizeof(http_10) - 1 +
    00123     sizeof(http_crnl) - 1 +
    00124     sizeof(http_host) - 1 +
    00125     sizeof(http_crnl) - 1 +
    00126     strlen(http_user_agent_fields) +
    00127     strlen(s.file) + strlen(s.host);
    00128   s.getrequestptr = 0;
    00129
    00130   s.httpheaderlineptr = 0;
    00131 }
    00132 /*-----------------------------------------------------------------------------------*/
    00133 void
    00134 webclient_close(void)
    00135 {
    00136   s.state = WEBCLIENT_STATE_CLOSE;
    00137 }
    00138 /*-----------------------------------------------------------------------------------*/
    00139 unsigned char
    00140 webclient_get(char *host, u16_t port, char *file)
    00141 {
    00142   struct uip_conn *conn;
    00143   uip_ipaddr_t *ipaddr;
    00144   static uip_ipaddr_t addr;
    00145
    00146   /* First check if the host is an IP address. */
    00147   ipaddr = &addr;
    00148   if(uiplib_ipaddrconv(host, (unsigned char *)addr) == 0) {
    00149     ipaddr = (uip_ipaddr_t *)resolv_lookup(host);
    00150
    00151     if(ipaddr == NULL) {
    00152       return 0;
    00153     }
    00154   }
    00155
    00156   conn = uip_connect(ipaddr, htons(port));
    00157
    00158   if(conn == NULL) {
    00159     return 0;
    00160   }
    00161
    00162   s.port = port;
    00163   strncpy(s.file, file, sizeof(s.file));
    00164   strncpy(s.host, host, sizeof(s.host));
    00165
    00166   init_connection();
    00167   return 1;
    00168 }
    00169 /*-----------------------------------------------------------------------------------*/
    00170 static unsigned char *
    00171 copy_string(unsigned char *dest,
    00172             const unsigned char *src, unsigned char len)
    00173 {
    00174   strncpy(dest, src, len);
    00175   return dest + len;
    00176 }
    00177 /*-----------------------------------------------------------------------------------*/
    00178 static void
    00179 senddata(void)
    00180 {
    00181   u16_t len;
    00182   char *getrequest;
    00183   char *cptr;
    00184
    00185   if(s.getrequestleft > 0) {
    00186     cptr = getrequest = (char *)uip_appdata;
    00187
    00188     cptr = copy_string(cptr, http_get, sizeof(http_get) - 1);
    00189     cptr = copy_string(cptr, s.file, strlen(s.file));
    00190     *cptr++ = ISO_space;
    00191     cptr = copy_string(cptr, http_10, sizeof(http_10) - 1);
    00192
    00193     cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1);
    00194
    00195     cptr = copy_string(cptr, http_host, sizeof(http_host) - 1);
    00196     cptr = copy_string(cptr, s.host, strlen(s.host));
    00197     cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1);
    00198
    00199     cptr = copy_string(cptr, http_user_agent_fields,
    00200                        strlen(http_user_agent_fields));
    00201
    00202     len = s.getrequestleft > uip_mss()?
    00203       uip_mss():
    00204       s.getrequestleft;
    00205     uip_send(&(getrequest[s.getrequestptr]), len);
    00206   }
    00207 }
    00208 /*-----------------------------------------------------------------------------------*/
    00209 static void
    00210 acked(void)
    00211 {
    00212   u16_t len;
    00213
    00214   if(s.getrequestleft > 0) {
    00215     len = s.getrequestleft > uip_mss()?
    00216       uip_mss():
    00217       s.getrequestleft;
    00218     s.getrequestleft -= len;
    00219     s.getrequestptr += len;
    00220   }
    00221 }
    00222 /*-----------------------------------------------------------------------------------*/
    00223 static u16_t
    00224 parse_statusline(u16_t len)
    00225 {
    00226   char *cptr;
    00227
    00228   while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
    00229     s.httpheaderline[s.httpheaderlineptr] = *(char *)uip_appdata;
    00230     ++((char *)uip_appdata);
    00231     --len;
    00232     if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
    00233
    00234       if((strncmp(s.httpheaderline, http_10,
    00235                   sizeof(http_10) - 1) == 0) ||
    00236          (strncmp(s.httpheaderline, http_11,
    00237                   sizeof(http_11) - 1) == 0)) {
    00238         cptr = &(s.httpheaderline[9]);
    00239         s.httpflag = HTTPFLAG_NONE;
    00240         if(strncmp(cptr, http_200, sizeof(http_200) - 1) == 0) {
    00241           /* 200 OK */
    00242           s.httpflag = HTTPFLAG_OK;
    00243         } else if(strncmp(cptr, http_301, sizeof(http_301) - 1) == 0 ||
    00244                   strncmp(cptr, http_302, sizeof(http_302) - 1) == 0) {
    00245           /* 301 Moved permanently or 302 Found. Location: header line
    00246              will contain thw new location. */
    00247           s.httpflag = HTTPFLAG_MOVED;
    00248         } else {
    00249           s.httpheaderline[s.httpheaderlineptr - 1] = 0;
    00250         }
    00251       } else {
    00252         uip_abort();
    00253         webclient_aborted();
    00254         return 0;
    00255       }
    00256
    00257       /* We're done parsing the status line, so we reset the pointer
    00258          and start parsing the HTTP headers.*/
    00259       s.httpheaderlineptr = 0;
    00260       s.state = WEBCLIENT_STATE_HEADERS;
    00261       break;
    00262     } else {
    00263       ++s.httpheaderlineptr;
    00264     }
    00265   }
    00266   return len;
    00267 }
    00268 /*-----------------------------------------------------------------------------------*/
    00269 static char
    00270 casecmp(char *str1, const char *str2, char len)
    00271 {
    00272   static char c;
    00273
    00274   while(len > 0) {
    00275     c = *str1;
    00276     /* Force lower-case characters. */
    00277     if(c & 0x40) {
    00278       c |= 0x20;
    00279     }
    00280     if(*str2 != c) {
    00281       return 1;
    00282     }
    00283     ++str1;
    00284     ++str2;
    00285     --len;
    00286   }
    00287   return 0;
    00288 }
    00289 /*-----------------------------------------------------------------------------------*/
    00290 static u16_t
    00291 parse_headers(u16_t len)
    00292 {
    00293   char *cptr;
    00294   static unsigned char i;
    00295
    00296   while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
    00297     s.httpheaderline[s.httpheaderlineptr] = *(char *)uip_appdata;
    00298     ++((char *)uip_appdata);
    00299     --len;
    00300     if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
    00301       /* We have an entire HTTP header line in s.httpheaderline, so
    00302          we parse it. */
    00303       if(s.httpheaderline[0] == ISO_cr) {
    00304         /* This was the last header line (i.e., and empty "\r\n"), so
    00305            we are done with the headers and proceed with the actual
    00306            data. */
    00307         s.state = WEBCLIENT_STATE_DATA;
    00308         return len;
    00309       }
    00310
    00311       s.httpheaderline[s.httpheaderlineptr - 1] = 0;
    00312       /* Check for specific HTTP header fields. */
    00313       if(casecmp(s.httpheaderline, http_content_type,
    00314                      sizeof(http_content_type) - 1) == 0) {
    00315         /* Found Content-type field. */
    00316         cptr = strchr(s.httpheaderline, ';');
    00317         if(cptr != NULL) {
    00318           *cptr = 0;
    00319         }
    00320         strncpy(s.mimetype, s.httpheaderline +
    00321                 sizeof(http_content_type) - 1, sizeof(s.mimetype));
    00322       } else if(casecmp(s.httpheaderline, http_location,
    00323                             sizeof(http_location) - 1) == 0) {
    00324         cptr = s.httpheaderline +
    00325           sizeof(http_location) - 1;
    00326
    00327         if(strncmp(cptr, http_http, 7) == 0) {
    00328           cptr += 7;
    00329           for(i = 0; i < s.httpheaderlineptr - 7; ++i) {
    00330             if(*cptr == 0 ||
    00331                *cptr == '/' ||
    00332                *cptr == ' ' ||
    00333                *cptr == ':') {
    00334               s.host[i] = 0;
    00335               break;
    00336             }
    00337             s.host[i] = *cptr;
    00338             ++cptr;
    00339           }
    00340         }
    00341         strncpy(s.file, cptr, sizeof(s.file));
    00342         /*      s.file[s.httpheaderlineptr - i] = 0;*/
    00343       }
    00344
    00345
    00346       /* We're done parsing, so we reset the pointer and start the
    00347          next line. */
    00348       s.httpheaderlineptr = 0;
    00349     } else {
    00350       ++s.httpheaderlineptr;
    00351     }
    00352   }
    00353   return len;
    00354 }
    00355 /*-----------------------------------------------------------------------------------*/
    00356 static void
    00357 newdata(void)
    00358 {
    00359   u16_t len;
    00360
    00361   len = uip_datalen();
    00362
    00363   if(s.state == WEBCLIENT_STATE_STATUSLINE) {
    00364     len = parse_statusline(len);
    00365   }
    00366
    00367   if(s.state == WEBCLIENT_STATE_HEADERS && len > 0) {
    00368     len = parse_headers(len);
    00369   }
    00370
    00371   if(len > 0 && s.state == WEBCLIENT_STATE_DATA &&
    00372      s.httpflag != HTTPFLAG_MOVED) {
    00373     webclient_datahandler((char *)uip_appdata, len);
    00374   }
    00375 }
    00376 /*-----------------------------------------------------------------------------------*/
    00377 void
    00378 webclient_appcall(void)
    00379 {
    00380   if(uip_connected()) {
    00381     s.timer = 0;
    00382     s.state = WEBCLIENT_STATE_STATUSLINE;
    00383     senddata();
    00384     webclient_connected();
    00385     return;
    00386   }
    00387
    00388   if(s.state == WEBCLIENT_STATE_CLOSE) {
    00389     webclient_closed();
    00390     uip_abort();
    00391     return;
    00392   }
    00393
    00394   if(uip_aborted()) {
    00395     webclient_aborted();
    00396   }
    00397   if(uip_timedout()) {
    00398     webclient_timedout();
    00399   }
    00400
    00401
    00402   if(uip_acked()) {
    00403     s.timer = 0;
    00404     acked();
    00405   }
    00406   if(uip_newdata()) {
    00407     s.timer = 0;
    00408     newdata();
    00409   }
    00410   if(uip_rexmit() ||
    00411      uip_newdata() ||
    00412      uip_acked()) {
    00413     senddata();
    00414   } else if(uip_poll()) {
    00415     ++s.timer;
    00416     if(s.timer == WEBCLIENT_TIMEOUT) {
    00417       webclient_timedout();
    00418       uip_abort();
    00419       return;
    00420     }
    00421         /*    senddata();*/
    00422   }
    00423
    00424   if(uip_closed()) {
    00425     if(s.httpflag != HTTPFLAG_MOVED) {
    00426       /* Send NULL data to signal EOF. */
    00427       webclient_datahandler(NULL, 0);
    00428     } else {
    00429       if(resolv_lookup(s.host) == NULL) {
    00430         resolv_query(s.host);
    00431       }
    00432       webclient_get(s.host, s.port, s.file);
    00433     }
    00434   }
    00435 }
    00436 /*-----------------------------------------------------------------------------------*/
    00437
    00438 /** @} */
    00439 /** @} */