//***************************************************************************** // // http.c - HTTP request creation functions. // // Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. // Software License Agreement // // Texas Instruments (TI) is supplying this software for use solely and // exclusively on TI's microcontroller products. The software is owned by // TI and/or its suppliers, and is protected under applicable copyright // laws. You may not combine this software with "viral" open-source // software in order to form a larger program. // // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL // DAMAGES, FOR ANY REASON WHATSOEVER. // // This is part of revision 2.1.0.12573 of the EK-TM4C1294XL Firmware Package. // //***************************************************************************** #include #include #include #include "inc/hw_types.h" #include "utils/ustdlib.h" #include "http.h" //***************************************************************************** // // Buffer used to store temporary strings for constructing/parsing requests. // //***************************************************************************** static char g_pcTempData[256]; //***************************************************************************** // // Declarations for request type strings. // //***************************************************************************** static char g_pcHttpConnect[] = "CONNECT "; static char g_pcHttpGet[] = "GET "; static char g_pcHttpPost[] = "POST "; static char g_pcHttpPut[] = "PUT "; static char g_pcHttpDelete[] = "DELETE "; static char g_pcHttpHead[] = "HEAD "; static char g_pcHttpTrace[] = "TRACE "; static char g_pcHttpOptions[] = "OPTIONS "; static char g_pcHttpPatch[] = "PATCH "; //***************************************************************************** // // HTTP suffixes used by HTTPMessageTypeSet(). Default is HTTP 1.1. // //***************************************************************************** #ifdef USE_HTTP_1_0 static const char g_pcSuffixHttp10[] = " HTTP/1.0\r\n\r\n"; #else static const char g_pcSuffixHttp11[] = " HTTP/1.1\r\n\r\n"; #endif //***************************************************************************** // //! Extract the portion of a string up to a specified character. //! //! \param cWhichOne specifies the character to compare against. //! \param pcSource is a pointer to the source/input string. //! \param pcOutput is a pointer to the destination/output string. //! \param pui32Size is a pointer to a variable that will receive the size of //! the destination/output string. //! //! \return None. // //***************************************************************************** static void BufferFillToCharacter(char cWhichOne, char *pcSource, char *pcOutput, uint32_t *pui32Size) { // // Search the string until the character is found. // *pui32Size = 0; while(*pcSource) { if(*pcSource == cWhichOne) { pcOutput[*pui32Size] = 0; pcSource++; return; } else { pcOutput[*pui32Size] = *pcSource; pcSource++; *pui32Size += 1; } } return; } //***************************************************************************** // //! Extract the portion of a string up to end-of-line (EOL). //! //! \param pcSource is a pointer to the source/input string. //! \param pcOutput is a pointer to the destination/output string. //! \param pui32Size is a pointer to a variable that will receive the size of //! the destination/output string. //! //! \return None. // //***************************************************************************** static void BufferFillToEOL(char *pcSource, char *pcOutput, uint32_t *pui32Size) { // // Search the string until EOL is found. // *pui32Size = 0; while(*pcSource) { if((*pcSource == '\r') && (*(pcSource+ 1) == '\n')) { pcOutput[*pui32Size] = 0; pcSource += 2; return; } else { pcOutput[*pui32Size] = *pcSource; pcSource++; *pui32Size += 1; } } return; } static void InsertRequest(char *pcDest, char *pcRequest) { uint32_t i; uint32_t ui32ReqSize; uint32_t ui32DstSize; ui32ReqSize = strlen(pcRequest); ui32DstSize = strlen(pcDest); pcDest[ui32DstSize + ui32ReqSize] = 0; for(i = ui32DstSize; i-- > 0; ) { pcDest[ui32ReqSize + i] = pcDest[i]; } for(i = 0; i < ui32ReqSize; i++) { pcDest[i] = pcRequest[i]; } } //***************************************************************************** // //! Set the HTTP message type. //! //! \param pcDest is a pointer to the destination/output string. //! \param ui8Type is the HTTP request type. Macros such as HTTP_MESSAGE_GET //! are defined in http.h. //! \param pcResource is a pointer to a string containing the resource portion //! of the HTTP message. The resource goes in between the type (ex: GET) and //! HTTP suffix on the first line of a HTTP request. An example would be //! index.html. //! //! This function should be called to start off a new HTTP request. //! //! \return None. // //***************************************************************************** void HTTPMessageTypeSet(char *pcDest, uint8_t ui8Type, char *pcResource) { // // Check to see if the resource and destination pointers are the same. If // yes, insert the request type at the beginning of the resource string. // if(pcDest == pcResource) { // // Add the request type to the buffer. // switch(ui8Type) { case HTTP_MESSAGE_CONNECT: { InsertRequest(pcDest, g_pcHttpConnect); break; } case HTTP_MESSAGE_GET: { InsertRequest(pcDest, g_pcHttpGet); break; } case HTTP_MESSAGE_POST: { InsertRequest(pcDest, g_pcHttpPost); break; } case HTTP_MESSAGE_PUT: { InsertRequest(pcDest, g_pcHttpPut); break; } case HTTP_MESSAGE_DELETE: { InsertRequest(pcDest, g_pcHttpDelete); break; } case HTTP_MESSAGE_HEAD: { InsertRequest(pcDest, g_pcHttpHead); break; } case HTTP_MESSAGE_TRACE: { InsertRequest(pcDest, g_pcHttpTrace); break; } case HTTP_MESSAGE_OPTIONS: { InsertRequest(pcDest, g_pcHttpOptions); break; } case HTTP_MESSAGE_PATCH: { InsertRequest(pcDest, g_pcHttpPatch); break; } } } else { // // Add the request type to the buffer. // switch(ui8Type) { case HTTP_MESSAGE_CONNECT: { usprintf(pcDest, g_pcHttpConnect); break; } case HTTP_MESSAGE_GET: { usprintf(pcDest, g_pcHttpGet); break; } case HTTP_MESSAGE_POST: { usprintf(pcDest, g_pcHttpPost); break; } case HTTP_MESSAGE_PUT: { usprintf(pcDest, g_pcHttpPut); break; } case HTTP_MESSAGE_DELETE: { usprintf(pcDest, g_pcHttpDelete); break; } case HTTP_MESSAGE_HEAD: { usprintf(pcDest, g_pcHttpHead); break; } case HTTP_MESSAGE_TRACE: { usprintf(pcDest, g_pcHttpTrace); break; } case HTTP_MESSAGE_OPTIONS: { usprintf(pcDest, g_pcHttpOptions); break; } case HTTP_MESSAGE_PATCH: { usprintf(pcDest, g_pcHttpPatch); break; } } // // Add the resource to the buffer. // strcat(pcDest, pcResource); } // // Finish the first "line" by adding the HTTP suffix. // #ifdef USE_HTTP_1_0 strcat(pcDest, g_pcSuffixHttp10); #else strcat(pcDest, g_pcSuffixHttp11); #endif } //***************************************************************************** // //! Add a header to a HTTP request. //! //! \param pcDest is a pointer to the destination/output string. //! \param pcHeaderName is a pointer to a string containing the header name. //! \param pcHeaderValue is a pointer to a string containing the header data. //! //! Note that this function must be called after HTTPMessageTypeSet() as it //! simply appends a header to an existing string/buffer. //! //! \return None. // //***************************************************************************** void HTTPMessageHeaderAdd(char *pcDest, char *pcHeaderName, char *pcHeaderValue) { // // Add the header name to the buffer. // strcat(pcDest, pcHeaderName); // // Add ":" and space. // strcat(pcDest, ": "); // // Add header value. // strcat(pcDest, pcHeaderValue); // // Add \r and \n. // strcat(pcDest, "\r\n"); } //***************************************************************************** // //! Add body data to to a HTTP request. //! //! \param pcDest is a pointer to the destination/output string. //! \param pcBodyData is a pointer to a string containing the body data. This //! can be anything from HTML to encoded data (such as JSON). //! //! Note that this function must be called after HTTPMessageTypeSet() and //! HTTPMessageHeaderAdd() as it simply appends the body data to an existing //! string/buffer. //! //! \return None. // //***************************************************************************** void HTTPMessageBodyAdd(char *pcDest, char *pcBodyData) { // // First, insert blank line between header section and body. // strcat(pcDest, "\r\n"); // // Add body content. // strcat(pcDest, pcBodyData); // // Add blank line. // strcat(pcDest, "\r\n\r\n"); } //***************************************************************************** // //! Parse a HTTP response. //! //! \param pcData is a pointer to the source string/buffer. //! \param pcResponseText is a pointer to a string that will receive the //! response text from the first line of the HTTP response. //! \param pui32NumHeaders is a pointer to a variable that will receive the //! number of headers detected in pcData. //! //! Note that this function must be called after HTTPMessageTypeSet() as it //! simply appends a header to an existing string/buffer. //! //! \return Returns the HTTP response code. If parsing error occurs, returns 0. // //***************************************************************************** uint32_t HTTPResponseParse(char *pcData, char *pcResponseText, uint32_t *pui32NumHeaders) { uint32_t ui32Response = 0; uint32_t ui32Size; // // Look for "HTTP/n.n" piece. // BufferFillToCharacter(' ', pcData, g_pcTempData, &ui32Size); pcData += (ui32Size + 1); // // Fail if not a HTTP response. // if(ustrncmp(g_pcTempData, "HTTP/", 5)) { *pcResponseText = 0; *pui32NumHeaders = 0; return 0; } // // Get the return code. // BufferFillToCharacter(' ', pcData, g_pcTempData, &ui32Size); pcData += (ui32Size + 1); // // Convert return code to unsigned long. // ui32Response = ustrtoul(g_pcTempData, 0, 10); // // Get the response text. // BufferFillToEOL(pcData, pcResponseText, &ui32Size); pcData += (ui32Size + 2); // // Parse the remainder of the packet. // *pui32NumHeaders = 0; while(*pcData) { // // Search line by line and count headers. Search until there is a blank // line, which means end of headers. // BufferFillToEOL(pcData, g_pcTempData, &ui32Size); pcData += (ui32Size + 2); // // Blank line. For the purposes of this function, we're done. // if(ui32Size == 0) { break; } else { // // Increment header counter. // *pui32NumHeaders += 1; } } return ui32Response; } //***************************************************************************** // //! Extract a specified header from a HTTP response string/buffer. //! //! \param pcData is a pointer to the source string/buffer. //! \param ui32HeaderIdx specifies the index of the header to extract. //! \param pcHeaderName is a pointer to a string that will receive the name of //! the header specified by ui32HeaderIdx. //! \param pcHeaderValue is a pointer to a string that will receive the value of //! the header specified by ui32HeaderIdx. //! //! Note that this function should be used in conjunction with //! HTTPResponseParse() since it notifies the application of the number of //! headers in a string/buffer. //! //! \return None. // //***************************************************************************** void HTTPResponseHeaderExtract(char *pcData, uint32_t ui32HeaderIdx, char *pcHeaderName, char *pcHeaderValue) { uint32_t ui32Size; uint32_t ui32HeaderNumber = 0; // // Read first line and discard. // BufferFillToEOL(pcData, g_pcTempData, &ui32Size); pcData += (ui32Size + 2); // // Find and return the requested header. // while(*pcData) { BufferFillToEOL(pcData, g_pcTempData, &ui32Size); pcData += (ui32Size + 2); // // Blank line, end of header section. // if(ui32Size == 0) { break; } else { if(ui32HeaderNumber == ui32HeaderIdx) { BufferFillToCharacter(':', g_pcTempData, pcHeaderName, &ui32Size); BufferFillToCharacter(0, &g_pcTempData[ui32Size + 2], pcHeaderValue, &ui32Size); pcHeaderValue[ui32Size] = 0; break; } else { // // Increment header counter. // ui32HeaderNumber++; } } } } //***************************************************************************** // //! Extract the body from a HTTP response string/buffer. //! //! \param pcData is a pointer to the source string/buffer. //! \param pcDest is a pointer to a string that will receive the body data. //! //! \return None. // //***************************************************************************** void HTTPResponseBodyExtract(char *pcData, char *pcDest) { bool bBodyFound = false; uint32_t ui32Size; // // Find and return the body. // while(*pcData) { // // Read lines until a blank line is found. This is the start of the // body. // BufferFillToEOL(pcData, g_pcTempData, &ui32Size); pcData += (ui32Size + 2); // // Blank line, end of header section. // if(ui32Size == 0) { bBodyFound = true; } // // If the body has been found, start filling the buffer. // if(bBodyFound) { pcDest[0] = 0; strcat(pcDest, pcData); break; } } }