webclient.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. /**
  2. * \addtogroup apps
  3. * @{
  4. */
  5. /**
  6. * \defgroup webclient Web client
  7. * @{
  8. *
  9. * This example shows a HTTP client that is able to download web pages
  10. * and files from web servers. It requires a number of callback
  11. * functions to be implemented by the module that utilizes the code:
  12. * webclient_datahandler(), webclient_connected(),
  13. * webclient_timedout(), webclient_aborted(), webclient_closed().
  14. */
  15. /**
  16. * \file
  17. * Implementation of the HTTP client.
  18. * \author Adam Dunkels <adam@dunkels.com>
  19. */
  20. /*
  21. * Copyright (c) 2002, Adam Dunkels.
  22. * All rights reserved.
  23. *
  24. * Redistribution and use in source and binary forms, with or without
  25. * modification, are permitted provided that the following conditions
  26. * are met:
  27. * 1. Redistributions of source code must retain the above copyright
  28. * notice, this list of conditions and the following disclaimer.
  29. * 2. Redistributions in binary form must reproduce the above
  30. * copyright notice, this list of conditions and the following
  31. * disclaimer in the documentation and/or other materials provided
  32. * with the distribution.
  33. * 3. The name of the author may not be used to endorse or promote
  34. * products derived from this software without specific prior
  35. * written permission.
  36. *
  37. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
  38. * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  39. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  40. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  41. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  42. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  43. * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  44. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  45. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  46. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  47. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  48. *
  49. * This file is part of the uIP TCP/IP stack.
  50. *
  51. * $Id: webclient.c,v 1.2 2006/06/11 21:46:37 adam Exp $
  52. *
  53. */
  54. #include "uip.h"
  55. #include "uiplib.h"
  56. #include "webclient.h"
  57. #include "resolv.h"
  58. #include <string.h>
  59. #define WEBCLIENT_TIMEOUT 100
  60. #define WEBCLIENT_STATE_STATUSLINE 0
  61. #define WEBCLIENT_STATE_HEADERS 1
  62. #define WEBCLIENT_STATE_DATA 2
  63. #define WEBCLIENT_STATE_CLOSE 3
  64. #define HTTPFLAG_NONE 0
  65. #define HTTPFLAG_OK 1
  66. #define HTTPFLAG_MOVED 2
  67. #define HTTPFLAG_ERROR 3
  68. #define ISO_nl 0x0a
  69. #define ISO_cr 0x0d
  70. #define ISO_space 0x20
  71. static struct webclient_state s;
  72. /*-----------------------------------------------------------------------------------*/
  73. char *
  74. webclient_mimetype(void)
  75. {
  76. return s.mimetype;
  77. }
  78. /*-----------------------------------------------------------------------------------*/
  79. char *
  80. webclient_filename(void)
  81. {
  82. return s.file;
  83. }
  84. /*-----------------------------------------------------------------------------------*/
  85. char *
  86. webclient_hostname(void)
  87. {
  88. return s.host;
  89. }
  90. /*-----------------------------------------------------------------------------------*/
  91. unsigned short
  92. webclient_port(void)
  93. {
  94. return s.port;
  95. }
  96. /*-----------------------------------------------------------------------------------*/
  97. void
  98. webclient_init(void)
  99. {
  100. }
  101. /*-----------------------------------------------------------------------------------*/
  102. static void
  103. init_connection(void)
  104. {
  105. s.state = WEBCLIENT_STATE_STATUSLINE;
  106. s.getrequestleft = sizeof(http_get) - 1 + 1 +
  107. sizeof(http_10) - 1 +
  108. sizeof(http_crnl) - 1 +
  109. sizeof(http_host) - 1 +
  110. sizeof(http_crnl) - 1 +
  111. strlen(http_user_agent_fields) +
  112. strlen(s.file) + strlen(s.host);
  113. s.getrequestptr = 0;
  114. s.httpheaderlineptr = 0;
  115. }
  116. /*-----------------------------------------------------------------------------------*/
  117. void
  118. webclient_close(void)
  119. {
  120. s.state = WEBCLIENT_STATE_CLOSE;
  121. }
  122. /*-----------------------------------------------------------------------------------*/
  123. unsigned char
  124. webclient_get(char *host, u16_t port, char *file)
  125. {
  126. struct uip_conn *conn;
  127. uip_ipaddr_t *ipaddr;
  128. static uip_ipaddr_t addr;
  129. /* First check if the host is an IP address. */
  130. ipaddr = &addr;
  131. if(uiplib_ipaddrconv(host, (unsigned char *)addr) == 0) {
  132. ipaddr = (uip_ipaddr_t *)resolv_lookup(host);
  133. if(ipaddr == NULL) {
  134. return 0;
  135. }
  136. }
  137. conn = uip_connect(ipaddr, htons(port));
  138. if(conn == NULL) {
  139. return 0;
  140. }
  141. s.port = port;
  142. strncpy(s.file, file, sizeof(s.file));
  143. strncpy(s.host, host, sizeof(s.host));
  144. init_connection();
  145. return 1;
  146. }
  147. /*-----------------------------------------------------------------------------------*/
  148. static unsigned char *
  149. copy_string(unsigned char *dest,
  150. const unsigned char *src, unsigned char len)
  151. {
  152. strncpy(dest, src, len);
  153. return dest + len;
  154. }
  155. /*-----------------------------------------------------------------------------------*/
  156. static void
  157. senddata(void)
  158. {
  159. u16_t len;
  160. char *getrequest;
  161. char *cptr;
  162. if(s.getrequestleft > 0) {
  163. cptr = getrequest = (char *)uip_appdata;
  164. cptr = copy_string(cptr, http_get, sizeof(http_get) - 1);
  165. cptr = copy_string(cptr, s.file, strlen(s.file));
  166. *cptr++ = ISO_space;
  167. cptr = copy_string(cptr, http_10, sizeof(http_10) - 1);
  168. cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1);
  169. cptr = copy_string(cptr, http_host, sizeof(http_host) - 1);
  170. cptr = copy_string(cptr, s.host, strlen(s.host));
  171. cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1);
  172. cptr = copy_string(cptr, http_user_agent_fields,
  173. strlen(http_user_agent_fields));
  174. len = s.getrequestleft > uip_mss()?
  175. uip_mss():
  176. s.getrequestleft;
  177. uip_send(&(getrequest[s.getrequestptr]), len);
  178. }
  179. }
  180. /*-----------------------------------------------------------------------------------*/
  181. static void
  182. acked(void)
  183. {
  184. u16_t len;
  185. if(s.getrequestleft > 0) {
  186. len = s.getrequestleft > uip_mss()?
  187. uip_mss():
  188. s.getrequestleft;
  189. s.getrequestleft -= len;
  190. s.getrequestptr += len;
  191. }
  192. }
  193. /*-----------------------------------------------------------------------------------*/
  194. static u16_t
  195. parse_statusline(u16_t len)
  196. {
  197. char *cptr;
  198. while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
  199. s.httpheaderline[s.httpheaderlineptr] = *(char *)uip_appdata;
  200. ++((char *)uip_appdata);
  201. --len;
  202. if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
  203. if((strncmp(s.httpheaderline, http_10,
  204. sizeof(http_10) - 1) == 0) ||
  205. (strncmp(s.httpheaderline, http_11,
  206. sizeof(http_11) - 1) == 0)) {
  207. cptr = &(s.httpheaderline[9]);
  208. s.httpflag = HTTPFLAG_NONE;
  209. if(strncmp(cptr, http_200, sizeof(http_200) - 1) == 0) {
  210. /* 200 OK */
  211. s.httpflag = HTTPFLAG_OK;
  212. } else if(strncmp(cptr, http_301, sizeof(http_301) - 1) == 0 ||
  213. strncmp(cptr, http_302, sizeof(http_302) - 1) == 0) {
  214. /* 301 Moved permanently or 302 Found. Location: header line
  215. will contain thw new location. */
  216. s.httpflag = HTTPFLAG_MOVED;
  217. } else {
  218. s.httpheaderline[s.httpheaderlineptr - 1] = 0;
  219. }
  220. } else {
  221. uip_abort();
  222. webclient_aborted();
  223. return 0;
  224. }
  225. /* We're done parsing the status line, so we reset the pointer
  226. and start parsing the HTTP headers.*/
  227. s.httpheaderlineptr = 0;
  228. s.state = WEBCLIENT_STATE_HEADERS;
  229. break;
  230. } else {
  231. ++s.httpheaderlineptr;
  232. }
  233. }
  234. return len;
  235. }
  236. /*-----------------------------------------------------------------------------------*/
  237. static char
  238. casecmp(char *str1, const char *str2, char len)
  239. {
  240. static char c;
  241. while(len > 0) {
  242. c = *str1;
  243. /* Force lower-case characters. */
  244. if(c & 0x40) {
  245. c |= 0x20;
  246. }
  247. if(*str2 != c) {
  248. return 1;
  249. }
  250. ++str1;
  251. ++str2;
  252. --len;
  253. }
  254. return 0;
  255. }
  256. /*-----------------------------------------------------------------------------------*/
  257. static u16_t
  258. parse_headers(u16_t len)
  259. {
  260. char *cptr;
  261. static unsigned char i;
  262. while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
  263. s.httpheaderline[s.httpheaderlineptr] = *(char *)uip_appdata;
  264. ++((char *)uip_appdata);
  265. --len;
  266. if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
  267. /* We have an entire HTTP header line in s.httpheaderline, so
  268. we parse it. */
  269. if(s.httpheaderline[0] == ISO_cr) {
  270. /* This was the last header line (i.e., and empty "\r\n"), so
  271. we are done with the headers and proceed with the actual
  272. data. */
  273. s.state = WEBCLIENT_STATE_DATA;
  274. return len;
  275. }
  276. s.httpheaderline[s.httpheaderlineptr - 1] = 0;
  277. /* Check for specific HTTP header fields. */
  278. if(casecmp(s.httpheaderline, http_content_type,
  279. sizeof(http_content_type) - 1) == 0) {
  280. /* Found Content-type field. */
  281. cptr = strchr(s.httpheaderline, ';');
  282. if(cptr != NULL) {
  283. *cptr = 0;
  284. }
  285. strncpy(s.mimetype, s.httpheaderline +
  286. sizeof(http_content_type) - 1, sizeof(s.mimetype));
  287. } else if(casecmp(s.httpheaderline, http_location,
  288. sizeof(http_location) - 1) == 0) {
  289. cptr = s.httpheaderline +
  290. sizeof(http_location) - 1;
  291. if(strncmp(cptr, http_http, 7) == 0) {
  292. cptr += 7;
  293. for(i = 0; i < s.httpheaderlineptr - 7; ++i) {
  294. if(*cptr == 0 ||
  295. *cptr == '/' ||
  296. *cptr == ' ' ||
  297. *cptr == ':') {
  298. s.host[i] = 0;
  299. break;
  300. }
  301. s.host[i] = *cptr;
  302. ++cptr;
  303. }
  304. }
  305. strncpy(s.file, cptr, sizeof(s.file));
  306. /* s.file[s.httpheaderlineptr - i] = 0;*/
  307. }
  308. /* We're done parsing, so we reset the pointer and start the
  309. next line. */
  310. s.httpheaderlineptr = 0;
  311. } else {
  312. ++s.httpheaderlineptr;
  313. }
  314. }
  315. return len;
  316. }
  317. /*-----------------------------------------------------------------------------------*/
  318. static void
  319. newdata(void)
  320. {
  321. u16_t len;
  322. len = uip_datalen();
  323. if(s.state == WEBCLIENT_STATE_STATUSLINE) {
  324. len = parse_statusline(len);
  325. }
  326. if(s.state == WEBCLIENT_STATE_HEADERS && len > 0) {
  327. len = parse_headers(len);
  328. }
  329. if(len > 0 && s.state == WEBCLIENT_STATE_DATA &&
  330. s.httpflag != HTTPFLAG_MOVED) {
  331. webclient_datahandler((char *)uip_appdata, len);
  332. }
  333. }
  334. /*-----------------------------------------------------------------------------------*/
  335. void
  336. webclient_appcall(void)
  337. {
  338. if(uip_connected()) {
  339. s.timer = 0;
  340. s.state = WEBCLIENT_STATE_STATUSLINE;
  341. senddata();
  342. webclient_connected();
  343. return;
  344. }
  345. if(s.state == WEBCLIENT_STATE_CLOSE) {
  346. webclient_closed();
  347. uip_abort();
  348. return;
  349. }
  350. if(uip_aborted()) {
  351. webclient_aborted();
  352. }
  353. if(uip_timedout()) {
  354. webclient_timedout();
  355. }
  356. if(uip_acked()) {
  357. s.timer = 0;
  358. acked();
  359. }
  360. if(uip_newdata()) {
  361. s.timer = 0;
  362. newdata();
  363. }
  364. if(uip_rexmit() ||
  365. uip_newdata() ||
  366. uip_acked()) {
  367. senddata();
  368. } else if(uip_poll()) {
  369. ++s.timer;
  370. if(s.timer == WEBCLIENT_TIMEOUT) {
  371. webclient_timedout();
  372. uip_abort();
  373. return;
  374. }
  375. /* senddata();*/
  376. }
  377. if(uip_closed()) {
  378. if(s.httpflag != HTTPFLAG_MOVED) {
  379. /* Send NULL data to signal EOF. */
  380. webclient_datahandler(NULL, 0);
  381. } else {
  382. if(resolv_lookup(s.host) == NULL) {
  383. resolv_query(s.host);
  384. }
  385. webclient_get(s.host, s.port, s.file);
  386. }
  387. }
  388. }
  389. /*-----------------------------------------------------------------------------------*/
  390. /** @} */
  391. /** @} */