/********************************************************************* * Function: static BOOL HTTPSendFile(void) * * PreCondition: curHTTP.file and curHTTP.offsets have both been * opened for reading. * * Input: None * * Output: TRUE if EOF was reached and reading is done * FALSE if more data remains * * Side Effects: None * * Overview: This function serves the next chunk of curHTTP's * file, up to a) available TX FIFO or b) up to * the next recorded callback index, whichever comes * first. * * Note: None ********************************************************************/ static BOOL HTTPSendFile(void) { WORD numBytes, len; BYTE c, data[64]; // Determine how many bytes we can read right now numBytes = mMIN(TCPIsPutReady(sktHTTP), curHTTP.nextCallback - curHTTP.byteCount); // Get/put as many bytes as possible curHTTP.byteCount += numBytes; while(numBytes > 0) { len = MPFSGetArray(curHTTP.file, data, mMIN(numBytes, 64)); if(len == 0) return TRUE; else TCPPutArray(sktHTTP, data, len); numBytes -= len; } // Check if a callback index was reached if(curHTTP.byteCount == curHTTP.nextCallback) { // Update the state machine smHTTP = SM_HTTP_SEND_FROM_CALLBACK; curHTTP.callbackPos = 0; // Read past the variable name and close the MPFS MPFSGet(curHTTP.file, NULL); do { if(!MPFSGet(curHTTP.file, &c)) break; curHTTP.byteCount++; } while(c != '~'); curHTTP.byteCount++; // Read in the callback address and next offset MPFSGetLong(curHTTP.offsets, &(curHTTP.callbackID)); if(!MPFSGetLong(curHTTP.offsets, &(curHTTP.nextCallback))) { curHTTP.nextCallback = 0xffffffff; MPFSClose(curHTTP.offsets); curHTTP.offsets = MPFS_INVALID_HANDLE; } } // We are not done sending a file yet... return FALSE; }
/********************************************************************* * Function: DWORD HTTPIncFile(TCP_SOCKET skt, * DWORD callbackPos, ROM BYTE* file) * * PreCondition: curHTTP is loaded * * Input: None * * Output: Updates curHTTP.callbackPos * * Side Effects: None * * Overview: Writes an MPFS file to the socket and returns * * Note: Provides rudimentary include support for dynamic * files which allows them to use header, footer, * and/or menu inclusion files rather than * duplicating code across all files. ********************************************************************/ void HTTPIncFile(ROM BYTE* file) { WORD count, len; BYTE data[64]; MPFS_HANDLE fp; // Check if this is a first round call if(curHTTP.callbackPos == 0x00) {// On initial call, open the file and save its ID fp = MPFSOpenROM(file); if(fp == MPFS_INVALID_HANDLE) {// File not found, so abort return; } ((DWORD_VAL*)&curHTTP.callbackPos)->w[0] = MPFSGetID(fp); } else {// The file was already opened, so load up it's ID and seek fp = MPFSOpenID(((DWORD_VAL*)&curHTTP.callbackPos)->w[0]); if(fp == MPFS_INVALID_HANDLE) {// File not found, so abort curHTTP.callbackPos = 0x00; return; } MPFSSeek(fp, ((DWORD_VAL*)&curHTTP.callbackPos)->w[1], MPFS_SEEK_FORWARD); } // Get/put as many bytes as possible count = TCPIsPutReady(sktHTTP); while(count > 0) { len = MPFSGetArray(fp, data, mMIN(count, 64)); if(len == 0) {// If no bytes were read, an EOF was reached MPFSClose(fp); curHTTP.callbackPos = 0x00; return; } else {// Write the bytes to the socket TCPPutArray(sktHTTP, data, len); count -= len; } } // Save the new address and close the file ((DWORD_VAL*)&curHTTP.callbackPos)->w[1] = MPFSTell(fp); MPFSClose(fp); return; }
static void HTTPHeaderParseCookie(void) { WORD lenA, lenB; // Verify there's enough space lenB = TCPFindROMArray(sktHTTP, HTTP_CRLF, HTTP_CRLF_LEN, 0, FALSE); if(lenB >= curHTTP.data + HTTP_MAX_DATA_LEN - curHTTP.ptrData - 2) {// If not, overflow curHTTP.httpStatus = HTTP_OVERFLOW; smHTTP = SM_HTTP_SERVE_HEADERS; return; } // While a CRLF is not immediate, grab a cookie value while(lenB != 0) { // Look for a ';' and use the shorter of that or a CRLF lenA = TCPFind(sktHTTP, ';', 0, FALSE); // Read to the terminator curHTTP.ptrData += TCPGetArray(sktHTTP, curHTTP.ptrData, mMIN(lenA, lenB)); // Insert an & to anticipate another cookie *(curHTTP.ptrData++) = '&'; // If semicolon, trash it and whitespace if(lenA < lenB) { TCPGet(sktHTTP, NULL); while(TCPFind(sktHTTP, ' ', 0, FALSE) == 0) TCPGet(sktHTTP, NULL); } // Find the new distance to the CRLF lenB = TCPFindROMArray(sktHTTP, HTTP_CRLF, HTTP_CRLF_LEN, 0, FALSE); } return; }
//------------------------------------------------------------------------------- // TCP receive response from server //------------------------------------------------------------------------------- err_t ICACHE_FLASH_ATTR tc_recv(TCP_SERV_CONN *ts_conn) { #if DEBUGSOO > 1 tcpsrv_received_data_default(ts_conn); #endif tcpsrv_unrecved_win(ts_conn); uint8 *pstr = ts_conn->pbufi; sint32 len = ts_conn->sizei; #if DEBUGSOO > 4 os_printf("IOT_Rec(%u): %s\n", len, pstr); #endif os_memset(iot_last_status, 0, sizeof(iot_last_status)); os_strncpy(iot_last_status, (char *)pstr, mMIN(sizeof(iot_last_status)-1, len)); // status/error iot_last_status_time = get_sntp_time(); if(len >= sizeof(key_http_ok1) + 3 + sizeof(key_http_ok2)) { if(os_memcmp(pstr, key_http_ok1, sizeof(key_http_ok1)-1) == 0 && os_memcmp(pstr + sizeof(key_http_ok1)-1 + 3, key_http_ok2, sizeof(key_http_ok2)-1) == 0) { // Check - 200 OK? #if DEBUGSOO > 4 os_printf(" - 200\n"); #endif uint8 *nstr = web_strnstr(pstr, "\r\n\r\n", len); // find body if(nstr != NULL) { pstr = nstr + 4; // body start len -= nstr - pstr; // body size uint8 *nstr = web_strnstr(pstr, "\r\n", len); // find next delimiter if(nstr != NULL) *nstr = '\0'; if(ahextoul(pstr)) { // not 0 = OK tc_init_flg &= ~TC_RUNNING; // clear run flag iot_data_processing->last_run = system_get_time(); #if DEBUGSOO > 4 os_printf("Ok!!!\n"); #endif } } } } //ts_conn->flag.rx_null = 1; // stop receiving data tc_close(); return ERR_OK; }
static void HTTPHeaderParseAuthorization(void) { WORD len; BYTE buf[40]; BYTE *ptrBuf; // If auth processing is not required, return if(curHTTP.isAuthorized & 0x80) return; // Clear the auth type ("BASIC ") TCPGetArray(sktHTTP, NULL, 6); // Find the terminating CRLF and make sure it's a multiple of four len = TCPFindROMArray(sktHTTP, HTTP_CRLF, HTTP_CRLF_LEN, 0, FALSE); len += 3; len &= 0xfc; len = mMIN(len, sizeof(buf)-4); // Read in 4 bytes at a time and decode (slower, but saves RAM) for(ptrBuf = buf; len > 0; len-=4, ptrBuf+=3) { TCPGetArray(sktHTTP, ptrBuf, 4); Base64Decode(ptrBuf, 4, ptrBuf, 3); } // Null terminate both, and make sure there's at least two terminators *ptrBuf = '\0'; for(len = 0, ptrBuf = buf; len < sizeof(buf); len++, ptrBuf++) if(*ptrBuf == ':') break; *(ptrBuf++) = '\0'; // Verify credentials curHTTP.isAuthorized = HTTPAuthenticate(buf, ptrBuf, NULL); return; }
/****************************************************************************** * FunctionName : udp_test_port_recv * Returns : none *******************************************************************************/ LOCAL void ICACHE_FLASH_ATTR udp_test_port_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, ip_addr_t *addr, u16_t port) { uint8 usrdata[32]; if (p == NULL) return; if(p->tot_len < 2) { pbuf_free(p); return; } uint16 length = mMIN(p->tot_len, sizeof(usrdata)-1); #if DEBUGSOO > 0 os_printf("udp " IPSTR ":%u [%d]\n", IP2STR(addr), port, p->tot_len); #endif length = pbuf_copy_partial(p, usrdata, length, 0); pbuf_free(p); uint8 *pudpbuf = (uint8 *)os_zalloc(udpbufsize+1); if(pudpbuf == NULL) return; uint16 udpbuflen = 0; int x = 0; if(length>2) x = atoi((char *)&usrdata[2]); if ((length>1)&&(usrdata[1]=='?')) switch(usrdata[0]) { case 'M': system_print_meminfo(); case 'A': { udp_puts("\nChip_id: %08x Flash_id: %08x\nsys_time:%08x ADC:%d\n", system_get_chip_id(), spi_flash_get_id(), system_get_time(), system_adc_read()); struct softap_config wiconfig; wifi_softap_get_config(&wiconfig); udp_puts("OPMode:%u SSID:'%s' Pwd:'%s' Ch:%u Authmode:%u MaxCon:%u Phu:%u ACon:%u\n", wifi_get_opmode(), wiconfig.ssid, wiconfig.password, wiconfig.channel, wiconfig.authmode, wiconfig.max_connection, wifi_get_phy_mode(), wifi_station_get_auto_connect()); udp_puts("Connect status:%u\n", wifi_station_get_connect_status()); }; case 'I': udp_puts("heapsize: %d\n", system_get_free_heap_size() + udpbufsize); udpbuflen += print_udp_psc(pudpbuf+udpbuflen, udpbufsize-udpbuflen); udpbuflen += print_tcp_psc(pudpbuf+udpbuflen, udpbufsize-udpbuflen); udpbuflen += chow_tcp_connection_info(pudpbuf+udpbuflen, udpbufsize-udpbuflen); break; case 'H': udp_puts("heapsize: %d\n", system_get_free_heap_size() + udpbufsize); break; case 'U': udp_puts("heapsize: %d\n", system_get_free_heap_size() + udpbufsize); udpbuflen += print_udp_psc(pudpbuf+udpbuflen, udpbufsize-udpbuflen); break; case 'T': udp_puts("heapsize: %d\n", system_get_free_heap_size() + udpbufsize); udpbuflen += print_tcp_psc(pudpbuf+udpbuflen, udpbufsize-udpbuflen); break; #ifdef USE_SRV_WEB_PORT case 'S': udp_puts("heapsize: %d\n", system_get_free_heap_size() + udpbufsize); udpbuflen += chow_tcp_connection_info(pudpbuf+udpbuflen, udpbufsize-udpbuflen); break; #endif case 'R': system_restart(); break; case 'P': udp_puts("system_set_os_print(%u)\n", x); system_set_os_print(x); break; case 'O': udp_puts("wifi_set_opmode(%u):%u\n", x, wifi_set_opmode(x)); break; case 'B': udp_puts("wifi_station_set_auto_connect(%u):%u\n", x, wifi_station_set_auto_connect(x)); break; case 'D': switch(x) { case 0: udp_puts("wifi_station_dhcpc_start:%u\n", wifi_station_dhcpc_start()); break; case 1: udp_puts("wifi_station_dhcpc_stop:%u\n", wifi_station_dhcpc_stop()); break; case 2: udp_puts("wifi_softap_dhcps_start:%u\n",wifi_softap_dhcps_start()); break; case 3: udp_puts("wifi_softap_dhcps_stop:%u\n", wifi_softap_dhcps_stop()); break; default: udp_puts("D(%u)?\n", x); } break; case 'F': if(flashchip != NULL) { udp_puts("FlashID: 0x%08x\nChip size: %d\nBlock size: %d\nSector size: %d\nPage size: %d\nStatus mask: 0x%08x\n", flashchip->deviceId, flashchip->chip_size, flashchip->block_size, flashchip->sector_size, flashchip->page_size, flashchip->status_mask ); udp_puts("Real Flash size: %u\n", spi_flash_real_size()); } else udp_puts("Unknown Flash type!\n"); break; case 'E': udp_puts("wifi_set_sleep_type(%d):%u\n", x, wifi_set_sleep_type(x)); break; case 'G': udp_puts("g_ic = %p\n", &g_ic); break; default: udp_puts("???\n"); } if(udpbuflen) { struct pbuf *z = pbuf_alloc(PBUF_TRANSPORT, udpbuflen, PBUF_RAM); if(z != NULL) { err_t err = pbuf_take(z, pudpbuf, udpbuflen); os_free(pudpbuf); if(err == ERR_OK) { udp_sendto(upcb, z, addr, port); } pbuf_free(z); return; } } os_free(pudpbuf); }
//============================================================================= bool ICACHE_FLASH_ATTR websock_rx_data(TCP_SERV_CONN *ts_conn) { // HTTP_CONN *CurHTTP; WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; if(web_conn == NULL) return false; WS_FRSTAT *ws = &web_conn->ws; uint16 len; uint8 *pstr; #if DEBUGSOO > 3 os_printf("ws_rx[%u]%u ", ts_conn->sizei, ts_conn->cntri); #endif if(ts_conn->sizei == 0) return true; // докачивать tcpsrv_unrecved_win(ts_conn); if((ws->flg & WS_FLG_CLOSE) != 0) { // убить буфер ts_conn->pbufi, конец давно :) web_feee_bufi(ts_conn); SetSCB(SCB_DISCONNECT); return false; } if(ts_conn->sizei > MAX_RX_BUF_SIZE) { #if DEBUGSOO > 0 os_printf("ws:rxbuf_full! "); #endif // убить буфер ts_conn->pbufi и ответить ошибкой WS_CLOSE_UNEXPECTED_ERROR web_feee_bufi(ts_conn); websock_tx_close_err(ts_conn, WS_CLOSE_MESSAGE_TOO_BIG); // WS_CLOSE_UNEXPECTED_ERROR); SetSCB(SCB_DISCONNECT); return false; } pstr = ts_conn->pbufi;// + ts_conn->cntri; len = ts_conn->sizei;// - ts_conn->cntri; while(ts_conn->cntri < ts_conn->sizei || (ws->flg & WS_FLG_FIN) != 0) { pstr = ts_conn->pbufi;// + ts_conn->cntri; len = ts_conn->sizei;// - ts_conn->cntri; if((ws->flg & WS_FLG_FIN) != 0 // обработка || ws->frame_len > ws->cur_len) { ws->flg &= ~WS_FLG_FIN; len = mMIN(ws->frame_len - ws->cur_len, mMIN(MAX_WS_DATA_BLK_SIZE, len)); // размаскировать if((ws->flg & WS_FLG_MASK) != 0) WebsocketMask(ws, pstr, len); #if DEBUGSOO > 3 os_printf("wsfr[%u]blk[%u]at:%u ", ws->frame_len, len, ws->cur_len); #endif switch(ws->status) { case sw_frs_binary: #if DEBUGSOO > 1 os_printf("ws:bin "); #endif if(ws->frame_len != 0) { // пока просто эхо uint32 opcode = WS_OPCODE_BINARY; if(ws->cur_len != 0) opcode = WS_OPCODE_CONTINUE; if(ws->frame_len == ws->cur_len + len) opcode |= WS_FRAGMENT_FIN; if(websock_tx_frame(ts_conn, opcode, pstr, len) != ERR_OK) { return false; // не докачивать, ошибка или закрытие } } ws->cur_len += len; ts_conn->cntri += len; break; case sw_frs_text: #if DEBUGSOO > 1 os_printf("ws:txt "); #if DEBUGSOO > 2 if(ws->frame_len != 0) { uint8 tt = pstr[len]; pstr[len] = 0; os_printf("'%s' ", pstr); pstr[len] = tt; } #endif #endif if(ws->frame_len == ws->cur_len + len && ws->frame_len != 0) { // полное соо web_conn->msgbufsize = tcp_sndbuf(ts_conn->pcb); // сколько можем выввести сейчас? if (web_conn->msgbufsize < MIN_SEND_SIZE) { #if DEBUGSOO > 0 os_printf("ws:sndbuf=%u! ", web_conn->msgbufsize); #endif websock_tx_close_err(ts_conn, WS_CLOSE_UNEXPECTED_ERROR); SetSCB(SCB_FCLOSE|SCB_DISCONNECT); return false; } if(ws->frame_len == (sizeof(txt_wsping)-1) && rom_xstrcmp(pstr, txt_wsping) != 0){ copy_s4d1(pstr, (void *)txt_wspong, sizeof(txt_wspong) - 1); if(websock_tx_frame(ts_conn, WS_OPCODE_TEXT | WS_FRAGMENT_FIN, pstr, sizeof(txt_wspong) - 1) != ERR_OK) { return false; // не докачивать, ошибка или закрытие } } else { web_conn->msgbuf = (uint8 *) os_malloc(web_conn->msgbufsize); if (web_conn->msgbuf == NULL) { #if DEBUGSOO > 0 os_printf("ws:mem!\n"); #endif websock_tx_close_err(ts_conn, WS_CLOSE_UNEXPECTED_ERROR); SetSCB(SCB_FCLOSE|SCB_DISCONNECT); return false; }; web_conn->msgbuflen = 0; uint32 opcode; if(CheckSCB(SCB_RETRYCB)) { // повторный callback? да if(web_conn->func_web_cb != NULL) web_conn->func_web_cb(ts_conn); if(!CheckSCB(SCB_RETRYCB)) { ClrSCB(SCB_FCLOSE | SCB_DISCONNECT); opcode = WS_OPCODE_CONTINUE | WS_FRAGMENT_FIN; } else opcode = WS_OPCODE_CONTINUE; } else { pstr[len] = '\0'; uint8 *vstr = os_strchr(pstr, '='); if(vstr != NULL) { *vstr++ = '\0'; web_int_vars(ts_conn, pstr, vstr); } else { web_conn->msgbuf[0] = 0; web_int_callback(ts_conn, pstr); } if(CheckSCB(SCB_RETRYCB)) opcode = WS_OPCODE_TEXT; else { ClrSCB(SCB_FCLOSE | SCB_DISCONNECT); opcode = WS_OPCODE_TEXT | WS_FRAGMENT_FIN; } } if(web_conn->msgbuflen != 0) { if(websock_tx_frame(ts_conn, opcode, web_conn->msgbuf, web_conn->msgbuflen) != ERR_OK) { os_free(web_conn->msgbuf); web_conn->msgbuf = NULL; return false; // не докачивать, ошибка или закрытие } } os_free(web_conn->msgbuf); web_conn->msgbuf = NULL; if(CheckSCB(SCB_RETRYCB)) return false; } } /* if(0) { uint32 opcode = WS_OPCODE_TEXT; if(ws->cur_len != 0) opcode = WS_OPCODE_CONTINUE; if(ws->frame_len == ws->cur_len + len) opcode |= WS_FRAGMENT_FIN; if(websock_tx_frame(ts_conn, opcode, pstr, len) != ERR_OK) { return false; // не докачивать, ошибка или закрытие } } */ ws->cur_len += len; ts_conn->cntri += len; return true; // докачивать // break; // break; case sw_frs_ping: #if DEBUGSOO > 1 os_printf("ws:ping "); #endif { uint32 opcode = WS_OPCODE_PONG; if(ws->cur_len != 0) opcode = WS_OPCODE_CONTINUE; if(ws->frame_len == ws->cur_len + len) opcode |= WS_FRAGMENT_FIN; if(websock_tx_frame(ts_conn, opcode, pstr, len) != ERR_OK) { return false; // не докачивать, ошибка или закрытие } } ws->cur_len += len; ts_conn->cntri += len; return true; // докачивать // break; case sw_frs_pong: #if DEBUGSOO > 1 os_printf("ws:pong "); #endif ws->cur_len += len; ts_conn->cntri += len; break; // return true; case sw_frs_close: #if DEBUGSOO > 1 os_printf("ws:close "); #endif // if((ws->flg & WS_FLG_CLOSE) == 0) { { if(len >= 2) { uint32 close_code = (pstr[0]<<8) | pstr[1]; #if DEBUGSOO > 1 os_printf("code:%d ", close_code); #endif if(close_code == WS_CLOSE_NORMAL) websock_tx_close_err(ts_conn, WS_CLOSE_NORMAL); // else websock_tx_frame(ts_conn, WS_OPCODE_CLOSE | WS_FRAGMENT_FIN, NULL, 0); } else { websock_tx_close_err(ts_conn, WS_CLOSE_NORMAL); // websock_tx_frame(ts_conn, WS_OPCODE_CLOSE | WS_FRAGMENT_FIN, NULL, 0); } } ts_conn->flag.pcb_time_wait_free = 1; SetSCB(SCB_DISCONNECT); // ts_conn->cntri = ts_conn->sizei; /* ws->cur_len += len; ts_conn->cntri += len; */ return false; default: #if DEBUGSOO > 0 os_printf("ws:f?! "); #endif websock_tx_close_err(ts_conn, WS_CLOSE_UNEXPECTED_ERROR); SetSCB(SCB_DISCONNECT); // ts_conn->cntri = ts_conn->sizei; return false; } } else if(ws->cur_len >= ws->frame_len) { // прием и разбор нового фрейма if((ws->flg & WS_FLG_FIN) != 0) { // обработка #if DEBUGSOO > 3 os_printf("ws_rx:fin=%u ", ws->cur_len); #endif } else { uint32 ret = WebsocketHead(ws, pstr, len); if(ret >= WS_CLOSE_NORMAL) { // error или close #if DEBUGSOO > 0 os_printf("ws:txerr=%u ", ret); #endif websock_tx_close_err(ts_conn, ret); // ts_conn->cntri = ts_conn->sizei; // убить буфер ts_conn->pbufi return false; // error } else if(ret == 0) { #if DEBUGSOO > 3 os_printf("ws_rx... "); #endif return true; // докачивать } ts_conn->cntri += ws->head_len; // вычесть заголовок /* switch(ws->status) { case sw_frs_binary: break; case sw_frs_text: if(ws->frame_len > MAX_RX_BUF_SIZE) { websock_tx_close_err(ts_conn, WS_CLOSE_MESSAGE_TOO_BIG); return false; } break; } */ } } #if DEBUGSOO > 3 os_printf("trim%u-%u ", ts_conn->sizei, ts_conn->sizei - ts_conn->cntri ); #endif if(!web_trim_bufi(ts_conn, &ts_conn->pbufi[ts_conn->cntri], ts_conn->sizei - ts_conn->cntri)) { #if DEBUGSOO > 0 os_printf("ws:trim_err! "); #endif // убить буфер ts_conn->pbufi и ответить ошибкой WS_CLOSE_UNEXPECTED_ERROR websock_tx_close_err(ts_conn, WS_CLOSE_UNEXPECTED_ERROR); SetSCB(SCB_DISCONNECT); // ts_conn->cntri = ts_conn->sizei; return false; }; } return false; // не докачивать, ошибка или закрытие }
/********************************************************************* * Function: static void HTTPProcess(void) * * PreCondition: HTTPInit() called and curHTTP loaded * * Input: None * * Output: None * * Side Effects: None * * Overview: Serves the current HTTP connection in curHTTP * * Note: None ********************************************************************/ static void HTTPProcess(void) { WORD lenA, lenB; BYTE c, i; BOOL isDone; BYTE *ext; BYTE buffer[HTTP_MAX_HEADER_LEN+1]; do { isDone = TRUE; // If a socket is disconnected at any time // forget about it and return to idle state. if(TCPWasReset(sktHTTP)) { smHTTP = SM_HTTP_IDLE; // Make sure any opened files are closed if(curHTTP.file != MPFS_INVALID_HANDLE) { MPFSClose(curHTTP.file); curHTTP.file = MPFS_INVALID_HANDLE; } if(curHTTP.offsets != MPFS_INVALID_HANDLE) { MPFSClose(curHTTP.offsets); curHTTP.offsets = MPFS_INVALID_HANDLE; } // Adjust the TCP FIFOs for optimal reception of // the next HTTP request from the browser TCPAdjustFIFOSize(sktHTTP, 1, 0, TCP_ADJUST_GIVE_REST_TO_RX | TCP_ADJUST_PRESERVE_RX); } switch(smHTTP) { case SM_HTTP_IDLE: // Check how much data is waiting lenA = TCPIsGetReady(sktHTTP); // If a connection has been made, then process the request if(lenA) {// Clear out state info and move to next state curHTTP.ptrData = curHTTP.data; smHTTP = SM_HTTP_PARSE_REQUEST; curHTTP.isAuthorized = 0xff; curHTTP.hasArgs = FALSE; curHTTP.callbackID = TickGet() + HTTP_TIMEOUT*TICK_SECOND; curHTTP.callbackPos = 0xffffffff; curHTTP.byteCount = 0; } // In all cases, we break // For new connections, this waits for the buffer to fill break; case SM_HTTP_PARSE_REQUEST: // Verify the entire first line is in the FIFO if(TCPFind(sktHTTP, '\n', 0, FALSE) == 0xffff) {// First line isn't here yet if(TCPGetRxFIFOFree(sktHTTP) == 0) {// If the FIFO is full, we overflowed curHTTP.httpStatus = HTTP_OVERFLOW; smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; } if(TickGet() > curHTTP.callbackID) {// A timeout has occurred TCPDisconnect(sktHTTP); smHTTP = SM_HTTP_DISCONNECT; isDone = FALSE; } break; } // Reset the watchdog timer curHTTP.callbackID = TickGet() + HTTP_TIMEOUT*TICK_SECOND; // Determine the request method lenA = TCPFind(sktHTTP, ' ', 0, FALSE); if(lenA > 5) lenA = 5; TCPGetArray(sktHTTP, curHTTP.data, lenA+1); if ( memcmppgm2ram(curHTTP.data, (ROM void*)"GET", 3) == 0) curHTTP.httpStatus = HTTP_GET; #if defined(HTTP_USE_POST) else if ( memcmppgm2ram(curHTTP.data, (ROM void*)"POST", 4) == 0) curHTTP.httpStatus = HTTP_POST; #endif else {// Unrecognized method, so return not implemented curHTTP.httpStatus = HTTP_NOT_IMPLEMENTED; smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; break; } // Find end of filename lenA = TCPFind(sktHTTP, ' ', 0, FALSE); lenB = TCPFindEx(sktHTTP, '?', 0, lenA, FALSE); lenA = mMIN(lenA, lenB); // If the file name is too long, then reject the request if(lenA > HTTP_MAX_DATA_LEN - HTTP_DEFAULT_LEN - 1) { curHTTP.httpStatus = HTTP_OVERFLOW; smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; break; } // Read in the filename and decode lenB = TCPGetArray(sktHTTP, curHTTP.data, lenA); curHTTP.data[lenB] = '\0'; HTTPURLDecode(curHTTP.data); // Check if this is an MPFS Upload #if defined(HTTP_MPFS_UPLOAD) if(memcmppgm2ram(&curHTTP.data[1], HTTP_MPFS_UPLOAD, strlenpgm(HTTP_MPFS_UPLOAD)) == 0) {// Read remainder of line, and bypass all file opening, etc. #if defined(HTTP_USE_AUTHENTICATION) curHTTP.isAuthorized = HTTPAuthenticate(NULL, NULL, &curHTTP.data[1]); #endif if(curHTTP.httpStatus == HTTP_GET) curHTTP.httpStatus = HTTP_MPFS_FORM; else curHTTP.httpStatus = HTTP_MPFS_UP; smHTTP = SM_HTTP_PARSE_HEADERS; isDone = FALSE; break; } #endif // If the last character is a not a directory delimiter, then try to open the file // String starts at 2nd character, because the first is always a '/' if(curHTTP.data[lenB-1] != '/') curHTTP.file = MPFSOpen(&curHTTP.data[1]); // If the open fails, then add our default name and try again if(curHTTP.file == MPFS_INVALID_HANDLE) { // Add the directory delimiter if needed if(curHTTP.data[lenB-1] != '/') curHTTP.data[lenB++] = '/'; // Add our default file name // If this is a loopback, then it's an SSL connection if(TCPIsLoopback(sktHTTP)) { strcpypgm2ram((void*)&curHTTP.data[lenB], HTTPS_DEFAULT_FILE); lenB += strlenpgm(HTTPS_DEFAULT_FILE); } else { strcpypgm2ram((void*)&curHTTP.data[lenB], HTTP_DEFAULT_FILE); lenB += strlenpgm(HTTP_DEFAULT_FILE); } // Try to open again curHTTP.file = MPFSOpen(&curHTTP.data[1]); } // Find the extension in the filename for(ext = curHTTP.data + lenB-1; ext != curHTTP.data; ext--) if(*ext == '.') break; // Compare to known extensions to determine Content-Type ext++; for(curHTTP.fileType = HTTP_TXT; curHTTP.fileType < HTTP_UNKNOWN; curHTTP.fileType++) if(!stricmppgm2ram(ext, (ROM void*)httpFileExtensions[curHTTP.fileType])) break; // Perform first round authentication (pass file name only) #if defined(HTTP_USE_AUTHENTICATION) curHTTP.isAuthorized = HTTPAuthenticate(NULL, NULL, &curHTTP.data[1]); #endif // If the file was found, see if it has an index if(curHTTP.file != MPFS_INVALID_HANDLE && (MPFSGetFlags(curHTTP.file) & MPFS2_FLAG_HASINDEX) ) { curHTTP.data[lenB-1] = '#'; curHTTP.offsets = MPFSOpen(&curHTTP.data[1]); } // Read GET args, up to buffer size - 1 lenA = TCPFind(sktHTTP, ' ', 0, FALSE); if(lenA != 0) { curHTTP.hasArgs = TRUE; // Trash the '?' TCPGet(sktHTTP, &c); // Verify there's enough space lenA--; if(lenA >= HTTP_MAX_DATA_LEN - 2) { curHTTP.httpStatus = HTTP_OVERFLOW; smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; break; } // Read in the arguments and '&'-terminate in anticipation of cookies curHTTP.ptrData += TCPGetArray(sktHTTP, curHTTP.data, lenA); *(curHTTP.ptrData++) = '&'; } // Clear the rest of the line lenA = TCPFind(sktHTTP, '\n', 0, FALSE); TCPGetArray(sktHTTP, NULL, lenA + 1); // Move to parsing the headers smHTTP = SM_HTTP_PARSE_HEADERS; // No break, continue to parsing headers case SM_HTTP_PARSE_HEADERS: // Loop over all the headers while(1) { // Make sure entire line is in the FIFO lenA = TCPFind(sktHTTP, '\n', 0, FALSE); if(lenA == 0xffff) {// If not, make sure we can receive more data if(TCPGetRxFIFOFree(sktHTTP) == 0) {// Overflow curHTTP.httpStatus = HTTP_OVERFLOW; smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; } if(TickGet() > curHTTP.callbackID) {// A timeout has occured TCPDisconnect(sktHTTP); smHTTP = SM_HTTP_DISCONNECT; isDone = FALSE; } break; } // Reset the watchdog timer curHTTP.callbackID = TickGet() + HTTP_TIMEOUT*TICK_SECOND; // If a CRLF is immediate, then headers are done if(lenA == 1) {// Remove the CRLF and move to next state TCPGetArray(sktHTTP, NULL, 2); smHTTP = SM_HTTP_AUTHENTICATE; isDone = FALSE; break; } // Find the header name, and use isDone as a flag to indicate a match lenB = TCPFindEx(sktHTTP, ':', 0, lenA, FALSE) + 2; isDone = FALSE; // If name is too long or this line isn't a header, ignore it if(lenB > sizeof(buffer)) { TCPGetArray(sktHTTP, NULL, lenA+1); continue; } // Read in the header name TCPGetArray(sktHTTP, buffer, lenB); buffer[lenB-1] = '\0'; lenA -= lenB; // Compare header read to ones we're interested in for(i = 0; i < HTTP_NUM_HEADERS; i++) { if(strcmppgm2ram((char*)buffer, (ROM char *)HTTPRequestHeaders[i]) == 0) {// Parse the header and stop the loop HTTPHeaderParseLookup(i); isDone = TRUE; break; } } // Clear the rest of the line, and call the loop again if(isDone) {// We already know how much to remove unless a header was found lenA = TCPFind(sktHTTP, '\n', 0, FALSE); } TCPGetArray(sktHTTP, NULL, lenA+1); } break; case SM_HTTP_AUTHENTICATE: #if defined(HTTP_USE_AUTHENTICATION) // Check current authorization state if(curHTTP.isAuthorized < 0x80) {// 401 error curHTTP.httpStatus = HTTP_UNAUTHORIZED; smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; #if defined(HTTP_NO_AUTH_WITHOUT_SSL) if(!TCPIsLoopback(sktHTTP)) curHTTP.httpStatus = HTTP_SSL_REQUIRED; #endif break; } #endif // Parse the args string *curHTTP.ptrData = '\0'; curHTTP.ptrData = HTTPURLDecode(curHTTP.data); // If this is an MPFS upload form request, bypass to headers #if defined(HTTP_MPFS_UPLOAD) if(curHTTP.httpStatus == HTTP_MPFS_FORM) { smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; break; } #endif // Move on to GET args, unless there are none smHTTP = SM_HTTP_PROCESS_GET; if(!curHTTP.hasArgs) smHTTP = SM_HTTP_PROCESS_POST; isDone = FALSE; curHTTP.hasArgs = FALSE; break; case SM_HTTP_PROCESS_GET: // Run the application callback HTTPExecuteGet() if(HTTPExecuteGet() == HTTP_IO_WAITING) {// If waiting for asynchronous process, return to main app break; } // Move on to POST data smHTTP = SM_HTTP_PROCESS_POST; case SM_HTTP_PROCESS_POST: #if defined(HTTP_USE_POST) // See if we have any new data if(TCPIsGetReady(sktHTTP) == curHTTP.callbackPos) { if(TickGet() > curHTTP.callbackID) {// If a timeout has occured, disconnect TCPDisconnect(sktHTTP); smHTTP = SM_HTTP_DISCONNECT; isDone = FALSE; break; } } if(curHTTP.httpStatus == HTTP_POST #if defined(HTTP_MPFS_UPLOAD) || (curHTTP.httpStatus >= HTTP_MPFS_UP && curHTTP.httpStatus <= HTTP_MPFS_ERROR) #endif ) { // Run the application callback HTTPExecutePost() #if defined(HTTP_MPFS_UPLOAD) if(curHTTP.httpStatus >= HTTP_MPFS_UP && curHTTP.httpStatus <= HTTP_MPFS_ERROR) { c = HTTPMPFSUpload(); if(c == HTTP_IO_DONE) { smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; break; } } else #endif c = HTTPExecutePost(); // If waiting for asynchronous process, return to main app if(c == HTTP_IO_WAITING) {// return to main app and make sure we don't get stuck by the watchdog curHTTP.callbackPos = TCPIsGetReady(sktHTTP) - 1; break; } else if(c == HTTP_IO_NEED_DATA) {// If waiting for more data curHTTP.callbackPos = TCPIsGetReady(sktHTTP); curHTTP.callbackID = TickGet() + HTTP_TIMEOUT*TICK_SECOND; // If more is expected and space is available, return to main app if(curHTTP.byteCount > 0 && TCPGetRxFIFOFree(sktHTTP) != 0) break; else {// Handle cases where application ran out of data or buffer space curHTTP.httpStatus = HTTP_INTERNAL_SERVER_ERROR; smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; break; } } } #endif // We're done with POST smHTTP = SM_HTTP_PROCESS_REQUEST; // No break, continue to sending request case SM_HTTP_PROCESS_REQUEST: // Check for 404 if(curHTTP.file == MPFS_INVALID_HANDLE) { curHTTP.httpStatus = HTTP_NOT_FOUND; smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; break; } // Set up the dynamic substitutions curHTTP.byteCount = 0; if(curHTTP.offsets == MPFS_INVALID_HANDLE) {// If no index file, then set next offset to huge curHTTP.nextCallback = 0xffffffff; } else {// Read in the next callback index MPFSGetLong(curHTTP.offsets, &(curHTTP.nextCallback)); } // Move to next state smHTTP = SM_HTTP_SERVE_HEADERS; case SM_HTTP_SERVE_HEADERS: // We're in write mode now: // Adjust the TCP FIFOs for optimal transmission of // the HTTP response to the browser TCPAdjustFIFOSize(sktHTTP, 1, 0, TCP_ADJUST_GIVE_REST_TO_TX); // Send headers TCPPutROMString(sktHTTP, (ROM BYTE*)HTTPResponseHeaders[curHTTP.httpStatus]); // If this is a redirect, print the rest of the Location: header if(curHTTP.httpStatus == HTTP_REDIRECT) { TCPPutString(sktHTTP, curHTTP.data); TCPPutROMString(sktHTTP, (ROM BYTE*)"\r\n\r\n304 Redirect: "); TCPPutString(sktHTTP, curHTTP.data); TCPPutROMString(sktHTTP, (ROM BYTE*)HTTP_CRLF); } // If not GET or POST, we're done if(curHTTP.httpStatus != HTTP_GET && curHTTP.httpStatus != HTTP_POST) {// Disconnect smHTTP = SM_HTTP_DISCONNECT; break; } // Output the content type, if known if(curHTTP.fileType != HTTP_UNKNOWN) { TCPPutROMString(sktHTTP, (ROM BYTE*)"Content-Type: "); TCPPutROMString(sktHTTP, (ROM BYTE*)httpContentTypes[curHTTP.fileType]); TCPPutROMString(sktHTTP, HTTP_CRLF); } // Output the gzip encoding header if needed if(MPFSGetFlags(curHTTP.file) & MPFS2_FLAG_ISZIPPED) { TCPPutROMString(sktHTTP, (ROM BYTE*)"Content-Encoding: gzip\r\n"); } // Output the cache-control TCPPutROMString(sktHTTP, (ROM BYTE*)"Cache-Control: "); if(curHTTP.httpStatus == HTTP_POST || curHTTP.nextCallback != 0xffffffff) {// This is a dynamic page or a POST request, so no cache TCPPutROMString(sktHTTP, (ROM BYTE*)"no-cache"); } else {// This is a static page, so save it for the specified amount of time TCPPutROMString(sktHTTP, (ROM BYTE*)"max-age="); TCPPutROMString(sktHTTP, (ROM BYTE*)HTTP_CACHE_LEN); } TCPPutROMString(sktHTTP, HTTP_CRLF); // Check if we should output cookies if(curHTTP.hasArgs) smHTTP = SM_HTTP_SERVE_COOKIES; else {// Terminate the headers TCPPutROMString(sktHTTP, HTTP_CRLF); smHTTP = SM_HTTP_SERVE_BODY; } // Move to next stage isDone = FALSE; break; case SM_HTTP_SERVE_COOKIES: #if defined(HTTP_USE_COOKIES) // If the TX FIFO runs out of space, the client will never get CRLFCRLF // Avoid writing huge cookies - keep it under a hundred bytes max // Write cookies one at a time as space permits for(curHTTP.ptrRead = curHTTP.data; curHTTP.hasArgs != 0; curHTTP.hasArgs--) { // Write the header TCPPutROMString(sktHTTP, (ROM BYTE*)"Set-Cookie: "); // Write the name, URL encoded, one character at a time while((c = *(curHTTP.ptrRead++))) { if(c == ' ') TCPPut(sktHTTP, '+'); else if(c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || c > 'z') { TCPPut(sktHTTP, '%'); TCPPut(sktHTTP, btohexa_high(c)); TCPPut(sktHTTP, btohexa_low(c)); } else TCPPut(sktHTTP, c); } TCPPut(sktHTTP, '='); // Write the value, URL encoded, one character at a time while((c = *(curHTTP.ptrRead++))) { if(c == ' ') TCPPut(sktHTTP, '+'); else if(c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || c > 'z') { TCPPut(sktHTTP, '%'); TCPPut(sktHTTP, btohexa_high(c)); TCPPut(sktHTTP, btohexa_low(c)); } else TCPPut(sktHTTP, c); } // Finish the line TCPPutROMString(sktHTTP, HTTP_CRLF); } #endif // We're done, move to next state TCPPutROMString(sktHTTP, HTTP_CRLF); smHTTP = SM_HTTP_SERVE_BODY; case SM_HTTP_SERVE_BODY: isDone = FALSE; // Try to send next packet if(HTTPSendFile()) {// If EOF, then we're done so close and disconnect MPFSClose(curHTTP.file); curHTTP.file = MPFS_INVALID_HANDLE; smHTTP = SM_HTTP_DISCONNECT; isDone = TRUE; } // If the TX FIFO is full, then return to main app loop if(TCPIsPutReady(sktHTTP) == 0) isDone = TRUE; break; case SM_HTTP_SEND_FROM_CALLBACK: isDone = TRUE; // Check that at least the minimum bytes are free if(TCPIsPutReady(sktHTTP) < HTTP_MIN_CALLBACK_FREE) break; // Fill TX FIFO from callback HTTPPrint(curHTTP.callbackID); if(curHTTP.callbackPos == 0) {// Callback finished its output, so move on isDone = FALSE; smHTTP = SM_HTTP_SERVE_BODY; }// Otherwise, callback needs more buffer space, so return and wait break; case SM_HTTP_DISCONNECT: // Loopbacks have no wait state, so all data must be retrieved first if(TCPIsLoopback(sktHTTP) && TCPGetTxFIFOFull(sktHTTP) != 0) break; // Make sure any opened files are closed if(curHTTP.file != MPFS_INVALID_HANDLE) { MPFSClose(curHTTP.file); curHTTP.file = MPFS_INVALID_HANDLE; } if(curHTTP.offsets != MPFS_INVALID_HANDLE) { MPFSClose(curHTTP.offsets); curHTTP.offsets = MPFS_INVALID_HANDLE; } TCPDisconnect(sktHTTP); smHTTP = SM_HTTP_IDLE; break; } } while(!isDone); }
static HTTP_IO_RESULT HTTPMPFSUpload(void) { BYTE c[16]; WORD lenA, lenB; switch(curHTTP.httpStatus) { // New upload, so look for the CRLFCRLF case HTTP_MPFS_UP: lenA = TCPFindROMArray(sktHTTP, (ROM BYTE*)"\r\n\r\n", 4, 0, FALSE); if(lenA != 0xffff) {// Found it, so remove all data up to and including lenA = TCPGetArray(sktHTTP, NULL, lenA); curHTTP.byteCount -= lenA; // Make sure first 6 bytes are also in if(TCPIsGetReady(sktHTTP) < (4 + 6) ) { lenA++; return HTTP_IO_NEED_DATA; } // Make sure it's an MPFS of the correct version lenA = TCPGetArray(sktHTTP, c, 10); curHTTP.byteCount -= lenA; if(memcmppgm2ram(c, (ROM void*)"\r\n\r\nMPFS\x02\x00", 10) == 0) {// Read as Ver 2.0 curHTTP.httpStatus = HTTP_MPFS_OK; // Format MPFS storage and put 6 byte tag curHTTP.file = MPFSFormat(); MPFSPutArray(curHTTP.file, &c[4], 6); } else {// Version is wrong curHTTP.httpStatus = HTTP_MPFS_ERROR; } } else {// Otherwise, remove as much as possible lenA = TCPGetArray(sktHTTP, NULL, TCPIsGetReady(sktHTTP) - 4); curHTTP.byteCount -= lenA; } break; // Received file is invalid case HTTP_MPFS_ERROR: curHTTP.byteCount -= TCPIsGetReady(sktHTTP); TCPDiscard(sktHTTP); if(curHTTP.byteCount < 100 || curHTTP.byteCount > 0x80000000) {// If almost all data was read, or if we overflowed, then return smHTTP = SM_HTTP_SERVE_HEADERS; return HTTP_IO_DONE; } break; // File is verified, so write the data case HTTP_MPFS_OK: // Determine how much to read lenA = TCPIsGetReady(sktHTTP); if(lenA > curHTTP.byteCount) lenA = curHTTP.byteCount; while(lenA > 0) { lenB = TCPGetArray(sktHTTP, c, mMIN(lenA,16)); curHTTP.byteCount -= lenB; lenA -= lenB; MPFSPutArray(curHTTP.file, c, lenB); } // If we've read all the data if(curHTTP.byteCount == 0) { MPFSPutEnd(); smHTTP = SM_HTTP_SERVE_HEADERS; return HTTP_IO_DONE; } } // Ask for more data return HTTP_IO_NEED_DATA; }