httpd.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. /*
  2. Esp8266 http server - core routines
  3. */
  4. /*
  5. * ----------------------------------------------------------------------------
  6. * "THE BEER-WARE LICENSE" (Revision 42):
  7. * Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
  8. * this notice you can do whatever you want with this stuff. If we meet some day,
  9. * and you think this stuff is worth it, you can buy me a beer in return.
  10. * ----------------------------------------------------------------------------
  11. * Modified and enhanced by Thorsten von Eicken in 2015
  12. * ----------------------------------------------------------------------------
  13. * adapted to meta-id project by ikujam@ikujam.org (2018)
  14. */
  15. #include <esp8266.h>
  16. #include "httpd.h"
  17. #include "httpdespfs.h"
  18. //#define HTTPD_DBG
  19. #ifdef HTTPD_DBG
  20. #define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0)
  21. #else
  22. #define DBG(format, ...) do { } while(0)
  23. #endif
  24. extern int metaCheckHash (int32 hash);
  25. //Max length of request head
  26. #define MAX_HEAD_LEN 1024
  27. //Max amount of connections
  28. #define MAX_CONN 6
  29. //Max post buffer len
  30. #define MAX_POST 1024
  31. //Max send buffer len
  32. #define MAX_SENDBUFF_LEN 2600
  33. //This gets set at init time.
  34. static HttpdBuiltInUrl *builtInUrls;
  35. //Private data for http connection
  36. struct HttpdPriv {
  37. char head[MAX_HEAD_LEN]; // buffer to accumulate header
  38. char from[24]; // source ip&port
  39. char *sendBuff; // output buffer
  40. short headPos; // offset into header
  41. short sendBuffLen; // offset into output buffer
  42. short sendBuffMax; // size of output buffer
  43. short code; // http response code (only for logging)
  44. };
  45. //Connection pool
  46. static HttpdPriv connPrivData[MAX_CONN];
  47. static HttpdConnData connData[MAX_CONN];
  48. static HttpdPostData connPostData[MAX_CONN];
  49. //Listening connection data
  50. static struct espconn httpdConn;
  51. static esp_tcp httpdTcp;
  52. static char metahostname[64];
  53. //Struct to keep extension->mime data in
  54. typedef struct {
  55. const char *ext;
  56. const char *mimetype;
  57. } MimeMap;
  58. //The mappings from file extensions to mime types. If you need an extra mime type,
  59. //add it here.
  60. static const MimeMap mimeTypes[] = {
  61. { "htm", "text/htm" },
  62. { "html", "text/html; charset=UTF-8" },
  63. { "css", "text/css" },
  64. { "js", "text/javascript" },
  65. { "txt", "text/plain" },
  66. { "wav", "audio/wav" },
  67. { "jpg", "image/jpeg" },
  68. { "jpeg", "image/jpeg" },
  69. { "png", "image/png" },
  70. { "tpl", "text/html; charset=UTF-8" },
  71. { NULL, "text/html" }, //default value
  72. };
  73. //Returns a static char* to a mime type for a given url to a file.
  74. const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) {
  75. int i = 0;
  76. //Go find the extension
  77. char *ext = url + (strlen(url) - 1);
  78. while (ext != url && *ext != '.') ext--;
  79. if (*ext == '.') ext++;
  80. //ToDo: os_strcmp is case sensitive; we may want to do case-intensive matching here...
  81. while (mimeTypes[i].ext != NULL && os_strcmp(ext, mimeTypes[i].ext) != 0) i++;
  82. return mimeTypes[i].mimetype;
  83. }
  84. // debug string to identify connection (ip address & port)
  85. // a static string works because callbacks don't get interrupted...
  86. static char connStr[24];
  87. static void debugConn(void *arg, char *what) {
  88. #if 0
  89. struct espconn *espconn = arg;
  90. esp_tcp *tcp = espconn->proto.tcp;
  91. os_sprintf(connStr, "%d.%d.%d.%d:%d ",
  92. tcp->remote_ip[0], tcp->remote_ip[1], tcp->remote_ip[2], tcp->remote_ip[3],
  93. tcp->remote_port);
  94. DBG("%s %s\n", connStr, what);
  95. #else
  96. connStr[0] = 0;
  97. #endif
  98. }
  99. // Retires a connection for re-use
  100. static void ICACHE_FLASH_ATTR httpdRetireConn(HttpdConnData *conn) {
  101. if (conn->conn && conn->conn->reverse == conn)
  102. conn->conn->reverse = NULL; // break reverse link
  103. // log information about the request we handled
  104. uint32 dt = conn->startTime;
  105. if (dt > 0) dt = (system_get_time() - dt) / 1000;
  106. if (conn->conn && conn->url)
  107. #if 0
  108. DBG("HTTP %s %s from %s -> %d in %ums, heap=%ld\n",
  109. conn->requestType == HTTPD_METHOD_GET ? "GET" : "POST", conn->url, conn->priv->from,
  110. conn->priv->code, dt, (unsigned long)system_get_free_heap_size());
  111. #else
  112. DBG("HTTP %s %s: %d, %ums, h=%ld\n",
  113. conn->requestType == HTTPD_METHOD_GET ? "GET" : "POST", conn->url,
  114. conn->priv->code, dt, (unsigned long)system_get_free_heap_size());
  115. #endif
  116. conn->conn = NULL; // don't try to send anything, the SDK crashes...
  117. if (conn->cgi != NULL) conn->cgi(conn); // free cgi data
  118. if (conn->post->buff != NULL) os_free(conn->post->buff);
  119. conn->cgi = NULL;
  120. conn->post->buff = NULL;
  121. conn->post->multipartBoundary = NULL;
  122. }
  123. //Stupid li'l helper function that returns the value of a hex char.
  124. static int httpdHexVal(char c) {
  125. if (c >= '0' && c <= '9') return c - '0';
  126. if (c >= 'A' && c <= 'F') return c - 'A' + 10;
  127. if (c >= 'a' && c <= 'f') return c - 'a' + 10;
  128. return 0;
  129. }
  130. //Decode a percent-encoded value.
  131. //Takes the valLen bytes stored in val, and converts it into at most retLen bytes that
  132. //are stored in the ret buffer. Returns the actual amount of bytes used in ret. Also
  133. //zero-terminates the ret buffer.
  134. int httpdUrlDecode(char *val, int valLen, char *ret, int retLen) {
  135. int s = 0, d = 0;
  136. int esced = 0, escVal = 0;
  137. while (s<valLen && d<retLen) {
  138. if (esced == 1) {
  139. escVal = httpdHexVal(val[s]) << 4;
  140. esced = 2;
  141. }
  142. else if (esced == 2) {
  143. escVal += httpdHexVal(val[s]);
  144. ret[d++] = escVal;
  145. esced = 0;
  146. }
  147. else if (val[s] == '%') {
  148. esced = 1;
  149. }
  150. else if (val[s] == '+') {
  151. ret[d++] = ' ';
  152. }
  153. else {
  154. ret[d++] = val[s];
  155. }
  156. s++;
  157. }
  158. if (d<retLen) ret[d] = 0;
  159. return d;
  160. }
  161. //Find a specific arg in a string of get- or post-data.
  162. //Line is the string of post/get-data, arg is the name of the value to find. The
  163. //zero-terminated result is written in buff, with at most buffLen bytes used. The
  164. //function returns the length of the result, or -1 if the value wasn't found. The
  165. //returned string will be urldecoded already.
  166. int ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg, char *buff, int buffLen) {
  167. char *p, *e;
  168. if (line == NULL) return 0;
  169. p = line;
  170. while (p != NULL && *p != '\n' && *p != '\r' && *p != 0) {
  171. //os_printf("findArg: %s\n", p);
  172. if (os_strncmp(p, arg, os_strlen(arg)) == 0 && p[strlen(arg)] == '=') {
  173. p += os_strlen(arg) + 1; //move p to start of value
  174. e = (char*)os_strstr(p, "&");
  175. if (e == NULL) e = p + os_strlen(p);
  176. //os_printf("findArg: val %s len %d\n", p, (e-p));
  177. return httpdUrlDecode(p, (e - p), buff, buffLen);
  178. }
  179. p = (char*)os_strstr(p, "&");
  180. if (p != NULL) p += 1;
  181. }
  182. //os_printf("Finding %s in %s: Not found :/\n", arg, line);
  183. return -1; //not found
  184. }
  185. //Get the value of a certain header in the HTTP client head
  186. int ICACHE_FLASH_ATTR httpdGetHeader(HttpdConnData *conn, char *header, char *ret, int retLen) {
  187. char *p = conn->priv->head;
  188. p = p + strlen(p) + 1; //skip GET/POST part
  189. p = p + strlen(p) + 1; //skip HTTP part
  190. while (p<(conn->priv->head + conn->priv->headPos)) {
  191. while (*p <= 32 && *p != 0) p++; //skip crap at start
  192. //See if this is the header
  193. if (os_strncmp(p, header, strlen(header)) == 0 && p[strlen(header)] == ':') {
  194. //Skip 'key:' bit of header line
  195. p = p + strlen(header) + 1;
  196. //Skip past spaces after the colon
  197. while (*p == ' ') p++;
  198. //Copy from p to end
  199. while (*p != 0 && *p != '\r' && *p != '\n' && retLen>1) {
  200. *ret++ = *p++;
  201. retLen--;
  202. }
  203. //Zero-terminate string
  204. *ret = 0;
  205. //All done :)
  206. return 1;
  207. }
  208. p += strlen(p) + 1; //Skip past end of string and \0 terminator
  209. }
  210. return 0;
  211. }
  212. //Setup an output buffer
  213. void ICACHE_FLASH_ATTR httpdSetOutputBuffer(HttpdConnData *conn, char *buff, short max) {
  214. conn->priv->sendBuff = buff;
  215. conn->priv->sendBuffLen = 0;
  216. conn->priv->sendBuffMax = max;
  217. }
  218. //Start the response headers.
  219. void ICACHE_FLASH_ATTR httpdStartResponse(HttpdConnData *conn, int code) {
  220. char buff[128];
  221. int l;
  222. conn->priv->code = code;
  223. char *status = code < 400 ? "OK" : "ERROR";
  224. l = os_sprintf(buff, "HTTP/1.0 %d %s\r\nServer: meta-id\r\nConnection: close\r\n", code, status);
  225. httpdSend(conn, buff, l);
  226. }
  227. //Send a http header.
  228. void ICACHE_FLASH_ATTR httpdHeader(HttpdConnData *conn, const char *field, const char *val) {
  229. char buff[256];
  230. int l;
  231. l = os_sprintf(buff, "%s: %s\r\n", field, val);
  232. httpdSend(conn, buff, l);
  233. }
  234. //Finish the headers.
  235. void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn) {
  236. httpdSend(conn, "\r\n", -1);
  237. }
  238. //ToDo: sprintf->snprintf everywhere... esp doesn't have snprintf tho' :/
  239. //Redirect to the given URL.
  240. void ICACHE_FLASH_ATTR httpdRedirect(HttpdConnData *conn, char *newUrl) {
  241. char buff[1024];
  242. int l;
  243. conn->priv->code = 302;
  244. l = os_sprintf(buff, "HTTP/1.0 302 Found\r\nServer: meta-id\r\nConnection: close\r\n"
  245. "Location: %s\r\n\r\nRedirecting to %s\r\n", newUrl, newUrl);
  246. httpdSend(conn, buff, l);
  247. }
  248. // set cookie with password hash and redirect
  249. // if hash=0, delete cookie with date in past
  250. int ICACHE_FLASH_ATTR httpdSetCookie(HttpdConnData *conn, char *newUrl, uint32 hash) {
  251. char buff[1024];
  252. int l;
  253. int expire;
  254. DBG("SetCookie : %d - redirect to %s\n", hash, newUrl);
  255. // connData->priv->code = 302;
  256. if(hash==0)expire=1979;else expire=2024;
  257. l = os_sprintf(buff, "HTTP/1.0 302 Found\r\nServer: meta-id\r\nConnection: close\r\n"
  258. "Set-Cookie: h=%d; path=/; expires=Mon, 1 Jan %d 23:42:01 GMT; max-age: 3600; HttpOnly\r\n"
  259. "Location: %s\r\n\r\nRedirecting to %s\r\n", hash, expire, newUrl, newUrl);
  260. httpdSend(conn, buff, l);
  261. /* hash, expire);
  262. conn->cgiArg=buff;
  263. connData->url=newUrl;*/
  264. return cgiEspFsHook(connData);
  265. }
  266. int ICACHE_FLASH_ATTR httpdSendAuthCookie(HttpdConnData *conn, uint32 hash) {
  267. char buff[1024];
  268. int expire;
  269. int l;
  270. DBG("SendAuthCookie : %d\n", hash);
  271. // connData->priv->code = 302;
  272. if(hash==0)expire=1979;else expire=2024;
  273. conn->priv->code = 302;
  274. l = os_sprintf(buff, "HTTP/1.0 302 Found\r\nServer: meta-id\r\nConnection: close\r\n"
  275. "Set-Cookie: h=%d; path=/; expires=Mon, 1 Jan %d 23:42:01 GMT; max-age: 3600; HttpOnly\r\n"
  276. "Location: /meta/auth\r\n\r\nRedirecting to /meta/auth\r\n", hash, expire);
  277. httpdSend(conn, buff, l);
  278. return HTTPD_CGI_DONE;
  279. }
  280. // authentication failed
  281. void ICACHE_FLASH_ATTR httpdForbidden(HttpdConnData *conn) {
  282. char buff[1024];
  283. int l;
  284. connData->priv->code = 401;
  285. l = os_sprintf(buff, "HTTP/1.0 401 Forbidden\r\nServer: meta-id\r\nConnection: close\r\n\r\n"
  286. "<html><body>Not allowed<br/><a href=\"/welcome.html\">Home</a></body></html>\r\n");
  287. httpdSend(connData, buff, l);
  288. }
  289. //Use this as a cgi function to redirect one url to another.
  290. int ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData) {
  291. if (connData->conn == NULL) {
  292. //Connection aborted. Clean up.
  293. return HTTPD_CGI_DONE;
  294. }
  295. httpdRedirect(connData, (char*)connData->cgiArg);
  296. return HTTPD_CGI_DONE;
  297. }
  298. //Add data to the send buffer. len is the length of the data. If len is -1
  299. //the data is seen as a C-string.
  300. //Returns 1 for success, 0 for out-of-memory.
  301. int ICACHE_FLASH_ATTR httpdSend(HttpdConnData *conn, const char *data, int len) {
  302. if (len<0) len = strlen(data);
  303. if (conn->priv->sendBuffLen + len>conn->priv->sendBuffMax) {
  304. DBG("%sERROR! httpdSend full (%d of %d)\n",
  305. connStr, conn->priv->sendBuffLen, conn->priv->sendBuffMax);
  306. return 0;
  307. }
  308. os_memcpy(conn->priv->sendBuff + conn->priv->sendBuffLen, data, len);
  309. conn->priv->sendBuffLen += len;
  310. return 1;
  311. }
  312. //Helper function to send any data in conn->priv->sendBuff
  313. void ICACHE_FLASH_ATTR httpdFlush(HttpdConnData *conn) {
  314. if (conn->priv->sendBuffLen != 0) {
  315. sint8 status = espconn_sent(conn->conn, (uint8_t*)conn->priv->sendBuff, conn->priv->sendBuffLen);
  316. if (status != 0) {
  317. DBG("%sERROR! espconn_sent returned %d, trying to send %d to %s\n",
  318. connStr, status, conn->priv->sendBuffLen, conn->url);
  319. }
  320. conn->priv->sendBuffLen = 0;
  321. }
  322. }
  323. //Callback called when the data on a socket has been successfully sent.
  324. static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) {
  325. debugConn(arg, "httpdSentCb");
  326. struct espconn* pCon = (struct espconn *)arg;
  327. HttpdConnData *conn = (HttpdConnData *)pCon->reverse;
  328. if (conn == NULL) return; // aborted connection
  329. char sendBuff[MAX_SENDBUFF_LEN];
  330. httpdSetOutputBuffer(conn, sendBuff, sizeof(sendBuff));
  331. if (conn->cgi == NULL) { //Marked for destruction?
  332. //os_printf("Closing 0x%p/0x%p->0x%p\n", arg, conn->conn, conn);
  333. espconn_disconnect(conn->conn); // we will get a disconnect callback
  334. return; //No need to call httpdFlush.
  335. }
  336. int r = conn->cgi(conn); //Execute cgi fn.
  337. if (r == HTTPD_CGI_DONE) {
  338. conn->cgi = NULL; //mark for destruction.
  339. }
  340. if (r == HTTPD_CGI_NOTFOUND || r == HTTPD_CGI_AUTHENTICATED) {
  341. DBG("%sERROR! Bad CGI code %d\n", connStr, r);
  342. conn->cgi = NULL; //mark for destruction.
  343. }
  344. httpdFlush(conn);
  345. }
  346. static const char *httpNotFoundHeader = "HTTP/1.0 404 Not Found\r\nServer: meta-id\r\n"
  347. "Connection: close\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nNot Found.\r\n";
  348. //This is called when the headers have been received and the connection is ready to send
  349. //the result headers and data.
  350. //We need to find the CGI function to call, call it, and dependent on what it returns either
  351. //find the next cgi function, wait till the cgi data is sent or close up the connection.
  352. static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
  353. int r;
  354. int i = 0;
  355. if (conn->url == NULL) {
  356. DBG("%sWtF? url = NULL\n", connStr);
  357. return; //Shouldn't happen
  358. }
  359. //See if we can find a CGI that's happy to handle the request.
  360. while (1) {
  361. //Look up URL in the built-in URL table.
  362. if (conn->cgi == NULL) {
  363. while (builtInUrls[i].url != NULL) {
  364. int match = 0;
  365. int urlLen = os_strlen(builtInUrls[i].url);
  366. //See if there's a literal match
  367. if (os_strcmp(builtInUrls[i].url, conn->url) == 0) match = 1;
  368. //See if there's a wildcard match
  369. if (builtInUrls[i].url[urlLen - 1] == '*' &&
  370. os_strncmp(builtInUrls[i].url, conn->url, urlLen - 1) == 0) match = 1;
  371. else if (builtInUrls[i].url[0] == '*' && ( strlen(conn->url) >= urlLen -1 ) &&
  372. os_strncmp(builtInUrls[i].url + 1, conn->url + strlen(conn->url) - urlLen + 1, urlLen - 1) == 0) match = 1;
  373. if (match) {
  374. if(builtInUrls[i].auth){
  375. DBG("%s%s auth check!\n", connStr, conn->url);
  376. if(!metaCheckHash(conn->hash)){
  377. DBG("%s%s failed authentication - 401!\n", connStr, conn->url);
  378. httpdForbidden(conn);
  379. httpdFlush(conn);
  380. conn->cgi = NULL; //mark for destruction.
  381. if (conn->post) conn->post->len = 0; // skip any remaining receives
  382. return;
  383. }
  384. DBG("%s%s Authenticated, proceeding\n", connStr, conn->url);
  385. }
  386. //os_printf("Is url index %d\n", i);
  387. conn->cgiData = NULL;
  388. conn->cgiResponse = NULL;
  389. conn->cgi = builtInUrls[i].cgiCb;
  390. conn->cgiArg = builtInUrls[i].cgiArg;
  391. break;
  392. }
  393. i++;
  394. }
  395. if (builtInUrls[i].url == NULL) {
  396. //Drat, we're at the end of the URL table. This usually shouldn't happen. Well, just
  397. //generate a built-in 404 to handle this.
  398. DBG("%s%s not found. 404!\n", connStr, conn->url);
  399. httpdSend(conn, httpNotFoundHeader, -1);
  400. httpdFlush(conn);
  401. conn->cgi = NULL; //mark for destruction.
  402. if (conn->post) conn->post->len = 0; // skip any remaining receives
  403. return;
  404. }
  405. }
  406. //Okay, we have a CGI function that matches the URL.
  407. // See if it wants to handle the particular URL we're supposed to handle.
  408. r = conn->cgi(conn);
  409. if (r == HTTPD_CGI_MORE) {
  410. //Yep, it's happy to do so and has more data to send.
  411. httpdFlush(conn);
  412. return;
  413. }
  414. else if (r == HTTPD_CGI_DONE) {
  415. //Yep, it's happy to do so and already is done sending data.
  416. httpdFlush(conn);
  417. conn->cgi = NULL; //mark for destruction.
  418. if (conn->post) conn->post->len = 0; // skip any remaining receives
  419. return;
  420. }
  421. else {
  422. if (!(r == HTTPD_CGI_NOTFOUND || r == HTTPD_CGI_AUTHENTICATED)) {
  423. os_printf("%shandler for %s returned invalid result %d\n", connStr, conn->url, r);
  424. }
  425. //URL doesn't want to handle the request: either the data isn't found or there's no
  426. //need to generate a login screen.
  427. conn->cgi = NULL; // force lookup again
  428. i++; //look at next url the next iteration of the loop.
  429. }
  430. }
  431. }
  432. //Parse a line of header data and modify the connection data accordingly.
  433. static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
  434. int i;
  435. char first_line = false;
  436. if (os_strncmp(h, "GET ", 4) == 0) {
  437. conn->requestType = HTTPD_METHOD_GET;
  438. first_line = true;
  439. }
  440. else if (os_strncmp(h, "POST ", 5) == 0) {
  441. conn->requestType = HTTPD_METHOD_POST;
  442. first_line = true;
  443. }
  444. if (first_line) {
  445. char *e;
  446. //Skip past the space after POST/GET
  447. i = 0;
  448. while (h[i] != ' ') i++;
  449. conn->url = h + i + 1;
  450. conn->hash=0;
  451. //Figure out end of url.
  452. e = (char*)os_strstr(conn->url, " ");
  453. if (e == NULL) return; //wtf?
  454. *e = 0; //terminate url part
  455. // Count number of open connections
  456. //esp_tcp *tcp = conn->conn->proto.tcp;
  457. //DBG("%sHTTP %s %s from %s\n", connStr,
  458. // conn->requestType == HTTPD_METHOD_GET ? "GET" : "POST", conn->url, conn->priv->from);
  459. //Parse out the URL part before the GET parameters.
  460. conn->getArgs = (char*)os_strstr(conn->url, "?");
  461. if (conn->getArgs != 0) {
  462. *conn->getArgs = 0;
  463. conn->getArgs++;
  464. //DBG("%sargs = %s\n", connStr, conn->getArgs);
  465. }
  466. else {
  467. conn->getArgs = NULL;
  468. }
  469. }
  470. else if (os_strncmp(h, "Content-Length:", 15) == 0) {
  471. i = 15;
  472. //Skip trailing spaces
  473. while (h[i] == ' ') i++;
  474. //Get POST data length
  475. conn->post->len = atoi(h + i);
  476. // Allocate the buffer
  477. if (conn->post->len > MAX_POST) {
  478. // we'll stream this in in chunks
  479. conn->post->buffSize = MAX_POST;
  480. }
  481. else {
  482. conn->post->buffSize = conn->post->len;
  483. }
  484. //DBG("Mallocced buffer for %d + 1 bytes of post data.\n", conn->post->buffSize);
  485. conn->post->buff = (char*)os_malloc(conn->post->buffSize + 1);
  486. conn->post->buffLen = 0;
  487. }
  488. else if (os_strncmp(h, "Content-Type: ", 14) == 0) {
  489. if (os_strstr(h, "multipart/form-data")) {
  490. // It's multipart form data so let's pull out the boundary for future use
  491. char *b;
  492. if ((b = os_strstr(h, "boundary=")) != NULL) {
  493. conn->post->multipartBoundary = b + 7; // move the pointer 2 chars before boundary then fill them with dashes
  494. conn->post->multipartBoundary[0] = '-';
  495. conn->post->multipartBoundary[1] = '-';
  496. //DBG("boundary = %s\n", conn->post->multipartBoundary);
  497. }
  498. }
  499. }
  500. else if (os_strncmp(h, "Cookie:", 7) == 0) {
  501. char*hash;
  502. hash=os_strstr(h,"h=");
  503. conn->hash = (uint32_t)atoi(hash+2);
  504. DBG("got cookie = %d\n", conn->hash);
  505. }
  506. }
  507. //Callback called when there's data available on a socket.
  508. static void ICACHE_FLASH_ATTR httpdRecvCb(void *arg, char *data, unsigned short len) {
  509. debugConn(arg, "httpdRecvCb");
  510. struct espconn* pCon = (struct espconn *)arg;
  511. HttpdConnData *conn = (HttpdConnData *)pCon->reverse;
  512. if (conn == NULL) return; // aborted connection
  513. char sendBuff[MAX_SENDBUFF_LEN];
  514. httpdSetOutputBuffer(conn, sendBuff, sizeof(sendBuff));
  515. //This is slightly evil/dirty: we abuse conn->post->len as a state variable for where in the http communications we are:
  516. //<0 (-1): Post len unknown because we're still receiving headers
  517. //==0: No post data
  518. //>0: Need to receive post data
  519. //ToDo: See if we can use something more elegant for this.
  520. for (int x = 0; x<len; x++) {
  521. if (conn->post->len<0) {
  522. //This byte is a header byte.
  523. if (conn->priv->headPos != MAX_HEAD_LEN) conn->priv->head[conn->priv->headPos++] = data[x];
  524. conn->priv->head[conn->priv->headPos] = 0;
  525. //Scan for /r/n/r/n. Receiving this indicate the headers end.
  526. if (data[x] == '\n' && (char *)os_strstr(conn->priv->head, "\r\n\r\n") != NULL) {
  527. //Indicate we're done with the headers.
  528. conn->post->len = 0;
  529. conn->post->multipartBoundary = NULL;
  530. //Reset url data
  531. conn->url = NULL;
  532. conn->hash=0;
  533. //Iterate over all received headers and parse them.
  534. char *p = conn->priv->head;
  535. while (p<(&conn->priv->head[conn->priv->headPos - 4])) {
  536. char *e = (char *)os_strstr(p, "\r\n"); //Find end of header line
  537. if (e == NULL) break; //Shouldn't happen.
  538. e[0] = 0; //Zero-terminate header
  539. httpdParseHeader(p, conn); //and parse it.
  540. p = e + 2; //Skip /r/n (now /0/n)
  541. }
  542. //If we don't need to receive post data, we can send the response now.
  543. if (conn->post->len == 0) {
  544. httpdProcessRequest(conn);
  545. }
  546. }
  547. }
  548. else if (conn->post->len != 0) {
  549. //This byte is a POST byte.
  550. conn->post->buff[conn->post->buffLen++] = data[x];
  551. conn->post->received++;
  552. if (conn->post->buffLen >= conn->post->buffSize || conn->post->received == conn->post->len) {
  553. //Received a chunk of post data
  554. conn->post->buff[conn->post->buffLen] = 0; //zero-terminate, in case the cgi handler knows it can use strings
  555. //Send the response.
  556. httpdProcessRequest(conn);
  557. conn->post->buffLen = 0;
  558. }
  559. }
  560. }
  561. }
  562. static void ICACHE_FLASH_ATTR httpdDisconCb(void *arg) {
  563. debugConn(arg, "httpdDisconCb");
  564. struct espconn* pCon = (struct espconn *)arg;
  565. HttpdConnData *conn = (HttpdConnData *)pCon->reverse;
  566. if (conn == NULL) return; // aborted connection
  567. httpdRetireConn(conn);
  568. }
  569. // Callback indicating a failure in the connection. "Recon" is probably intended in the sense
  570. // of "you need to reconnect". Sigh... Note that there is no DisconCb after ReconCb
  571. static void ICACHE_FLASH_ATTR httpdReconCb(void *arg, sint8 err) {
  572. debugConn(arg, "httpdReconCb");
  573. struct espconn* pCon = (struct espconn *)arg;
  574. HttpdConnData *conn = (HttpdConnData *)pCon->reverse;
  575. if (conn == NULL) return; // aborted connection
  576. DBG("%s***** reset, err=%d\n", connStr, err);
  577. httpdRetireConn(conn);
  578. }
  579. static void ICACHE_FLASH_ATTR httpdConnectCb(void *arg) {
  580. debugConn(arg, "httpdConnectCb");
  581. struct espconn *conn = arg;
  582. // Find empty conndata in pool
  583. int i;
  584. for (i = 0; i<MAX_CONN; i++) if (connData[i].conn == NULL) break;
  585. //DBG("Con req, conn=%p, pool slot %d\n", conn, i);
  586. if (i == MAX_CONN) {
  587. os_printf("%sHTTP: conn pool overflow!\n", connStr);
  588. espconn_disconnect(conn);
  589. return;
  590. }
  591. #if 0
  592. int num = 0;
  593. for (int j = 0; j<MAX_CONN; j++) if (connData[j].conn != NULL) num++;
  594. DBG("%sConnect (%d open)\n", connStr, num + 1);
  595. #endif
  596. connData[i].priv = &connPrivData[i];
  597. connData[i].conn = conn;
  598. conn->reverse = connData+i;
  599. connData[i].priv->headPos = 0;
  600. esp_tcp *tcp = conn->proto.tcp;
  601. os_sprintf(connData[i].priv->from, "%d.%d.%d.%d:%d", tcp->remote_ip[0], tcp->remote_ip[1],
  602. tcp->remote_ip[2], tcp->remote_ip[3], tcp->remote_port);
  603. connData[i].post = &connPostData[i];
  604. connData[i].post->buff = NULL;
  605. connData[i].post->buffLen = 0;
  606. connData[i].post->received = 0;
  607. connData[i].post->len = -1;
  608. connData[i].startTime = system_get_time();
  609. espconn_regist_recvcb(conn, httpdRecvCb);
  610. espconn_regist_reconcb(conn, httpdReconCb);
  611. espconn_regist_disconcb(conn, httpdDisconCb);
  612. espconn_regist_sentcb(conn, httpdSentCb);
  613. espconn_set_opt(conn, ESPCONN_REUSEADDR | ESPCONN_NODELAY);
  614. }
  615. //Httpd initialization routine. Call this to kick off webserver functionality.
  616. void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, char* hostname, int port) {
  617. int i;
  618. for (i = 0; i<MAX_CONN; i++) {
  619. connData[i].conn = NULL;
  620. connData[i].hash = 0;
  621. }
  622. httpdConn.type = ESPCONN_TCP;
  623. httpdConn.state = ESPCONN_NONE;
  624. httpdTcp.local_port = port;
  625. httpdConn.proto.tcp = &httpdTcp;
  626. builtInUrls = fixedUrls;
  627. os_sprintf(metahostname,hostname);
  628. DBG("Httpd init, conn=%p\n", &httpdConn);
  629. espconn_regist_connectcb(&httpdConn, httpdConnectCb);
  630. espconn_accept(&httpdConn);
  631. espconn_tcp_set_max_con_allow(&httpdConn, MAX_CONN);
  632. }
  633. // looks up connection handle based on ip / port
  634. HttpdConnData * ICACHE_FLASH_ATTR httpdLookUpConn(uint8_t * ip, int port) {
  635. int i;
  636. for (i = 0; i<MAX_CONN; i++)
  637. {
  638. HttpdConnData *conn = connData+i;
  639. if (conn->conn == NULL)
  640. continue;
  641. if (conn->cgi == NULL)
  642. continue;
  643. if (conn->conn->proto.tcp->remote_port != port )
  644. continue;
  645. if (os_memcmp(conn->conn->proto.tcp->remote_ip, ip, 4) != 0)
  646. continue;
  647. return conn;
  648. }
  649. return NULL;
  650. }
  651. // this method is used for setting the response of a CGI handler outside of the HTTP callback
  652. // this method useful at the following scenario:
  653. // Browser -> CGI handler -> MCU request
  654. // MCU response -> CGI handler -> browser
  655. // when MCU response arrives, the handler looks up connection based on ip/port and call httpdSetCGIResponse with the data to transmit
  656. int ICACHE_FLASH_ATTR httpdSetCGIResponse(HttpdConnData * conn, void * response) {
  657. char sendBuff[MAX_SENDBUFF_LEN];
  658. conn->priv->sendBuff = sendBuff;
  659. conn->priv->sendBuffLen = 0;
  660. conn->cgiResponse = response;
  661. httpdProcessRequest(conn);
  662. conn->cgiResponse = NULL;
  663. return HTTPD_CGI_DONE;
  664. }