// Processes a single request from a web client. void webserver_process_request (int connfd, string clientaddress) { mutex_requests.lock (); config_globals_simultaneous_connection_count++; mutex_requests.unlock (); // The environment for this request. // It gets passed around from function to function during the entire request. // This provides thread-safety to the request. Webserver_Request request; // Store remote client address in the request. request.remote_address = clientaddress; try { if (config_globals_running) { // Connection health flag. bool connection_healthy = true; // Read the client's request. // With the HTTP protocol it is not possible to read the request till EOF, // because EOF does never come, because the browser keeps the connection open // for the response. // The HTTP protocol works per line. // Read one line of data from the client. // An empty line marks the end of the headers. #define BUFFERSIZE 2048 int bytes_read; bool header_parsed = true; char buffer [BUFFERSIZE]; // Fix valgrind unitialized value message. memset (&buffer, 0, BUFFERSIZE); do { bytes_read = get_line (connfd, buffer, BUFFERSIZE); if (bytes_read <= 0) connection_healthy = false; // Parse the browser's request's headers. header_parsed = http_parse_header (buffer, &request); } while (header_parsed); if (connection_healthy) { // In the case of a POST request, more data follows: The POST request itself. // The length of that data is indicated in the header's Content-Length line. // Read that data, and parse it. string postdata; if (request.is_post) { bool done_reading = false; int total_bytes_read = 0; do { bytes_read = read (connfd, buffer, BUFFERSIZE); for (int i = 0; i < bytes_read; i++) { postdata += buffer [i]; } // EOF indicates reading is ready. // An error also indicates that reading is ready. if (bytes_read <= 0) done_reading = true; if (bytes_read < 0) connection_healthy = false; // "Content-Length" bytes read: Done. total_bytes_read += bytes_read; if (total_bytes_read >= request.content_length) done_reading = true; } while (!done_reading); if (total_bytes_read < request.content_length) connection_healthy = false; } if (connection_healthy) { http_parse_post (postdata, &request); // Assemble response. bootstrap_index (&request); http_assemble_response (&request); // Send response to browser. const char * output = request.reply.c_str(); // The C function strlen () fails on null characters in the reply, so take string::size() size_t length = request.reply.size (); int written = write (connfd, output, length); if (written) {}; } else { // cerr << "Insufficient data received, closing connection" << endl; } } else { // cerr << "Unhealthy connection was closed" << endl; } } } catch (exception & e) { string message ("Internal error: "); message.append (e.what ()); Database_Logs::log (message); } catch (exception * e) { string message ("Internal error: "); message.append (e->what ()); Database_Logs::log (message); } catch (...) { Database_Logs::log ("A general internal error occurred"); } // Done: Close. close (connfd); mutex_requests.lock (); config_globals_simultaneous_connection_count--; mutex_requests.unlock (); }
/*------------------------------------------------------------------------ * The received header must contain the word "GET" or "POST" to be * considered a valid request. * With HTTP 1.1 where the connection is left open, the header I send * should include content length. With HTTP 1.0 you can just close the * connection after sending the page and the browser knows its done. * * The HTTP protocol specification is at http://www.w3.org/Protocols/ *------------------------------------------------------------------------ */ ushort http_server(uchar *inbuf, ushort header_len, ushort tcp_len, uchar nr, uchar resend) { static uint counter=0; uint content_length; uint data_len; uchar *ptr; uchar *tcp_data; if (http_debug == DEBUG) printf("\n\n%s************ tcp_len = %d ************%d\n", __func__,tcp_len,http_state); else if ((counter++ % 100) == 0) printf("."); /* Make sure this is a valid connection */ if (nr == NO_CONNECTION) return 0; /* Compute start of TCP data */ /* Save first 20 chars and seq number just in case * we need to re-generate page * TODO: if post, then save switch state infomation * If this is a resend, set sequence number to what it was * the last time we sent this */ if (!resend) { tcp_data = inbuf + ETHER_IP_HLEN + header_len; memcpy(conxn[nr].query, tcp_data, 20); conxn[nr].old_sequence = conxn[nr].my_sequence; } else { tcp_data = inbuf; conxn[nr].my_sequence = conxn[nr].old_sequence; } if (http_debug == DEBUG) printf("#1> http_state = %d......\n",http_state); /* Pre-porcess HTTP state change. */ switch (http_state) { case HTTP_START: if ( (strstr((const char *)tcp_data, "GET") != NULL) && ((strstr((const char *)tcp_data, "index") != NULL) || (strstr((const char *)tcp_data, "/ ") != NULL)) ) http_state = HTTP_GET; else if ( (strstr((const char *)tcp_data, "POST") != NULL) ) http_state = HTTP_POST; break; case HTTP_GET: if ( (strstr((const char *)tcp_data, "POST") != NULL) ) http_state = HTTP_POST; break; case HTTP_UPLOAD: /* process the situation of network disconnection */ if ( (strstr((const char *)tcp_data, "GET") != NULL) && ((strstr((const char *)tcp_data, "index") != NULL) || (strstr((const char *)tcp_data, "/ ") != NULL)) ) http_state = HTTP_GET; break; } switch (http_state) { case HTTP_START: break; case HTTP_GET: /* send download page to HTTP client */ http_send_ok(nr, web_page, 200, "OK"); break; case HTTP_POST: if (http_parse_post(tcp_data, &content_length)) { http_state = HTTP_START; return 1; } else { post_info.upload_len = content_length; post_info.data_len = 0; post_info.buf = (uchar *)upgrade_buffer; http_state = HTTP_HEAD_DATA; } if (http_parse_mime(tcp_data, tcp_len - header_len, nr) == 0) http_state = HTTP_UPLOAD; break; case HTTP_HEAD_DATA: post_info.rcv_len = tcp_len - header_len; if (http_check_filename(tcp_data)) { http_send_error(nr, 400, "Bad Request", "No upload file name to be slected !"); http_state = HTTP_START; return 1; } ptr = (uchar *)strstr((const char *)tcp_data, c_headerend); ptr = ptr + strlen(c_headerend); /* upload file start pointer */ data_len = tcp_len - header_len - (ptr - tcp_data ); post_info.data_len = data_len; memcpy(post_info.buf, ptr, data_len); http_state = HTTP_UPLOAD; break; case HTTP_UPLOAD: post_info.rcv_len += tcp_len - header_len; if (http_debug == DEBUG) { printf("post_info.rcv_len=%d post_info.upload_len=%d\n" ,post_info.rcv_len,post_info.upload_len); } if (post_info.rcv_len >= post_info.upload_len) { /* To check the last packet if contains image data. */ /* 6-byte include two "--" and one \r\n */ /* 8-byte include two "--" and two \r\n */ if ((tcp_len - header_len) > (strlen(boundary_code)+6)) data_len = tcp_len - header_len - \ strlen(boundary_code) - 8; else data_len = 0; if (http_check_image(post_info.buf, post_info.data_len)) { http_send_error(nr, 400, "Bad Request", "File format is invalid !"); http_state = HTTP_START; return (1); } else { memset(text, 0x00, sizeof(text)); #ifdef CONFIG_MTD_CORTINA_CS752X_NAND sprintf(text,"%d", 5 + post_info.data_len/(1024*1000)); #else sprintf(text,"%d", 10 + post_info.data_len/(1024*100)); #endif replace_tag((uchar *)upgrade200_1, "TAG:NUM1", text); http_send_ok(nr, upgrade200_1, 200, "OK"); if (do_upgrade_image(post_info.buf, post_info.data_len)) { http_state = HTTP_START; return 0; } else { http_state = HTTP_REBOOT; break; } } } else { data_len = tcp_len - header_len; memcpy((void *)&post_info.buf[post_info.data_len], tcp_data, data_len); post_info.data_len += data_len; } break; case HTTP_REBOOT: if ( (strstr((const char *)tcp_data, "GET") != NULL) && (strstr((const char *)tcp_data, "reboot") != NULL) ) { http_state = HTTP_START; http_send_ok(nr, reboot200, 200, "OK"); /* * Reserved time for tx those packets that queue * in packet buffer before system reset. */ udelay(500); do_reset(NULL, 0, 0, NULL); } break; default: printf("HTTP State Error ! %d\n", http_state); break; } if (http_debug == DEBUG) printf("#2> http_state = %d......\n",http_state); return 0; }