2 * Copyright (c) 2002, Adam Dunkels.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
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.
30 * This file is part of the "contiki" web browser.
32 * $Id: webclient.c,v 1.11 2010/10/19 18:29:03 adamdunkels Exp $
38 #include "contiki-net.h"
41 #include "webclient.h"
43 #define WEBCLIENT_TIMEOUT 100
45 #define WEBCLIENT_STATE_STATUSLINE 0
46 #define WEBCLIENT_STATE_HEADERS 1
47 #define WEBCLIENT_STATE_DATA 2
48 #define WEBCLIENT_STATE_CLOSE 3
50 #define HTTPFLAG_NONE 0
52 #define HTTPFLAG_MOVED 2
53 #define HTTPFLAG_ERROR 3
58 #define ISO_space 0x20
60 struct webclient_state
{
67 char file
[WWW_CONF_MAX_URLLEN
];
71 char httpheaderline
[200];
72 u16_t httpheaderlineptr
;
77 static struct webclient_state s
;
79 /*-----------------------------------------------------------------------------------*/
81 webclient_mimetype(void)
85 /*-----------------------------------------------------------------------------------*/
87 webclient_filename(void)
91 /*-----------------------------------------------------------------------------------*/
93 webclient_hostname(void)
97 /*-----------------------------------------------------------------------------------*/
103 /*-----------------------------------------------------------------------------------*/
109 /*-----------------------------------------------------------------------------------*/
111 init_connection(void)
113 s
.state
= WEBCLIENT_STATE_STATUSLINE
;
115 s
.getrequestleft
= sizeof(http_get
) - 1 + 1 +
116 sizeof(http_10
) - 1 +
117 sizeof(http_crnl
) - 1 +
118 sizeof(http_host
) - 1 +
119 sizeof(http_crnl
) - 1 +
120 (u16_t
)strlen(http_user_agent_fields
) +
121 (u16_t
)strlen(s
.file
) + (u16_t
)strlen(s
.host
);
124 s
.httpheaderlineptr
= 0;
126 /*-----------------------------------------------------------------------------------*/
128 webclient_close(void)
130 s
.state
= WEBCLIENT_STATE_CLOSE
;
132 /*-----------------------------------------------------------------------------------*/
134 webclient_get(const char *host
, u16_t port
, const char *file
)
137 struct uip_conn
*conn
;
138 uip_ipaddr_t
*ipaddr
;
140 /* First check if the host is an IP address. */
142 if(uiplib_ipaddrconv(host
, &addr
) == 0) {
144 ipaddr
= resolv_lookup(host
);
154 conn
= tcp_connect(ipaddr
, uip_htons(port
), NULL
);
161 strncpy(s
.file
, file
, sizeof(s
.file
));
162 strncpy(s
.host
, host
, sizeof(s
.host
));
167 /*-----------------------------------------------------------------------------------*/
168 /* Copy data into a "window", specified by the windowstart and
169 windowend variables. Only data that fits within the window is
170 copied. This function is used to copy data into the uIP buffer, which
171 typically is smaller than the data that is to be copied.
173 static unsigned char *windowptr
;
174 static int windowstart
, windowend
;
176 window_copy(int curptr
, const char *data
, unsigned char datalen
)
180 if(windowstart
== windowend
) {
181 return curptr
+ datalen
;
184 if(curptr
+ datalen
< windowstart
) {
185 /* If all the data is before the window, we do not copy the
187 return curptr
+ datalen
;
190 if(curptr
> windowend
) {
191 /* If all the data is after the window, we do not copy the data. */
192 return curptr
+ datalen
;
197 /* Trim off data before the window. */
198 data
+= windowstart
- curptr
;
199 len
-= windowstart
- curptr
;
201 /* Trim off data after the window. */
202 if(len
> windowend
- windowstart
) {
203 len
= windowend
- windowstart
;
206 strncpy(windowptr
+ windowstart
, data
, len
);
209 return curptr
+ datalen
;
211 /*-----------------------------------------------------------------------------------*/
218 if(s
.getrequestleft
> 0) {
220 windowstart
= s
.getrequestptr
;
222 windowend
= windowstart
+ uip_mss();
223 windowptr
= (char *)uip_appdata
- windowstart
;
225 curptr
= window_copy(curptr
, http_get
, sizeof(http_get
) - 1);
226 curptr
= window_copy(curptr
, s
.file
, (unsigned char)strlen(s
.file
));
227 curptr
= window_copy(curptr
, " ", 1);
228 curptr
= window_copy(curptr
, http_10
, sizeof(http_10
) - 1);
230 curptr
= window_copy(curptr
, http_crnl
, sizeof(http_crnl
) - 1);
232 curptr
= window_copy(curptr
, http_host
, sizeof(http_host
) - 1);
233 curptr
= window_copy(curptr
, s
.host
, (unsigned char)strlen(s
.host
));
234 curptr
= window_copy(curptr
, http_crnl
, sizeof(http_crnl
) - 1);
236 curptr
= window_copy(curptr
, http_user_agent_fields
,
237 (unsigned char)strlen(http_user_agent_fields
));
239 len
= s
.getrequestleft
> uip_mss()?
242 uip_send(uip_appdata
, len
);
245 /*-----------------------------------------------------------------------------------*/
251 if(s
.getrequestleft
> 0) {
252 len
= s
.getrequestleft
> uip_mss()?
255 s
.getrequestleft
-= len
;
256 s
.getrequestptr
+= len
;
259 /*-----------------------------------------------------------------------------------*/
261 parse_statusline(u16_t len
)
265 while(len
> 0 && s
.httpheaderlineptr
< sizeof(s
.httpheaderline
)) {
266 s
.httpheaderline
[s
.httpheaderlineptr
] = *(char *)uip_appdata
;
267 uip_appdata
= (char *)uip_appdata
+ 1;
269 if(s
.httpheaderline
[s
.httpheaderlineptr
] == ISO_nl
) {
271 if((strncmp(s
.httpheaderline
, http_10
,
272 sizeof(http_10
) - 1) == 0) ||
273 (strncmp(s
.httpheaderline
, http_11
,
274 sizeof(http_11
) - 1) == 0)) {
275 cptr
= &(s
.httpheaderline
[9]);
276 s
.httpflag
= HTTPFLAG_NONE
;
277 if(strncmp(cptr
, http_200
, sizeof(http_200
) - 1) == 0) {
279 s
.httpflag
= HTTPFLAG_OK
;
280 } else if(strncmp(cptr
, http_301
, sizeof(http_301
) - 1) == 0 ||
281 strncmp(cptr
, http_302
, sizeof(http_302
) - 1) == 0) {
282 /* 301 Moved permanently or 302 Found. Location: header line
283 will contain thw new location. */
284 s
.httpflag
= HTTPFLAG_MOVED
;
286 s
.httpheaderline
[s
.httpheaderlineptr
- 1] = 0;
294 /* We're done parsing the status line, so we reset the pointer
295 and start parsing the HTTP headers.*/
296 s
.httpheaderlineptr
= 0;
297 s
.state
= WEBCLIENT_STATE_HEADERS
;
300 ++s
.httpheaderlineptr
;
305 /*-----------------------------------------------------------------------------------*/
307 casecmp(char *str1
, const char *str2
, char len
)
313 /* Force lower-case characters. */
326 /*-----------------------------------------------------------------------------------*/
328 parse_headers(u16_t len
)
331 static unsigned char i
;
333 while(len
> 0 && s
.httpheaderlineptr
< sizeof(s
.httpheaderline
)) {
334 s
.httpheaderline
[s
.httpheaderlineptr
] = *(char *)uip_appdata
;
335 uip_appdata
= (char *)uip_appdata
+ 1;
337 if(s
.httpheaderline
[s
.httpheaderlineptr
] == ISO_nl
) {
338 /* We have an entire HTTP header line in s.httpheaderline, so
340 if(s
.httpheaderline
[0] == ISO_cr
) {
341 /* This was the last header line (i.e., and empty "\r\n"), so
342 we are done with the headers and proceed with the actual
344 s
.state
= WEBCLIENT_STATE_DATA
;
348 s
.httpheaderline
[s
.httpheaderlineptr
- 1] = 0;
349 /* Check for specific HTTP header fields. */
350 if(casecmp(s
.httpheaderline
, http_content_type
,
351 sizeof(http_content_type
) - 1) == 0) {
352 /* Found Content-type field. */
353 cptr
= strchr(s
.httpheaderline
, ';');
357 strncpy(s
.mimetype
, s
.httpheaderline
+
358 sizeof(http_content_type
) - 1, sizeof(s
.mimetype
));
359 } else if(casecmp(s
.httpheaderline
, http_location
,
360 sizeof(http_location
) - 1) == 0) {
361 cptr
= s
.httpheaderline
+
362 sizeof(http_location
) - 1;
364 if(strncmp(cptr
, http_http
, 7) == 0) {
366 for(i
= 0; i
< s
.httpheaderlineptr
- 7; ++i
) {
378 strncpy(s
.file
, cptr
, sizeof(s
.file
));
379 /* s.file[s.httpheaderlineptr - i] = 0;*/
383 /* We're done parsing, so we reset the pointer and start the
385 s
.httpheaderlineptr
= 0;
387 ++s
.httpheaderlineptr
;
392 /*-----------------------------------------------------------------------------------*/
400 if(s
.state
== WEBCLIENT_STATE_STATUSLINE
) {
401 len
= parse_statusline(len
);
404 if(s
.state
== WEBCLIENT_STATE_HEADERS
&& len
> 0) {
405 len
= parse_headers(len
);
408 if(len
> 0 && s
.state
== WEBCLIENT_STATE_DATA
&&
409 s
.httpflag
!= HTTPFLAG_MOVED
) {
410 webclient_datahandler((char *)uip_appdata
, len
);
413 /*-----------------------------------------------------------------------------------*/
415 webclient_appcall(void *state
)
419 if(uip_connected()) {
421 s
.state
= WEBCLIENT_STATE_STATUSLINE
;
423 webclient_connected();
424 tcp_markconn(uip_conn
, &s
);
429 webclient_timedout();
441 if(s
.state
== WEBCLIENT_STATE_CLOSE
) {
448 /* The acked() and newdata() functions may alter the uip_appdata
449 ptr, so we need to store it in the "dataptr" variable so that we
450 can restore it before the senddata() function is called. */
451 dataptr
= uip_appdata
;
462 uip_appdata
= dataptr
;
468 } else if(uip_poll()) {
470 if(s
.timer
== WEBCLIENT_TIMEOUT
) {
471 webclient_timedout();
479 tcp_markconn(uip_conn
, NULL
);
480 if(s
.httpflag
!= HTTPFLAG_MOVED
) {
481 /* Send NULL data to signal EOF. */
482 webclient_datahandler(NULL
, 0);
484 /* conn = uip_connect(uip_conn->ripaddr, s.port);
486 dispatcher_markconn(conn, NULL);
490 if(resolv_lookup(s
.host
) == NULL
) {
491 resolv_query(s
.host
);
494 webclient_get(s
.host
, s
.port
, s
.file
);
498 /*-----------------------------------------------------------------------------------*/