1 /** 2 * \addtogroup apps 3 * @{ 4 */ 5 6 /** 7 * \defgroup webclient Web client 8 * @{ 9 * 10 * This example shows a HTTP client that is able to download web pages 11 * and files from web servers. It requires a number of callback 12 * functions to be implemented by the module that utilizes the code: 13 * webclient_datahandler(), webclient_connected(), 14 * webclient_timedout(), webclient_aborted(), webclient_closed(). 15 */ 16 17 /** 18 * \file 19 * Implementation of the HTTP client. 20 * \author Adam Dunkels <[email protected]> 21 */ 22 23 /* 24 * Copyright (c) 2002, Adam Dunkels. 25 * All rights reserved. 26 * 27 * Redistribution and use in source and binary forms, with or without 28 * modification, are permitted provided that the following conditions 29 * are met: 30 * 1. Redistributions of source code must retain the above copyright 31 * notice, this list of conditions and the following disclaimer. 32 * 2. Redistributions in binary form must reproduce the above 33 * copyright notice, this list of conditions and the following 34 * disclaimer in the documentation and/or other materials provided 35 * with the distribution. 36 * 3. The name of the author may not be used to endorse or promote 37 * products derived from this software without specific prior 38 * written permission. 39 * 40 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 41 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 42 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 43 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 44 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 45 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 46 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 47 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 48 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 49 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 50 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 51 * 52 * This file is part of the uIP TCP/IP stack. 53 * 54 * $Id: webclient.c,v 1.2 2006/06/11 21:46:37 adam Exp $ 55 * 56 */ 57 58 #include "uip.h" 59 #include "uiplib.h" 60 #include "webclient.h" 61 #include "resolv.h" 62 63 #include <string.h> 64 65 #define WEBCLIENT_TIMEOUT 100 66 67 #define WEBCLIENT_STATE_STATUSLINE 0 68 #define WEBCLIENT_STATE_HEADERS 1 69 #define WEBCLIENT_STATE_DATA 2 70 #define WEBCLIENT_STATE_CLOSE 3 71 72 #define HTTPFLAG_NONE 0 73 #define HTTPFLAG_OK 1 74 #define HTTPFLAG_MOVED 2 75 #define HTTPFLAG_ERROR 3 76 77 78 #define ISO_nl 0x0a 79 #define ISO_cr 0x0d 80 #define ISO_space 0x20 81 82 83 static struct webclient_state s; 84 85 /*-----------------------------------------------------------------------------------*/ 86 char * 87 webclient_mimetype(void) 88 { 89 return s.mimetype; 90 } 91 /*-----------------------------------------------------------------------------------*/ 92 char * 93 webclient_filename(void) 94 { 95 return s.file; 96 } 97 /*-----------------------------------------------------------------------------------*/ 98 char * 99 webclient_hostname(void) 100 { 101 return s.host; 102 } 103 /*-----------------------------------------------------------------------------------*/ 104 unsigned short 105 webclient_port(void) 106 { 107 return s.port; 108 } 109 /*-----------------------------------------------------------------------------------*/ 110 void 111 webclient_init(void) 112 { 113 114 } 115 /*-----------------------------------------------------------------------------------*/ 116 static void 117 init_connection(void) 118 { 119 s.state = WEBCLIENT_STATE_STATUSLINE; 120 121 s.getrequestleft = sizeof(http_get) - 1 + 1 + 122 sizeof(http_10) - 1 + 123 sizeof(http_crnl) - 1 + 124 sizeof(http_host) - 1 + 125 sizeof(http_crnl) - 1 + 126 strlen(http_user_agent_fields) + 127 strlen(s.file) + strlen(s.host); 128 s.getrequestptr = 0; 129 130 s.httpheaderlineptr = 0; 131 } 132 /*-----------------------------------------------------------------------------------*/ 133 void 134 webclient_close(void) 135 { 136 s.state = WEBCLIENT_STATE_CLOSE; 137 } 138 /*-----------------------------------------------------------------------------------*/ 139 unsigned char 140 webclient_get(char *host, u16_t port, char *file) 141 { 142 struct uip_conn *conn; 143 uip_ipaddr_t *ipaddr; 144 static uip_ipaddr_t addr; 145 146 /* First check if the host is an IP address. */ 147 ipaddr = &addr; 148 if(uiplib_ipaddrconv(host, (unsigned char *)addr) == 0) { 149 ipaddr = (uip_ipaddr_t *)resolv_lookup(host); 150 151 if(ipaddr == NULL) { 152 return 0; 153 } 154 } 155 156 conn = uip_connect(ipaddr, htons(port)); 157 158 if(conn == NULL) { 159 return 0; 160 } 161 162 s.port = port; 163 strncpy(s.file, file, sizeof(s.file)); 164 strncpy(s.host, host, sizeof(s.host)); 165 166 init_connection(); 167 return 1; 168 } 169 /*-----------------------------------------------------------------------------------*/ 170 static unsigned char * 171 copy_string(unsigned char *dest, 172 const unsigned char *src, unsigned char len) 173 { 174 strncpy(dest, src, len); 175 return dest + len; 176 } 177 /*-----------------------------------------------------------------------------------*/ 178 static void 179 senddata(void) 180 { 181 u16_t len; 182 char *getrequest; 183 char *cptr; 184 185 if(s.getrequestleft > 0) { 186 cptr = getrequest = (char *)uip_appdata; 187 188 cptr = copy_string(cptr, http_get, sizeof(http_get) - 1); 189 cptr = copy_string(cptr, s.file, strlen(s.file)); 190 *cptr++ = ISO_space; 191 cptr = copy_string(cptr, http_10, sizeof(http_10) - 1); 192 193 cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1); 194 195 cptr = copy_string(cptr, http_host, sizeof(http_host) - 1); 196 cptr = copy_string(cptr, s.host, strlen(s.host)); 197 cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1); 198 199 cptr = copy_string(cptr, http_user_agent_fields, 200 strlen(http_user_agent_fields)); 201 202 len = s.getrequestleft > uip_mss()? 203 uip_mss(): 204 s.getrequestleft; 205 uip_send(&(getrequest[s.getrequestptr]), len); 206 } 207 } 208 /*-----------------------------------------------------------------------------------*/ 209 static void 210 acked(void) 211 { 212 u16_t len; 213 214 if(s.getrequestleft > 0) { 215 len = s.getrequestleft > uip_mss()? 216 uip_mss(): 217 s.getrequestleft; 218 s.getrequestleft -= len; 219 s.getrequestptr += len; 220 } 221 } 222 /*-----------------------------------------------------------------------------------*/ 223 static u16_t 224 parse_statusline(u16_t len) 225 { 226 char *cptr; 227 228 while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) { 229 s.httpheaderline[s.httpheaderlineptr] = *(char *)uip_appdata; 230 ++((char *)uip_appdata); 231 --len; 232 if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) { 233 234 if((strncmp(s.httpheaderline, http_10, 235 sizeof(http_10) - 1) == 0) || 236 (strncmp(s.httpheaderline, http_11, 237 sizeof(http_11) - 1) == 0)) { 238 cptr = &(s.httpheaderline[9]); 239 s.httpflag = HTTPFLAG_NONE; 240 if(strncmp(cptr, http_200, sizeof(http_200) - 1) == 0) { 241 /* 200 OK */ 242 s.httpflag = HTTPFLAG_OK; 243 } else if(strncmp(cptr, http_301, sizeof(http_301) - 1) == 0 || 244 strncmp(cptr, http_302, sizeof(http_302) - 1) == 0) { 245 /* 301 Moved permanently or 302 Found. Location: header line 246 will contain thw new location. */ 247 s.httpflag = HTTPFLAG_MOVED; 248 } else { 249 s.httpheaderline[s.httpheaderlineptr - 1] = 0; 250 } 251 } else { 252 uip_abort(); 253 webclient_aborted(); 254 return 0; 255 } 256 257 /* We're done parsing the status line, so we reset the pointer 258 and start parsing the HTTP headers.*/ 259 s.httpheaderlineptr = 0; 260 s.state = WEBCLIENT_STATE_HEADERS; 261 break; 262 } else { 263 ++s.httpheaderlineptr; 264 } 265 } 266 return len; 267 } 268 /*-----------------------------------------------------------------------------------*/ 269 static char 270 casecmp(char *str1, const char *str2, char len) 271 { 272 static char c; 273 274 while(len > 0) { 275 c = *str1; 276 /* Force lower-case characters. */ 277 if(c & 0x40) { 278 c |= 0x20; 279 } 280 if(*str2 != c) { 281 return 1; 282 } 283 ++str1; 284 ++str2; 285 --len; 286 } 287 return 0; 288 } 289 /*-----------------------------------------------------------------------------------*/ 290 static u16_t 291 parse_headers(u16_t len) 292 { 293 char *cptr; 294 static unsigned char i; 295 296 while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) { 297 s.httpheaderline[s.httpheaderlineptr] = *(char *)uip_appdata; 298 ++((char *)uip_appdata); 299 --len; 300 if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) { 301 /* We have an entire HTTP header line in s.httpheaderline, so 302 we parse it. */ 303 if(s.httpheaderline[0] == ISO_cr) { 304 /* This was the last header line (i.e., and empty "\r\n"), so 305 we are done with the headers and proceed with the actual 306 data. */ 307 s.state = WEBCLIENT_STATE_DATA; 308 return len; 309 } 310 311 s.httpheaderline[s.httpheaderlineptr - 1] = 0; 312 /* Check for specific HTTP header fields. */ 313 if(casecmp(s.httpheaderline, http_content_type, 314 sizeof(http_content_type) - 1) == 0) { 315 /* Found Content-type field. */ 316 cptr = strchr(s.httpheaderline, ';'); 317 if(cptr != NULL) { 318 *cptr = 0; 319 } 320 strncpy(s.mimetype, s.httpheaderline + 321 sizeof(http_content_type) - 1, sizeof(s.mimetype)); 322 } else if(casecmp(s.httpheaderline, http_location, 323 sizeof(http_location) - 1) == 0) { 324 cptr = s.httpheaderline + 325 sizeof(http_location) - 1; 326 327 if(strncmp(cptr, http_http, 7) == 0) { 328 cptr += 7; 329 for(i = 0; i < s.httpheaderlineptr - 7; ++i) { 330 if(*cptr == 0 || 331 *cptr == '/' || 332 *cptr == ' ' || 333 *cptr == ':') { 334 s.host[i] = 0; 335 break; 336 } 337 s.host[i] = *cptr; 338 ++cptr; 339 } 340 } 341 strncpy(s.file, cptr, sizeof(s.file)); 342 /* s.file[s.httpheaderlineptr - i] = 0;*/ 343 } 344 345 346 /* We're done parsing, so we reset the pointer and start the 347 next line. */ 348 s.httpheaderlineptr = 0; 349 } else { 350 ++s.httpheaderlineptr; 351 } 352 } 353 return len; 354 } 355 /*-----------------------------------------------------------------------------------*/ 356 static void 357 newdata(void) 358 { 359 u16_t len; 360 361 len = uip_datalen(); 362 363 if(s.state == WEBCLIENT_STATE_STATUSLINE) { 364 len = parse_statusline(len); 365 } 366 367 if(s.state == WEBCLIENT_STATE_HEADERS && len > 0) { 368 len = parse_headers(len); 369 } 370 371 if(len > 0 && s.state == WEBCLIENT_STATE_DATA && 372 s.httpflag != HTTPFLAG_MOVED) { 373 webclient_datahandler((char *)uip_appdata, len); 374 } 375 } 376 /*-----------------------------------------------------------------------------------*/ 377 void 378 webclient_appcall(void) 379 { 380 if(uip_connected()) { 381 s.timer = 0; 382 s.state = WEBCLIENT_STATE_STATUSLINE; 383 senddata(); 384 webclient_connected(); 385 return; 386 } 387 388 if(s.state == WEBCLIENT_STATE_CLOSE) { 389 webclient_closed(); 390 uip_abort(); 391 return; 392 } 393 394 if(uip_aborted()) { 395 webclient_aborted(); 396 } 397 if(uip_timedout()) { 398 webclient_timedout(); 399 } 400 401 402 if(uip_acked()) { 403 s.timer = 0; 404 acked(); 405 } 406 if(uip_newdata()) { 407 s.timer = 0; 408 newdata(); 409 } 410 if(uip_rexmit() || 411 uip_newdata() || 412 uip_acked()) { 413 senddata(); 414 } else if(uip_poll()) { 415 ++s.timer; 416 if(s.timer == WEBCLIENT_TIMEOUT) { 417 webclient_timedout(); 418 uip_abort(); 419 return; 420 } 421 /* senddata();*/ 422 } 423 424 if(uip_closed()) { 425 if(s.httpflag != HTTPFLAG_MOVED) { 426 /* Send NULL data to signal EOF. */ 427 webclient_datahandler(NULL, 0); 428 } else { 429 if(resolv_lookup(s.host) == NULL) { 430 resolv_query(s.host); 431 } 432 webclient_get(s.host, s.port, s.file); 433 } 434 } 435 } 436 /*-----------------------------------------------------------------------------------*/ 437 438 /** @} */ 439 /** @} */ 440