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_HTTPS 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 - 10]; // URL - "http://<host>/"
67  uint16_t getrequestptr;
68  uint16_t getrequestleft;
69 
70  char httpheaderline[WWW_CONF_MAX_URLLEN + 10]; // URL + "Location: "
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((char *)(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 = (unsigned 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) {
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 reached the end of an HTTP header line. */
336  if(s.httpheaderline[0] == ISO_cr) {
337  /* This was the last header line (i.e., and empty "\r\n"), so
338  we are done with the headers and proceed with the actual
339  data. */
340  s.state = WEBCLIENT_STATE_DATA;
341  return len;
342  }
343 
344  if(s.httpheaderlineptr < sizeof(s.httpheaderline) - 1) {
345  /* We have an entire HTTP header line in s.httpheaderline, so
346  we parse it. */
347  s.httpheaderline[s.httpheaderlineptr - 1] = 0;
348  /* Check for specific HTTP header fields. */
349  if(casecmp(s.httpheaderline, http_content_type,
350  sizeof(http_content_type) - 1) == 0) {
351  /* Found Content-type field. */
352  cptr = strchr(s.httpheaderline, ';');
353  if(cptr != NULL) {
354  *cptr = 0;
355  }
356  strncpy(s.mimetype, s.httpheaderline +
357  sizeof(http_content_type) - 1, sizeof(s.mimetype));
358  } else if(casecmp(s.httpheaderline, http_location,
359  sizeof(http_location) - 1) == 0) {
360  cptr = s.httpheaderline +
361  sizeof(http_location) - 1;
362 
363  if(strncmp(cptr, http_https, sizeof(http_https) - 1) == 0) {
364  s.httpflag = HTTPFLAG_HTTPS;
365  } else if(strncmp(cptr, http_http, 7) == 0) {
366  cptr += 7;
367  for(i = 0; i < s.httpheaderlineptr - 7; ++i) {
368  if(*cptr == 0 ||
369  *cptr == '/' ||
370  *cptr == ' ' ||
371  *cptr == ':') {
372  s.host[i] = 0;
373  break;
374  }
375  s.host[i] = *cptr;
376  ++cptr;
377  }
378  }
379  strncpy(s.file, cptr, sizeof(s.file));
380  /* s.file[s.httpheaderlineptr - i] = 0;*/
381  }
382  }
383 
384  /* We're done parsing, so we reset the pointer and start the
385  next line. */
386  s.httpheaderlineptr = 0;
387  } else {
388  if(s.httpheaderlineptr < sizeof(s.httpheaderline) - 1) {
389  ++s.httpheaderlineptr;
390  }
391  }
392  }
393  return len;
394 }
395 /*-----------------------------------------------------------------------------------*/
396 static void
397 newdata(void)
398 {
399  uint16_t len;
400 
401  len = uip_datalen();
402 
403  if(s.state == WEBCLIENT_STATE_STATUSLINE) {
404  len = parse_statusline(len);
405  }
406 
407  if(s.state == WEBCLIENT_STATE_HEADERS && len > 0) {
408  len = parse_headers(len);
409  }
410 
411  if(len > 0 && s.state == WEBCLIENT_STATE_DATA &&
412  s.httpflag == HTTPFLAG_OK) {
413  webclient_datahandler((char *)uip_appdata, len);
414  }
415 }
416 /*-----------------------------------------------------------------------------------*/
417 void
418 webclient_appcall(void *state)
419 {
420  char *dataptr;
421 
422  if(uip_connected()) {
423  s.timer = 0;
424  s.state = WEBCLIENT_STATE_STATUSLINE;
425  senddata();
426  webclient_connected();
427  tcp_markconn(uip_conn, &s);
428  return;
429  }
430 
431  if(uip_timedout()) {
432  webclient_timedout();
433  }
434 
435  if(uip_aborted()) {
436  webclient_aborted();
437  }
438 
439  if(state == NULL) {
440  uip_abort();
441  return;
442  }
443 
444  if(s.state == WEBCLIENT_STATE_CLOSE) {
445  webclient_closed();
446  uip_abort();
447  return;
448  }
449 
450  /* The acked() and newdata() functions may alter the uip_appdata
451  ptr, so we need to store it in the "dataptr" variable so that we
452  can restore it before the senddata() function is called. */
453  dataptr = uip_appdata;
454 
455  if(uip_acked()) {
456  s.timer = 0;
457  acked();
458  }
459  if(uip_newdata()) {
460  s.timer = 0;
461  newdata();
462  }
463 
464  uip_appdata = dataptr;
465 
466  if(uip_rexmit() ||
467  uip_newdata() ||
468  uip_acked()) {
469  senddata();
470  } else if(uip_poll()) {
471  ++s.timer;
472  if(s.timer == WEBCLIENT_TIMEOUT) {
473  webclient_timedout();
474  uip_abort();
475  return;
476  }
477  /* senddata();*/
478  }
479 
480  if(uip_closed()) {
481  tcp_markconn(uip_conn, NULL);
482  /* Client requested close takes precedence over server initiated close. */
483  if(s.state == WEBCLIENT_STATE_CLOSE) {
484  webclient_closed();
485  return;
486  }
487  switch(s.httpflag) {
488  case HTTPFLAG_HTTPS:
489  /* Send some info to the user. */
490  webclient_datahandler((char *)http_redirect, sizeof(http_redirect) - 1);
491  webclient_datahandler(s.file, strlen(s.file));
492  webclient_datahandler((char *)http_crnl, sizeof(http_crnl) - 1);
493  /* FALLTHROUGH */
494  case HTTPFLAG_OK:
495  /* Send NULL data to signal EOF. */
496  webclient_datahandler(NULL, 0);
497  break;
498  case HTTPFLAG_MOVED:
499  /* conn = uip_connect(uip_conn->ripaddr, s.port);
500  if(conn != NULL) {
501  dispatcher_markconn(conn, NULL);
502  init_connection();
503  }*/
504 #if UIP_UDP
505  if(resolv_lookup(s.host, NULL) != RESOLV_STATUS_CACHED) {
506  resolv_query(s.host);
507  }
508 #endif /* UIP_UDP */
509  webclient_get(s.host, s.port, s.file);
510  break;
511  }
512  }
513 }
514 /*-----------------------------------------------------------------------------------*/
#define uip_acked()
Has previously sent data been acknowledged?
Definition: uip.h:750
static uip_ipaddr_t ipaddr
Pointer to prefix information option in uip_buf.
Definition: uip-nd6.c:129
Representation of a uIP TCP connection.
Definition: uip.h:1353
#define uip_abort()
Abort the current connection.
Definition: uip.h:683
static void newdata(void)
Definition: resolv.c:787
static uip_ds6_addr_t * addr
Pointer to a router list entry.
Definition: uip-nd6.c:124
CCIF struct uip_conn * tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
Open a TCP connection to the specified IP address and port.
static const nrf_drv_timer_t timer
Timer instance used for rtimer.
Definition: rtimer-arch.c:49
#define uip_closed()
Has the connection been closed by the other end?
Definition: uip.h:772
uip_appdata
Pointer to the application data in the packet buffer.
Definition: tcp_loader.c:74
#define uip_aborted()
Has the connection been aborted by the other end?
Definition: uip.h:782
#define uip_datalen()
The length of any incoming data that is currently available (if available) in the uip_appdata buffer...
Definition: uip.h:652
#define NULL
The null pointer.
CCIF void uip_send(const void *data, int len)
Send data on the current connection.
Definition: uip.c:1960
resolv_status_t resolv_lookup(const char *name, uip_ipaddr_t **ipaddr)
Look up a hostname in the array of known hostnames.
Definition: resolv.c:1349
#define uip_mss()
Get the current maximum segment size that can be sent on the current connection.
Definition: uip.h:839
#define uip_connected()
Has the connection just been connected?
Definition: uip.h:762
Hostname is fresh and usable.
Definition: resolv.h:63
#define uiplib_ipaddrconv
Convert a textual representation of an IP address to a numerical representation.
Definition: uiplib.h:71
void resolv_query(const char *name)
Queues a name so that a question for the name will be sent out.
Definition: resolv.c:1270
#define uip_rexmit()
Do we need to retransmit previously data?
Definition: uip.h:804
uint16_t len
Length of the data that was previously sent.
Definition: uip.h:1363
#define uip_poll()
Is the connection being polled by uIP?
Definition: uip.h:818
#define uip_timedout()
Has the connection timed out?
Definition: uip.h:792
#define uip_newdata()
Is new incoming data available?
Definition: uip.h:739
CCIF uint16_t uip_htons(uint16_t val)
Convert a 16-bit quantity from host byte order to network byte order.
Definition: uip.c:1948