uIP 1.0
|
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 /** @} */