Contiki 3.x
webclient.c
1 /*
2  * Copyright (c) 2002, Adam Dunkels.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following
12  * disclaimer in the documentation and/or other materials provided
13  * with the distribution.
14  * 3. The name of the author may not be used to endorse or promote
15  * products derived from this software without specific prior
16  * written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
24  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * This file is part of the "contiki" web browser.
31  *
32  *
33  */
34 
35 #include <string.h>
36 
37 #include "contiki-net.h"
38 #include "www.h"
39 
40 #include "webclient.h"
41 
42 #define WEBCLIENT_TIMEOUT 100
43 
44 #define WEBCLIENT_STATE_STATUSLINE 0
45 #define WEBCLIENT_STATE_HEADERS 1
46 #define WEBCLIENT_STATE_DATA 2
47 #define WEBCLIENT_STATE_CLOSE 3
48 
49 #define HTTPFLAG_NONE 0
50 #define HTTPFLAG_OK 1
51 #define HTTPFLAG_MOVED 2
52 #define HTTPFLAG_ERROR 3
53 
54 
55 #define ISO_nl 0x0a
56 #define ISO_cr 0x0d
57 #define ISO_space 0x20
58 
59 struct webclient_state {
60  uint8_t timer;
61  uint8_t state;
62  uint8_t httpflag;
63 
64  uint16_t port;
65  char host[40];
66  char file[WWW_CONF_MAX_URLLEN];
67  uint16_t getrequestptr;
68  uint16_t getrequestleft;
69 
70  char httpheaderline[200];
71  uint16_t httpheaderlineptr;
72 
73  char mimetype[32];
74 };
75 
76 static struct webclient_state s;
77 
78 /*-----------------------------------------------------------------------------------*/
79 char *
80 webclient_mimetype(void)
81 {
82  return s.mimetype;
83 }
84 /*-----------------------------------------------------------------------------------*/
85 char *
86 webclient_filename(void)
87 {
88  return s.file;
89 }
90 /*-----------------------------------------------------------------------------------*/
91 char *
92 webclient_hostname(void)
93 {
94  return s.host;
95 }
96 /*-----------------------------------------------------------------------------------*/
97 unsigned short
98 webclient_port(void)
99 {
100  return s.port;
101 }
102 /*-----------------------------------------------------------------------------------*/
103 void
104 webclient_init(void)
105 {
106 
107 }
108 /*-----------------------------------------------------------------------------------*/
109 static void
110 init_connection(void)
111 {
112  s.state = WEBCLIENT_STATE_STATUSLINE;
113 
114  s.getrequestleft = sizeof(http_get) - 1 + 1 +
115  sizeof(http_10) - 1 +
116  sizeof(http_crnl) - 1 +
117  sizeof(http_host) - 1 +
118  sizeof(http_crnl) - 1 +
119  (uint16_t)strlen(http_user_agent_fields) +
120  (uint16_t)strlen(s.file) + (uint16_t)strlen(s.host);
121  s.getrequestptr = 0;
122 
123  s.httpheaderlineptr = 0;
124 }
125 /*-----------------------------------------------------------------------------------*/
126 void
127 webclient_close(void)
128 {
129  s.state = WEBCLIENT_STATE_CLOSE;
130 }
131 /*-----------------------------------------------------------------------------------*/
132 unsigned char
133 webclient_get(const char *host, uint16_t port, const char *file)
134 {
135  uip_ipaddr_t addr;
136  struct uip_conn *conn;
137  uip_ipaddr_t *ipaddr;
138 
139  /* First check if the host is an IP address. */
140  ipaddr = &addr;
141  if(uiplib_ipaddrconv(host, &addr) == 0) {
142 #if UIP_UDP
143  if(resolv_lookup(host,&ipaddr) != RESOLV_STATUS_CACHED) {
144  return 0;
145  }
146 #else /* UIP_UDP */
147  return 0;
148 #endif /* UIP_UDP */
149  }
150 
151  conn = tcp_connect(ipaddr, uip_htons(port), NULL);
152 
153  if(conn == NULL) {
154  return 0;
155  }
156 
157  s.port = port;
158  strncpy(s.file, file, sizeof(s.file));
159  strncpy(s.host, host, sizeof(s.host));
160 
161  init_connection();
162  return 1;
163 }
164 /*-----------------------------------------------------------------------------------*/
165 /* Copy data into a "window", specified by the windowstart and
166  windowend variables. Only data that fits within the window is
167  copied. This function is used to copy data into the uIP buffer, which
168  typically is smaller than the data that is to be copied.
169 */
170 static unsigned char *windowptr;
171 static int windowstart, windowend;
172 static int
173 window_copy(int curptr, const char *data, unsigned char datalen)
174 {
175  int len;
176 
177  if(windowstart == windowend) {
178  return curptr + datalen;
179  }
180 
181  if(curptr + datalen < windowstart) {
182  /* If all the data is before the window, we do not copy the
183  data. */
184  return curptr + datalen;
185  }
186 
187  if(curptr > windowend) {
188  /* If all the data is after the window, we do not copy the data. */
189  return curptr + datalen;
190  }
191 
192  len = datalen;
193 
194  /* Trim off data before the window. */
195  data += windowstart - curptr;
196  len -= windowstart - curptr;
197 
198  /* Trim off data after the window. */
199  if(len > windowend - windowstart) {
200  len = windowend - windowstart;
201  }
202 
203  strncpy(windowptr + windowstart, data, len);
204  windowstart += len;
205 
206  return curptr + datalen;
207 }
208 /*-----------------------------------------------------------------------------------*/
209 static void
210 senddata(void)
211 {
212  uint16_t len;
213  int curptr;
214 
215  if(s.getrequestleft > 0) {
216 
217  windowstart = s.getrequestptr;
218  curptr = 0;
219  windowend = windowstart + uip_mss();
220  windowptr = (char *)uip_appdata - windowstart;
221 
222  curptr = window_copy(curptr, http_get, sizeof(http_get) - 1);
223  curptr = window_copy(curptr, s.file, (unsigned char)strlen(s.file));
224  curptr = window_copy(curptr, " ", 1);
225  curptr = window_copy(curptr, http_10, sizeof(http_10) - 1);
226 
227  curptr = window_copy(curptr, http_crnl, sizeof(http_crnl) - 1);
228 
229  curptr = window_copy(curptr, http_host, sizeof(http_host) - 1);
230  curptr = window_copy(curptr, s.host, (unsigned char)strlen(s.host));
231  curptr = window_copy(curptr, http_crnl, sizeof(http_crnl) - 1);
232 
233  curptr = window_copy(curptr, http_user_agent_fields,
234  (unsigned char)strlen(http_user_agent_fields));
235 
236  len = s.getrequestleft > uip_mss()?
237  uip_mss():
238  s.getrequestleft;
239  uip_send(uip_appdata, len);
240  }
241 }
242 /*-----------------------------------------------------------------------------------*/
243 static void
244 acked(void)
245 {
246  uint16_t len;
247 
248  if(s.getrequestleft > 0) {
249  len = s.getrequestleft > uip_mss()?
250  uip_mss():
251  s.getrequestleft;
252  s.getrequestleft -= len;
253  s.getrequestptr += len;
254  }
255 }
256 /*-----------------------------------------------------------------------------------*/
257 static uint16_t
258 parse_statusline(uint16_t len)
259 {
260  char *cptr;
261 
262  while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
263  s.httpheaderline[s.httpheaderlineptr] = *(char *)uip_appdata;
264  uip_appdata = (char *)uip_appdata + 1;
265  --len;
266  if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
267 
268  if((strncmp(s.httpheaderline, http_10,
269  sizeof(http_10) - 1) == 0) ||
270  (strncmp(s.httpheaderline, http_11,
271  sizeof(http_11) - 1) == 0)) {
272  cptr = &(s.httpheaderline[9]);
273  s.httpflag = HTTPFLAG_NONE;
274  if(strncmp(cptr, http_200, sizeof(http_200) - 1) == 0) {
275  /* 200 OK */
276  s.httpflag = HTTPFLAG_OK;
277  } else if(strncmp(cptr, http_301, sizeof(http_301) - 1) == 0 ||
278  strncmp(cptr, http_302, sizeof(http_302) - 1) == 0) {
279  /* 301 Moved permanently or 302 Found. Location: header line
280  will contain thw new location. */
281  s.httpflag = HTTPFLAG_MOVED;
282  } else {
283  s.httpheaderline[s.httpheaderlineptr - 1] = 0;
284  }
285  } else {
286  uip_abort();
287  webclient_aborted();
288  return 0;
289  }
290 
291  /* We're done parsing the status line, so we reset the pointer
292  and start parsing the HTTP headers.*/
293  s.httpheaderlineptr = 0;
294  s.state = WEBCLIENT_STATE_HEADERS;
295  break;
296  } else {
297  ++s.httpheaderlineptr;
298  }
299  }
300  return len;
301 }
302 /*-----------------------------------------------------------------------------------*/
303 static char
304 casecmp(char *str1, const char *str2, char len)
305 {
306  static char c;
307 
308  while(len > 0) {
309  c = *str1;
310  /* Force lower-case characters. */
311  if(c & 0x40) {
312  c |= 0x20;
313  }
314  if(*str2 != c) {
315  return 1;
316  }
317  ++str1;
318  ++str2;
319  --len;
320  }
321  return 0;
322 }
323 /*-----------------------------------------------------------------------------------*/
324 static uint16_t
325 parse_headers(uint16_t len)
326 {
327  char *cptr;
328  static unsigned char i;
329 
330  while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
331  s.httpheaderline[s.httpheaderlineptr] = *(char *)uip_appdata;
332  uip_appdata = (char *)uip_appdata + 1;
333  --len;
334  if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
335  /* We have an entire HTTP header line in s.httpheaderline, so
336  we parse it. */
337  if(s.httpheaderline[0] == ISO_cr) {
338  /* This was the last header line (i.e., and empty "\r\n"), so
339  we are done with the headers and proceed with the actual
340  data. */
341  s.state = WEBCLIENT_STATE_DATA;
342  return len;
343  }
344 
345  s.httpheaderline[s.httpheaderlineptr - 1] = 0;
346  /* Check for specific HTTP header fields. */
347  if(casecmp(s.httpheaderline, http_content_type,
348  sizeof(http_content_type) - 1) == 0) {
349  /* Found Content-type field. */
350  cptr = strchr(s.httpheaderline, ';');
351  if(cptr != NULL) {
352  *cptr = 0;
353  }
354  strncpy(s.mimetype, s.httpheaderline +
355  sizeof(http_content_type) - 1, sizeof(s.mimetype));
356  } else if(casecmp(s.httpheaderline, http_location,
357  sizeof(http_location) - 1) == 0) {
358  cptr = s.httpheaderline +
359  sizeof(http_location) - 1;
360 
361  if(strncmp(cptr, http_http, 7) == 0) {
362  cptr += 7;
363  for(i = 0; i < s.httpheaderlineptr - 7; ++i) {
364  if(*cptr == 0 ||
365  *cptr == '/' ||
366  *cptr == ' ' ||
367  *cptr == ':') {
368  s.host[i] = 0;
369  break;
370  }
371  s.host[i] = *cptr;
372  ++cptr;
373  }
374  }
375  strncpy(s.file, cptr, sizeof(s.file));
376  /* s.file[s.httpheaderlineptr - i] = 0;*/
377  }
378 
379 
380  /* We're done parsing, so we reset the pointer and start the
381  next line. */
382  s.httpheaderlineptr = 0;
383  } else {
384  ++s.httpheaderlineptr;
385  }
386  }
387  return len;
388 }
389 /*-----------------------------------------------------------------------------------*/
390 static void
391 newdata(void)
392 {
393  uint16_t len;
394 
395  len = uip_datalen();
396 
397  if(s.state == WEBCLIENT_STATE_STATUSLINE) {
398  len = parse_statusline(len);
399  }
400 
401  if(s.state == WEBCLIENT_STATE_HEADERS && len > 0) {
402  len = parse_headers(len);
403  }
404 
405  if(len > 0 && s.state == WEBCLIENT_STATE_DATA &&
406  s.httpflag != HTTPFLAG_MOVED) {
407  webclient_datahandler((char *)uip_appdata, len);
408  }
409 }
410 /*-----------------------------------------------------------------------------------*/
411 void
412 webclient_appcall(void *state)
413 {
414  char *dataptr;
415 
416  if(uip_connected()) {
417  s.timer = 0;
418  s.state = WEBCLIENT_STATE_STATUSLINE;
419  senddata();
420  webclient_connected();
421  tcp_markconn(uip_conn, &s);
422  return;
423  }
424 
425  if(uip_timedout()) {
426  webclient_timedout();
427  }
428 
429  if(uip_aborted()) {
430  webclient_aborted();
431  }
432 
433  if(state == NULL) {
434  uip_abort();
435  return;
436  }
437 
438  if(s.state == WEBCLIENT_STATE_CLOSE) {
439  webclient_closed();
440  uip_abort();
441  return;
442  }
443 
444 
445  /* The acked() and newdata() functions may alter the uip_appdata
446  ptr, so we need to store it in the "dataptr" variable so that we
447  can restore it before the senddata() function is called. */
448  dataptr = uip_appdata;
449 
450  if(uip_acked()) {
451  s.timer = 0;
452  acked();
453  }
454  if(uip_newdata()) {
455  s.timer = 0;
456  newdata();
457  }
458 
459  uip_appdata = dataptr;
460 
461  if(uip_rexmit() ||
462  uip_newdata() ||
463  uip_acked()) {
464  senddata();
465  } else if(uip_poll()) {
466  ++s.timer;
467  if(s.timer == WEBCLIENT_TIMEOUT) {
468  webclient_timedout();
469  uip_abort();
470  return;
471  }
472  /* senddata();*/
473  }
474 
475  if(uip_closed()) {
476  tcp_markconn(uip_conn, NULL);
477  if(s.httpflag != HTTPFLAG_MOVED) {
478  /* Send NULL data to signal EOF. */
479  webclient_datahandler(NULL, 0);
480  } else {
481  /* conn = uip_connect(uip_conn->ripaddr, s.port);
482  if(conn != NULL) {
483  dispatcher_markconn(conn, NULL);
484  init_connection();
485  }*/
486 #if UIP_UDP
487  if(resolv_lookup(s.host, NULL) != RESOLV_STATUS_CACHED) {
488  resolv_query(s.host);
489  }
490 #endif /* UIP_UDP */
491  webclient_get(s.host, s.port, s.file);
492  }
493  }
494 }
495 /*-----------------------------------------------------------------------------------*/
A timer.
Definition: timer.h:86
Hostname is fresh and usable.
Definition: resolv.h:68
Representation of a uIP TCP connection.
Definition: uip.h:1336
CCIF struct uip_conn * tcp_connect(uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
Open a TCP connection to the specified IP address and port.
#define uip_aborted()
Has the connection been aborted by the other end?
Definition: uip.h:781
CCIF void uip_send(const void *data, int len)
Send data on the current connection.
Definition: uip6.c:2310
#define uip_newdata()
Is new incoming data available?
Definition: uip.h:738
#define uip_mss()
Get the current maximum segment size that can be sent on the current connection.
Definition: uip.h:838
#define NULL
The null pointer.
uint16_t len
Length of the data that was previously sent.
Definition: uip.h:1347
#define uip_poll()
Is the connection being polled by uIP?
Definition: uip.h:817
#define uiplib_ipaddrconv
Convert a textual representation of an IP address to a numerical representation.
Definition: uiplib.h:71
#define uip_acked()
Has previously sent data been acknowledged?
Definition: uip.h:749
#define uip_connected()
Has the connection just been connected?
Definition: uip.h:761
#define uip_abort()
Abort the current connection.
Definition: uip.h:682
CCIF uint16_t uip_htons(uint16_t val)
Convert a 16-bit quantity from host byte order to network byte order.
Definition: uip6.c:2298
#define uip_datalen()
The length of any incoming data that is currently available (if available) in the uip_appdata buffer...
Definition: uip.h:651
#define uip_timedout()
Has the connection timed out?
Definition: uip.h:791
#define uip_closed()
Has the connection been closed by the other end?
Definition: uip.h:771
uip_appdata
Pointer to the application data in the packet buffer.
Definition: tcp_loader.c:74
#define uip_rexmit()
Do we need to retransmit previously data?
Definition: uip.h:803