/** * Name: runServer * Description: Runs the server. If a connection is accepted, then parent process continues listening * and spawns child process to process the message and close the connection to client. * * @param sock Socket File Descriptor for listening connection */ void runServer(int sock) { struct sockaddr_storage clientaddr; //continuous loop while (1) { socklen_t addr_size = sizeof(clientaddr); //accept the new connection int newsocket = accept(sock, (struct sockaddr*) &clientaddr, &addr_size); __android_log_print(ANDROID_LOG_INFO, TAG, "Accepting Socket"); if (newsocket < 0) { __android_log_print(ANDROID_LOG_ERROR, TAG, "Socket accepting failed", 1); __android_log_print(ANDROID_LOG_INFO, TAG, strerror(errno)); endServer(sock, NULL, newsocket); continue; } //split between parent process and child process. Parent=listening child=accepting if (!fork()) { close(sock); char buffer[HTTP_MAX_SIZE]; __android_log_print(ANDROID_LOG_INFO, TAG, "Receiving"); //grab info into httpReceive httpReceive(newsocket,buffer); __android_log_print(ANDROID_LOG_INFO,TAG,"%s %d", buffer, (int) strlen(buffer)); //kick back message up to java const char * message = processMessage(buffer); __android_log_print(ANDROID_LOG_INFO,TAG,"%s %d", message, (int) strlen(message)); __android_log_print(ANDROID_LOG_INFO, TAG, "Sending"); //get the message and send it to client if (send(newsocket,message,strlen(message), 0) < 0){ __android_log_print(ANDROID_LOG_ERROR,TAG,"Send Failed",1); endServer(-1, NULL, newsocket); } __android_log_print(ANDROID_LOG_INFO,TAG,"Sent Message %s", message); //close the new socket and exit the process close(newsocket); exit(0); } close(newsocket); } }
error_t httpReadStream(HttpConnection *connection, void *data, size_t size, size_t *received, uint_t flags) { error_t error; size_t n; //No data has been read yet *received = 0; //Chunked encoding transfer is used? if(connection->request.chunkedEncoding) { //Point to the output buffer char_t *p = data; //Read as much data as possible while(*received < size) { //End of HTTP request body? if(connection->request.lastChunk) return ERROR_END_OF_STREAM; //Acquire a new chunk when the current chunk //has been completely consumed if(connection->request.byteCount == 0) { //The size of each chunk is sent right before the chunk itself error = httpReadChunkSize(connection); //Failed to decode the chunk-size field? if(error) return error; //Any chunk whose size is zero terminates the data transfer if(!connection->request.byteCount) { //The user must be satisfied with data already on hand return (*received > 0) ? NO_ERROR : ERROR_END_OF_STREAM; } } //Limit the number of bytes to read at a time n = MIN(size - *received, connection->request.byteCount); //Read data error = httpReceive(connection, p, n, &n, flags); //Any error to report? if(error) return error; //Total number of data that have been read *received += n; //Remaining data still available in the current chunk connection->request.byteCount -= n; //The HTTP_FLAG_BREAK_CHAR flag causes the function to stop reading //data as soon as the specified break character is encountered if(flags & HTTP_FLAG_BREAK_CRLF) { //Check whether a break character has been received if(p[n - 1] == LSB(flags)) break; } //The HTTP_FLAG_WAIT_ALL flag causes the function to return //only when the requested number of bytes have been read else if(!(flags & HTTP_FLAG_WAIT_ALL)) { break; } //Advance data pointer p += n; } } //Default encoding? else { //Return immediately if the end of the request body has been reached if(!connection->request.byteCount) return ERROR_END_OF_STREAM; //Limit the number of bytes to read n = MIN(size, connection->request.byteCount); //Read data error = httpReceive(connection, data, n, received, flags); //Any error to report if(error) return error; //Decrement the count of remaining bytes to read connection->request.byteCount -= *received; } //Successful read operation return NO_ERROR; }
error_t httpReadHeader(HttpConnection *connection) { error_t error; size_t length; char_t *token; char_t *p; char_t *s; //Set the maximum time the server will wait for an HTTP //request before closing the connection error = socketSetTimeout(connection->socket, HTTP_SERVER_IDLE_TIMEOUT); //Any error to report? if(error) return error; //Read the first line of the request error = httpReceive(connection, connection->buffer, HTTP_SERVER_BUFFER_SIZE - 1, &length, SOCKET_FLAG_BREAK_CRLF); //Unable to read any data? if(error) return error; //Revert to default timeout error = socketSetTimeout(connection->socket, HTTP_SERVER_TIMEOUT); //Any error to report? if(error) return error; //Properly terminate the string with a NULL character connection->buffer[length] = '\0'; //Debug message TRACE_INFO("%s", connection->buffer); //The Request-Line begins with a method token token = strtok_r(connection->buffer, " \r\n", &p); //Unable to retrieve the method? if(!token) return ERROR_INVALID_REQUEST; //The Method token indicates the method to be performed on the //resource identified by the Request-URI error = strSafeCopy(connection->request.method, token, HTTP_SERVER_METHOD_MAX_LEN); //Any error to report? if(error) return ERROR_INVALID_REQUEST; //The Request-URI is following the method token token = strtok_r(NULL, " \r\n", &p); //Unable to retrieve the Request-URI? if(!token) return ERROR_INVALID_REQUEST; //Check whether a query string is present s = strchr(token, '?'); //Query string found? if(s != NULL) { //Split the string *s = '\0'; //Save the Request-URI error = httpDecodePercentEncodedString(token, connection->request.uri, HTTP_SERVER_URI_MAX_LEN); //Any error to report? if(error) return ERROR_INVALID_REQUEST; //Check the length of the query string if(strlen(s + 1) > HTTP_SERVER_QUERY_STRING_MAX_LEN) return ERROR_INVALID_REQUEST; //Save the query string strcpy(connection->request.queryString, s + 1); } else { //Save the Request-URI error = httpDecodePercentEncodedString(token, connection->request.uri, HTTP_SERVER_URI_MAX_LEN); //Any error to report? if(error) return ERROR_INVALID_REQUEST; //No query string connection->request.queryString[0] = '\0'; } //Redirect to the default home page if necessary if(!strcasecmp(connection->request.uri, "/")) strcpy(connection->request.uri, connection->settings->defaultDocument); //Clean the resulting path pathCanonicalize(connection->request.uri); //The protocol version is following the Request-URI token = strtok_r(NULL, " \r\n", &p); //HTTP version 0.9? if(!token) { //Save version number connection->request.version = HTTP_VERSION_0_9; //Persistent connections are not supported connection->request.keepAlive = FALSE; } //HTTP version 1.0? else if(!strcasecmp(token, "HTTP/1.0")) { //Save version number connection->request.version = HTTP_VERSION_1_0; //By default connections are not persistent connection->request.keepAlive = FALSE; } //HTTP version 1.1? else if(!strcasecmp(token, "HTTP/1.1")) { //Save version number connection->request.version = HTTP_VERSION_1_1; //HTTP 1.1 makes persistent connections the default connection->request.keepAlive = TRUE; } //HTTP version not supported? else { return ERROR_INVALID_REQUEST; } //Default value for properties connection->request.chunkedEncoding = FALSE; connection->request.contentLength = 0; //HTTP 0.9 does not support Full-Request if(connection->request.version >= HTTP_VERSION_1_0) { //Local variables char_t firstChar; char_t *separator; char_t *name; char_t *value; //This variable is used to decode header fields that span multiple lines firstChar = '\0'; //Parse the header fields of the HTTP request while(1) { //Decode multiple-line header field error = httpReadHeaderField(connection, connection->buffer, HTTP_SERVER_BUFFER_SIZE, &firstChar); //Any error to report? if(error) return error; //Debug message TRACE_DEBUG("%s", connection->buffer); //An empty line indicates the end of the header fields if(!strcmp(connection->buffer, "\r\n")) break; //Check whether a separator is present separator = strchr(connection->buffer, ':'); //Separator found? if(separator != NULL) { //Split the line *separator = '\0'; //Get field name and value name = strTrimWhitespace(connection->buffer); value = strTrimWhitespace(separator + 1); //Connection field found? if(!strcasecmp(name, "Connection")) { //Check whether persistent connections are supported or not if(!strcasecmp(value, "keep-alive")) connection->request.keepAlive = TRUE; else if(!strcasecmp(value, "close")) connection->request.keepAlive = FALSE; } //Transfer-Encoding field found? else if(!strcasecmp(name, "Transfer-Encoding")) { //Check whether chunked encoding is used if(!strcasecmp(value, "chunked")) connection->request.chunkedEncoding = TRUE; } //Content-Type field found? else if(!strcasecmp(name, "Content-Type")) { //Parse Content-Type field httpParseContentTypeField(connection, value); } //Content-Length field found? else if(!strcasecmp(name, "Content-Length")) { //Get the length of the body data connection->request.contentLength = atoi(value); } //Authorization field found? else if(!strcasecmp(name, "Authorization")) { //Parse Authorization field httpParseAuthField(connection, value); } } } } //Prepare to read the HTTP request body if(connection->request.chunkedEncoding) { connection->request.byteCount = 0; connection->request.firstChunk = TRUE; connection->request.lastChunk = FALSE; } else { connection->request.byteCount = connection->request.contentLength; } //The request header has been successfully parsed return NO_ERROR; }
error_t httpReadChunkSize(HttpConnection *connection) { error_t error; size_t n; char_t *end; char_t s[8]; //First chunk to be received? if(connection->request.firstChunk) { //Clear the flag connection->request.firstChunk = FALSE; } else { //Read the CRLF that follows the previous chunk-data field error = httpReceive(connection, s, sizeof(s) - 1, &n, SOCKET_FLAG_BREAK_CRLF); //Any error to report? if(error) return error; //Properly terminate the string with a NULL character s[n] = '\0'; //The chunk data must be terminated by CRLF if(strcmp(s, "\r\n")) return ERROR_WRONG_ENCODING; } //Read the chunk-size field error = httpReceive(connection, s, sizeof(s) - 1, &n, SOCKET_FLAG_BREAK_CRLF); //Any error to report? if(error) return error; //Properly terminate the string with a NULL character s[n] = '\0'; //Remove extra whitespaces strRemoveTrailingSpace(s); //Retrieve the size of the chunk connection->request.byteCount = strtoul(s, &end, 16); //No valid conversion could be performed? if(end == s || *end != '\0') return ERROR_WRONG_ENCODING; //Any chunk whose size is zero terminates the data transfer if(!connection->request.byteCount) { //The end of the HTTP request body has been reached connection->request.lastChunk = TRUE; //Skip the trailer while(1) { //Read a complete line error = httpReceive(connection, s, sizeof(s) - 1, &n, SOCKET_FLAG_BREAK_CRLF); //Unable to read any data? if(error) return error; //Properly terminate the string with a NULL character s[n] = '\0'; //The trailer is terminated by an empty line if(!strcmp(s, "\r\n")) break; } } //Successful processing return NO_ERROR; }
error_t httpReadHeaderField(HttpConnection *connection, char_t *buffer, size_t size, char_t *firstChar) { error_t error; size_t n; size_t length; //This is the actual length of the header field length = 0; //The process of moving from a multiple-line representation of a header //field to its single line representation is called unfolding do { //Check the length of the header field if((length + 1) >= size) { //Report an error error = ERROR_INVALID_REQUEST; //Exit immediately break; } //NULL character found? if(*firstChar == '\0') { //Prepare to decode the first header field length = 0; } //LWSP character found? else if(*firstChar == ' ' || *firstChar == '\t') { //Unfolding is accomplished by regarding CRLF immediately //followed by a LWSP as equivalent to the LWSP character buffer[length] = *firstChar; //The current header field spans multiple lines length++; } //Any other character? else { //Restore the very first character of the header field buffer[0] = *firstChar; //Prepare to decode a new header field length = 1; } //Read data until a CLRF character is encountered error = httpReceive(connection, buffer + length, size - 1 - length, &n, SOCKET_FLAG_BREAK_CRLF); //Any error to report? if(error) break; //Update the length of the header field length += n; //Properly terminate the string with a NULL character buffer[length] = '\0'; //An empty line indicates the end of the header fields if(!strcmp(buffer, "\r\n")) break; //Read the next character to detect if the CRLF is immediately //followed by a LWSP character error = httpReceive(connection, firstChar, sizeof(char_t), &n, SOCKET_FLAG_WAIT_ALL); //Any error to report? if(error) break; //LWSP character found? if(*firstChar == ' ' || *firstChar == '\t') { //CRLF immediately followed by LWSP as equivalent to the LWSP character if(length >= 2) { if(buffer[length - 2] == '\r' || buffer[length - 1] == '\n') { //Remove trailing CRLF sequence length -= 2; //Properly terminate the string with a NULL character buffer[length] = '\0'; } } } //A header field may span multiple lines... } while(*firstChar == ' ' || *firstChar == '\t'); //Return status code return error; }