/********************************************************************* * 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; }
int FileClose(FILE_HANDLE fh) { #if defined STACK_USE_MPFS2 MPFSClose(fh); #elif defined STACK_USE_MDD return FSfclose(fh); #endif return 0; }
static BOOL Quit(void) { switch(smFTPCommand) { case SM_FTP_CMD_IDLE: #if defined(FTP_PUT_ENABLED) if ( smFTPCommand == SM_FTP_CMD_RECEIVE ) MPFSClose(); #endif if ( FTPDataSocket != INVALID_SOCKET ) { #if defined(FTP_PUT_ENABLED) MPFSClose(); #endif TCPDisconnect(FTPDataSocket); smFTPCommand = SM_FTP_CMD_WAIT; } else goto Quit_Done; break; case SM_FTP_CMD_WAIT: if ( !TCPIsConnected(FTPDataSocket) ) { Quit_Done: FTPResponse = FTP_RESP_QUIT_OK; smFTPCommand = SM_FTP_CMD_WAIT_FOR_DISCONNECT; } break; case SM_FTP_CMD_WAIT_FOR_DISCONNECT: if ( TCPIsPutReady(FTPSocket) ) { if ( TCPIsConnected(FTPSocket) ) TCPDisconnect(FTPSocket); } break; } return FALSE; }
/********************************************************************* * 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; }
////////////////////////////////////////////////////////////////////////////////////////// // NOTE: The following XMODEM code has been upgarded to MPFS2 from MPFS Classic. // Upgrading to HTTP2 and MPFS2 is *strongly* recommended for all new designs. // MPFS2 images can be uploaded directly using the MPFS2.exe tool. ////////////////////////////////////////////////////////////////////////////////////////// static BOOL DownloadMPFS(void) { enum SM_MPFS { SM_MPFS_SOH, SM_MPFS_BLOCK, SM_MPFS_BLOCK_CMP, SM_MPFS_DATA, } state; BYTE c; MPFS_HANDLE handle; BOOL lbDone; BYTE blockLen=0; BYTE lResult; BYTE tempData[XMODEM_BLOCK_LEN]; DWORD lastTick; DWORD currentTick; state = SM_MPFS_SOH; lbDone = FALSE; handle = MPFSFormat(); // Notify the host that we are ready to receive... lastTick = TickGet(); do { currentTick = TickGet(); if ( currentTick - lastTick >= (TICK_SECOND/2) ) { lastTick = TickGet(); while(BusyUART()); WriteUART(XMODEM_NAK); /* * Blink LED to indicate that we are waiting for * host to send the file. */ LED6_IO ^= 1; } } while(!DataRdyUART()); while(!lbDone) { if(DataRdyUART()) { // Toggle LED as we receive the data from host. LED6_IO ^= 1; c = ReadUART(); } else { // Real application should put some timeout to make sure // that we do not wait forever. continue; } switch(state) { default: if ( c == XMODEM_SOH ) { state = SM_MPFS_BLOCK; } else if ( c == XMODEM_EOT ) { // Turn off LED when we are done. LED6_IO = 1; MPFSClose(handle); while(BusyUART()); WriteUART(XMODEM_ACK); lbDone = TRUE; } else { while(BusyUART()); WriteUART(XMODEM_NAK); } break; case SM_MPFS_BLOCK: // We do not use block information. lResult = XMODEM_ACK; blockLen = 0; state = SM_MPFS_BLOCK_CMP; break; case SM_MPFS_BLOCK_CMP: // We do not use 1's comp. block value. state = SM_MPFS_DATA; break; case SM_MPFS_DATA: // Buffer block data until it is over. tempData[blockLen++] = c; if ( blockLen > XMODEM_BLOCK_LEN ) { lResult = XMODEM_ACK; for ( c = 0; c < XMODEM_BLOCK_LEN; c++ ) MPFSPutArray(handle,&tempData[c],1); MPFSPutEnd(handle); while(BusyUART()); WriteUART(lResult); state = SM_MPFS_SOH; } break; } } return TRUE; }
static BOOL DownloadMPFS(void) { enum SM_MPFS { SM_MPFS_SOH, SM_MPFS_BLOCK, SM_MPFS_BLOCK_CMP, SM_MPFS_DATA, } state; BYTE c; MPFS handle; BOOL lbDone; BYTE blockLen; BYTE lResult; BYTE tempData[XMODEM_BLOCK_LEN]; TICK lastTick; TICK currentTick; state = SM_MPFS_SOH; lbDone = FALSE; handle = MPFSFormat(); /* * Notify the host that we are ready to receive... */ lastTick = TickGet(); do { currentTick = TickGet(); if ( TickGetDiff(currentTick, lastTick) >= (TICK_SECOND/2) ) { lastTick = TickGet(); serPutByte(XMODEM_NAK); /* * Blink LED to indicate that we are waiting for * host to send the file. */ //LATA2 ^= 1; } } while( !serIsGetReady() ); while(!lbDone) { if ( serIsGetReady() ) { /* * Toggle LED as we receive the data from host. */ //LATA2 ^= 1; c = serGetByte(); } else { /* * Real application should put some timeout to make sure * that we do not wait forever. */ continue; } switch(state) { default: if ( c == XMODEM_SOH ) { state = SM_MPFS_BLOCK; } else if ( c == XMODEM_EOT ) { /* * Turn off LED when we are done. */ //LATA2 = 1; MPFSClose(); serPutByte(XMODEM_ACK); lbDone = TRUE; } else serPutByte(XMODEM_NAK); break; case SM_MPFS_BLOCK: /* * We do not use block information. */ lResult = XMODEM_ACK; blockLen = 0; state = SM_MPFS_BLOCK_CMP; break; case SM_MPFS_BLOCK_CMP: /* * We do not use 1's comp. block value. */ state = SM_MPFS_DATA; break; case SM_MPFS_DATA: /* * Buffer block data until it is over. */ tempData[blockLen++] = c; if ( blockLen > XMODEM_BLOCK_LEN ) { /* * We have one block data. * Write it to EEPROM. */ MPFSPutBegin(handle); lResult = XMODEM_ACK; for ( c = 0; c < XMODEM_BLOCK_LEN; c++ ) MPFSPut(tempData[c]); handle = MPFSPutEnd(); serPutByte(lResult); state = SM_MPFS_SOH; } break; } } /* * This small wait is required if SLIP is in use. * If this is not used, PC might misinterpret SLIP * module communication and never close file transfer * dialog box. */ #if defined(STACK_USE_SLIP) { BYTE i; i = 255; while( i-- ); } #endif return TRUE; }
static BOOL PutFile(void) { BYTE v; switch(smFTPCommand) { case SM_FTP_CMD_IDLE: if ( !FTPFlags.Bits.bLoggedIn ) { FTPResponse = FTP_RESP_LOGIN; return TRUE; } else { FTPResponse = FTP_RESP_DATA_OPEN; FTPDataSocket = TCPConnect(&REMOTE_HOST(FTPSocket), FTPDataPort.Val); // Make sure that a valid socket was available and returned // If not, return with an error if(FTPDataSocket != INVALID_SOCKET) { smFTPCommand = SM_FTP_CMD_WAIT; } else { FTPResponse = FTP_RESP_DATA_NO_SOCKET; return TRUE; } } break; case SM_FTP_CMD_WAIT: if ( TCPIsConnected(FTPDataSocket) ) { #if defined(FTP_PUT_ENABLED) if ( !MPFSIsInUse() ) #endif { #if defined(FTP_PUT_ENABLED) FTPFileHandle = MPFSFormat(); #endif smFTPCommand = SM_FTP_CMD_RECEIVE; } } break; case SM_FTP_CMD_RECEIVE: if ( TCPIsGetReady(FTPDataSocket) ) { // Reload timeout timer. lastActivity = TickGet(); MPFSPutBegin(FTPFileHandle); while( TCPGet(FTPDataSocket, &v) ) { USARTPut(v); #if defined(FTP_PUT_ENABLED) MPFSPut(v); #endif } FTPFileHandle = MPFSPutEnd(); TCPDiscard(FTPDataSocket); // Print hash characters on FTP client display if(TCPIsPutReady(FTPSocket)) { TCPPut(FTPSocket, '#'); TCPFlush(FTPSocket); } } else if ( !TCPIsConnected(FTPDataSocket) ) { #if defined(FTP_PUT_ENABLED) MPFSPutEnd(); MPFSClose(); #endif TCPDisconnect(FTPDataSocket); FTPDataSocket = INVALID_SOCKET; FTPResponse = FTP_RESP_DATA_CLOSE; return TRUE; } } return FALSE; }
static BOOL PutFile(void) { BYTE v; switch(smFTPCommand) { case SM_FTP_CMD_IDLE: if ( !FTPFlags.Bits.bLoggedIn ) { FTPResponse = FTP_RESP_LOGIN; return TRUE; } else { FTPResponse = FTP_RESP_DATA_OPEN; FTPDataSocket = TCPOpen((PTR_BASE)&TCPGetRemoteInfo(FTPSocket)->remote, TCP_OPEN_NODE_INFO, FTPDataPort.Val, TCP_PURPOSE_FTP_DATA); // Make sure that a valid socket was available and returned // If not, return with an error if(FTPDataSocket == INVALID_SOCKET) { FTPResponse = FTP_RESP_DATA_NO_SOCKET; return TRUE; } smFTPCommand = SM_FTP_CMD_WAIT; } break; case SM_FTP_CMD_WAIT: if ( TCPIsConnected(FTPDataSocket) ) { #if defined(FTP_PUT_ENABLED) FTPFileHandle = MPFSFormat(); #endif smFTPCommand = SM_FTP_CMD_RECEIVE; } break; case SM_FTP_CMD_RECEIVE: if ( TCPIsGetReady(FTPDataSocket) ) { // Reload timeout timer. lastActivity = TickGet(); MPFSPutBegin(FTPFileHandle); while( TCPGet(FTPDataSocket, &v) ) { #if defined(FTP_PUT_ENABLED) MPFSPut(v); #endif } FTPFileHandle = MPFSPutEnd(); } else if ( !TCPIsConnected(FTPDataSocket) ) { #if defined(FTP_PUT_ENABLED) MPFSClose(); #endif TCPDisconnect(FTPDataSocket); FTPDataSocket = INVALID_SOCKET; FTPResponse = FTP_RESP_DATA_CLOSE; return TRUE; } } return FALSE; }
int MPFS_Close ( uintptr_t handle ) { MPFS_HANDLE address = ((MPFS_HANDLE )handle); MPFSClose ( address ); return MPFS_OK; }
/********************************************************************* * 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); }