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 *
webclient_mimetype(void)87 webclient_mimetype(void)
88 {
89 return s.mimetype;
90 }
91 /*-----------------------------------------------------------------------------------*/
92 char *
webclient_filename(void)93 webclient_filename(void)
94 {
95 return s.file;
96 }
97 /*-----------------------------------------------------------------------------------*/
98 char *
webclient_hostname(void)99 webclient_hostname(void)
100 {
101 return s.host;
102 }
103 /*-----------------------------------------------------------------------------------*/
104 unsigned short
webclient_port(void)105 webclient_port(void)
106 {
107 return s.port;
108 }
109 /*-----------------------------------------------------------------------------------*/
110 void
webclient_init(void)111 webclient_init(void)
112 {
113
114 }
115 /*-----------------------------------------------------------------------------------*/
116 static void
init_connection(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
webclient_close(void)134 webclient_close(void)
135 {
136 s.state = WEBCLIENT_STATE_CLOSE;
137 }
138 /*-----------------------------------------------------------------------------------*/
139 unsigned char
webclient_get(char * host,u16_t port,char * file)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 *
copy_string(unsigned char * dest,const unsigned char * src,unsigned char len)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
senddata(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
acked(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
parse_statusline(u16_t len)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
casecmp(char * str1,const char * str2,char len)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
parse_headers(u16_t len)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
newdata(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
webclient_appcall(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