uIP 1.0
C:/asm/STM32-ethernet/ENC28J60prj/uip-master/apps/webclient/webclient.c
См. документацию.
00001 /**
00002  * \addtogroup apps
00003  * @{
00004  */
00005 
00006 /**
00007  * \defgroup webclient Web-клиент
00008  * @{
00009  *
00010  * Этот пример показывает, как клиент протокола HTTP может загружать
00011  * web-страницы с web-серверов. Он требует реализации нескольких функций
00012  * обратного вызова (callback) в модуле, который задействует код:
00013  * webclient_datahandler(), webclient_connected(),
00014  * webclient_timedout(), webclient_aborted(), webclient_closed().
00015  */
00016 
00017 /**
00018  * \file
00019  * Реализация клиента HTTP.
00020  * \author Adam Dunkels <adam@dunkels.com>
00021  */
00022 
00023 /*
00024  * Copyright (c) 2002, Adam Dunkels.
00025  * Все права зарезервированы.
00026  *
00027  * Повторное распространение, использование в исходном и двоичном виде,
00028  * с модификацией или без - разрешается, если выполняются следующие
00029  * условия:
00030  * 1. Распространение исходного кода должно сохранить вышеуказанную пометку
00031  *    копирайта, этот список условий и следующую правовую оговорку.
00032  * 2. Распространение исходного кода должно сохранить вышеуказанную пометку
00033  *    копирайта, этот список условий и следующую правовую оговорку в
00034  *    документации и/или других материалах, которые будут предоставлены
00035  *    вместе с распространяемыми материалами.
00036  * 3. Имя автора не может использоваться, чтобы подтвердить или продвинуть
00037  *    продукты, написанные с использованием этого программного обеспечения
00038  *    без специального на то разрешения.
00039  *
00040  * ЭТО ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ АВТОРОМ ``КАК ЕСТЬ'', БЕЗ
00041  * КАКОЙ-ЛИБО ЛЮБОЙ РАСШИРЕННОЙ ИЛИ ПОДРАЗУМЕВАЕМОЙ ГАРАНТИИ, ВКЛЮЧАЯ,
00042  * НО НЕ ОГРАНИЧИВАЯСЬ ЭТИМ, ГАРАНТИИ ВЫСОКОГО СПРОСА И ПРИГОДНОСТИ
00043  * ДЛЯ КОНКРЕТНОЙ ЦЕЛИ. АВТОР НИ ПРИ КАКИХ УСЛОВИЯХ НЕ ОТВЕТСТВЕНЕН
00044  * ЗА ЛЮБЫЕ УБЫТКИ - ПРЯМЫЕ, КОСВЕННЫЕ, СЛУЧАЙНЫЕ, СПЕЦИАЛЬНЫЕ, ОБРАЗЦОВЫЕ
00045  * ИЛИ ПОСЛЕДОВАТЕЛЬНЫЕ (ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ЭТИМ, ТРЕБОВАНИЯ
00046  * ЗАМЕНЫ ТОВАРА ИЛИ СЕРВИСА; ПОТЕРИ ИСПОЛЬЗОВАНИЯ, ДАННЫХ ИЛИ ВЫГОДЫ;
00047  * ИЛИ ПРЕКРАЩЕНИЕ БИЗНЕСА), ОДНАКО ВЫЗВАННЫЕ ПО ЛЮБОЙ ТЕОРИИ ОТВЕТСТВЕННОСТИ,
00048  * ЛИБО В КОНТРАКТЕ, ПРЯМОЙ ОТВЕТСТВЕННОСТИ, ЛИБО В НАРУШЕНИИ ЗАКОННЫХ ПРАВ
00049  * (ВКЛЮЧАЯ ТАК ИЛИ ИНАЧЕ НЕБРЕЖНОСТЬ), ВОЗНИКАЮЩИЕ ВСЕГДА ИЗ ИСПОЛЬЗОВАНИЯ
00050  * ЭТОГО ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ, ДАЖЕ ЕСЛИ БЫЛО ПРЕДУПРЕЖДЕНИЕ О ВОЗМОЖНОСТИ
00051  * ТАКОГО ПОВРЕЖДЕНИЯ.
00052  *
00053  * Этот файл является частью стека uIP TCP/IP..
00054  *
00055  * $Id: webclient.c,v 1.2 2006/06/11 21:46:37 adam Exp $
00056  *
00057  */
00058 
00059 #include "uip.h"
00060 #include "uiplib.h"
00061 #include "webclient.h"
00062 #include "resolv.h"
00063 
00064 #include <string.h>
00065 
00066 #define WEBCLIENT_TIMEOUT 100
00067 
00068 #define WEBCLIENT_STATE_STATUSLINE 0
00069 #define WEBCLIENT_STATE_HEADERS    1
00070 #define WEBCLIENT_STATE_DATA       2
00071 #define WEBCLIENT_STATE_CLOSE      3
00072 
00073 #define HTTPFLAG_NONE   0
00074 #define HTTPFLAG_OK     1
00075 #define HTTPFLAG_MOVED  2
00076 #define HTTPFLAG_ERROR  3
00077 
00078 
00079 #define ISO_nl       0x0a
00080 #define ISO_cr       0x0d
00081 #define ISO_space    0x20
00082 
00083 
00084 static struct webclient_state s;
00085 
00086 /*-----------------------------------------------------------------------------------*/
00087 char *
00088 webclient_mimetype(void)
00089 {
00090   return s.mimetype;
00091 }
00092 /*-----------------------------------------------------------------------------------*/
00093 char *
00094 webclient_filename(void)
00095 {
00096   return s.file;
00097 }
00098 /*-----------------------------------------------------------------------------------*/
00099 char *
00100 webclient_hostname(void)
00101 {
00102   return s.host;
00103 }
00104 /*-----------------------------------------------------------------------------------*/
00105 unsigned short
00106 webclient_port(void)
00107 {
00108   return s.port;
00109 }
00110 /*-----------------------------------------------------------------------------------*/
00111 void
00112 webclient_init(void)
00113 {
00114 
00115 }
00116 /*-----------------------------------------------------------------------------------*/
00117 static void
00118 init_connection(void)
00119 {
00120   s.state = WEBCLIENT_STATE_STATUSLINE;
00121 
00122   s.getrequestleft = sizeof(http_get) - 1 + 1 +
00123     sizeof(http_10) - 1 +
00124     sizeof(http_crnl) - 1 +
00125     sizeof(http_host) - 1 +
00126     sizeof(http_crnl) - 1 +
00127     strlen(http_user_agent_fields) +
00128     strlen(s.file) + strlen(s.host);
00129   s.getrequestptr = 0;
00130 
00131   s.httpheaderlineptr = 0;
00132 }
00133 /*-----------------------------------------------------------------------------------*/
00134 void
00135 webclient_close(void)
00136 {
00137   s.state = WEBCLIENT_STATE_CLOSE;
00138 }
00139 /*-----------------------------------------------------------------------------------*/
00140 unsigned char
00141 webclient_get(char *host, u16_t port, char *file)
00142 {
00143   struct uip_conn *conn;
00144   uip_ipaddr_t *ipaddr;
00145   static uip_ipaddr_t addr;
00146   
00147   /* First check if the host is an IP address. */
00148   ipaddr = &addr;
00149   if(uiplib_ipaddrconv(host, (unsigned char *)addr) == 0) {
00150     ipaddr = (uip_ipaddr_t *)resolv_lookup(host);
00151     
00152     if(ipaddr == NULL) {
00153       return 0;
00154     }
00155   }
00156   
00157   conn = uip_connect(ipaddr, htons(port));
00158   
00159   if(conn == NULL) {
00160     return 0;
00161   }
00162   
00163   s.port = port;
00164   strncpy(s.file, file, sizeof(s.file));
00165   strncpy(s.host, host, sizeof(s.host));
00166   
00167   init_connection();
00168   return 1;
00169 }
00170 /*-----------------------------------------------------------------------------------*/
00171 static unsigned char *
00172 copy_string(unsigned char *dest,
00173             const unsigned char *src, unsigned char len)
00174 {
00175   strncpy(dest, src, len);
00176   return dest + len;
00177 }
00178 /*-----------------------------------------------------------------------------------*/
00179 static void
00180 senddata(void)
00181 {
00182   u16_t len;
00183   char *getrequest;
00184   char *cptr;
00185   
00186   if(s.getrequestleft > 0) {
00187     cptr = getrequest = (char *)uip_appdata;
00188 
00189     cptr = copy_string(cptr, http_get, sizeof(http_get) - 1);
00190     cptr = copy_string(cptr, s.file, strlen(s.file));
00191     *cptr++ = ISO_space;
00192     cptr = copy_string(cptr, http_10, sizeof(http_10) - 1);
00193 
00194     cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1);
00195     
00196     cptr = copy_string(cptr, http_host, sizeof(http_host) - 1);
00197     cptr = copy_string(cptr, s.host, strlen(s.host));
00198     cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1);
00199 
00200     cptr = copy_string(cptr, http_user_agent_fields,
00201                        strlen(http_user_agent_fields));
00202     
00203     len = s.getrequestleft > uip_mss()?
00204       uip_mss():
00205       s.getrequestleft;
00206     uip_send(&(getrequest[s.getrequestptr]), len);
00207   }
00208 }
00209 /*-----------------------------------------------------------------------------------*/
00210 static void
00211 acked(void)
00212 {
00213   u16_t len;
00214   
00215   if(s.getrequestleft > 0) {
00216     len = s.getrequestleft > uip_mss()?
00217       uip_mss():
00218       s.getrequestleft;
00219     s.getrequestleft -= len;
00220     s.getrequestptr += len;
00221   }
00222 }
00223 /*-----------------------------------------------------------------------------------*/
00224 static u16_t
00225 parse_statusline(u16_t len)
00226 {
00227   char *cptr;
00228   
00229   while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
00230     s.httpheaderline[s.httpheaderlineptr] = *(char *)uip_appdata;
00231     ++((char *)uip_appdata);
00232     --len;
00233     if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
00234 
00235       if((strncmp(s.httpheaderline, http_10,
00236                   sizeof(http_10) - 1) == 0) ||
00237                   (strncmp(s.httpheaderline, http_11,
00238                   sizeof(http_11) - 1) == 0)) {
00239          cptr = &(s.httpheaderline[9]);
00240          s.httpflag = HTTPFLAG_NONE;
00241          if(strncmp(cptr, http_200, sizeof(http_200) - 1) == 0) {
00242             /* 200 OK */
00243             s.httpflag = HTTPFLAG_OK;
00244          } else if(strncmp(cptr, http_301, sizeof(http_301) - 1) == 0 ||
00245                     strncmp(cptr, http_302, sizeof(http_302) - 1) == 0) {
00246             /* 301 Moved permanently или 302 Found. Место:
00247                строка заголовка будет содержать новое размещение. */
00248             s.httpflag = HTTPFLAG_MOVED;
00249          } else {
00250             s.httpheaderline[s.httpheaderlineptr - 1] = 0;
00251          }
00252       } else {
00253          uip_abort();
00254          webclient_aborted();
00255          return 0;
00256       }
00257       
00258       /* Парсинг строки статуса завершен, так что сбросим указатель
00259          и начнем парсинг заголовков HTTP.*/
00260       s.httpheaderlineptr = 0;
00261       s.state = WEBCLIENT_STATE_HEADERS;
00262       break;
00263     } else {
00264       ++s.httpheaderlineptr;
00265     }
00266   }
00267   return len;
00268 }
00269 /*-----------------------------------------------------------------------------------*/
00270 static char
00271 casecmp(char *str1, const char *str2, char len)
00272 {
00273   static char c;
00274   
00275   while(len > 0) {
00276     c = *str1;
00277     /* Принудительно перейти к символам нижнего регистра (lower-case). */
00278     if(c & 0x40) {
00279       c |= 0x20;
00280     }
00281     if(*str2 != c) {
00282       return 1;
00283     }
00284     ++str1;
00285     ++str2;
00286     --len;
00287   }
00288   return 0;
00289 }
00290 /*-----------------------------------------------------------------------------------*/
00291 static u16_t
00292 parse_headers(u16_t len)
00293 {
00294   char *cptr;
00295   static unsigned char i;
00296   
00297   while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
00298     s.httpheaderline[s.httpheaderlineptr] = *(char *)uip_appdata;
00299     ++((char *)uip_appdata);
00300     --len;
00301     if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
00302       /* У нас честь полная строка заголовка HTTP в s.httpheaderline,
00303          сделаем её парсинг. */
00304       if(s.httpheaderline[0] == ISO_cr) {
00305          /* Это была последняя строка заголовка (например пустая "\r\n"),
00306             с обработкой заголовков покончено, перейдем к реальным данным. */
00307          s.state = WEBCLIENT_STATE_DATA;
00308          return len;
00309       }
00310    
00311       s.httpheaderline[s.httpheaderlineptr - 1] = 0;
00312       /* Проверка полей, специфичных для заголовка HTTP. */
00313       if(casecmp(s.httpheaderline, http_content_type,
00314                  sizeof(http_content_type) - 1) == 0) {
00315          /* Найдено поле Content-type. */
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 + sizeof(http_location) - 1;
00325       
00326          if(strncmp(cptr, http_http, 7) == 0) {
00327             cptr += 7;
00328             for(i = 0; i < s.httpheaderlineptr - 7; ++i) {
00329                if(*cptr == 0 ||
00330                   *cptr == '/' ||
00331                   *cptr == ' ' ||
00332                   *cptr == ':') {
00333                   s.host[i] = 0;
00334                   break;
00335                }
00336                s.host[i] = *cptr;
00337                ++cptr;
00338             }
00339          }
00340          strncpy(s.file, cptr, sizeof(s.file));
00341          /* s.file[s.httpheaderlineptr - i] = 0;*/
00342       }
00343    
00344       /* Мы завершили парсинг, так что сбросим указатель и начнем
00345          обработку следующей строки. */
00346       s.httpheaderlineptr = 0;
00347     } else {
00348       ++s.httpheaderlineptr;
00349     }
00350   }
00351   return len;
00352 }
00353 /*-----------------------------------------------------------------------------------*/
00354 static void
00355 newdata(void)
00356 {
00357   u16_t len;
00358 
00359   len = uip_datalen();
00360 
00361   if(s.state == WEBCLIENT_STATE_STATUSLINE) {
00362     len = parse_statusline(len);
00363   }
00364   
00365   if(s.state == WEBCLIENT_STATE_HEADERS && len > 0) {
00366     len = parse_headers(len);
00367   }
00368 
00369   if(len > 0 && s.state == WEBCLIENT_STATE_DATA &&
00370      s.httpflag != HTTPFLAG_MOVED) {
00371     webclient_datahandler((char *)uip_appdata, len);
00372   }
00373 }
00374 /*-----------------------------------------------------------------------------------*/
00375 void
00376 webclient_appcall(void)
00377 {
00378   if(uip_connected()) {
00379     s.timer = 0;
00380     s.state = WEBCLIENT_STATE_STATUSLINE;
00381     senddata();
00382     webclient_connected();
00383     return;
00384   }
00385 
00386   if(s.state == WEBCLIENT_STATE_CLOSE) {
00387     webclient_closed();
00388     uip_abort();
00389     return;
00390   }
00391 
00392   if(uip_aborted()) {
00393     webclient_aborted();
00394   }
00395   if(uip_timedout()) {
00396     webclient_timedout();
00397   }
00398 
00399   
00400   if(uip_acked()) {
00401     s.timer = 0;
00402     acked();
00403   }
00404   if(uip_newdata()) {
00405     s.timer = 0;
00406     newdata();
00407   }
00408   if(uip_rexmit() ||
00409      uip_newdata() ||
00410      uip_acked()) {
00411     senddata();
00412   } else if(uip_poll()) {
00413     ++s.timer;
00414     if(s.timer == WEBCLIENT_TIMEOUT) {
00415       webclient_timedout();
00416       uip_abort();
00417       return;
00418     }
00419         /*    senddata();*/
00420   }
00421 
00422   if(uip_closed()) {
00423     if(s.httpflag != HTTPFLAG_MOVED) {
00424       /* Отправка данных NULL для сигнализации о конце файла (EOF). */
00425       webclient_datahandler(NULL, 0);
00426     } else {
00427       if(resolv_lookup(s.host) == NULL) {
00428          resolv_query(s.host);
00429       }
00430       webclient_get(s.host, s.port, s.file);
00431     }
00432   }
00433 }
00434 /*-----------------------------------------------------------------------------------*/
00435 
00436 /** @} */
00437 /** @} */