Exemple #1
0
/*********************************************************************
 * 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;
}
Exemple #2
0
/*********************************************************************
 * 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;
}
Exemple #3
0
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;

}
Exemple #4
0
//-------------------------------------------------------------------------------
// 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;
}
Exemple #5
0
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);
}
Exemple #7
0
//=============================================================================
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; // не докачивать, ошибка или закрытие
}
Exemple #8
0
/*********************************************************************
 * 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);

}
Exemple #9
0
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;
	
}