xref: /nrf52832-nimble/rt-thread/components/net/uip/apps/webclient/webclient.c (revision 104654410c56c573564690304ae786df310c91fc)
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