static void ServeFile(FILE * stream_file, const char * fname, SdFile & theFile, EthernetClient & client) { freeMemory(); const char * ext; for (ext=fname + strlen(fname); ext>fname; ext--) if (*ext == '.') { ext++; break; } if (ext > fname) { if (strcmp(ext, "jpg") == 0) ServeHeader(stream_file, 200, "OK", true, "image/jpeg"); else if (strcmp(ext, "gif") == 0) ServeHeader(stream_file, 200, "OK", true, "image/gif"); else if (strcmp(ext, "css") == 0) ServeHeader(stream_file, 200, "OK", true, "text/css"); else if (strcmp(ext, "js") == 0) ServeHeader(stream_file, 200, "OK", true, "application/javascript"); else if (strcmp(ext, "ico") == 0) ServeHeader(stream_file, 200, "OK", true, "image/x-icon"); else ServeHeader(stream_file, 200, "OK", true); } else ServeHeader(stream_file, 200, "OK", true); #ifdef ARDUINO flush_sendbuf(client); #else fflush(stream_file); #endif while (theFile.available()) { int bytes = theFile.read(sendbuf, 512); if (bytes <= 0) break; client.write((uint8_t*) sendbuf, bytes); } }
void web::ProcessWebClients() { // listen for incoming clients EthernetClient client = m_server->available(); if (client) { bool bReset = false; #ifdef ARDUINO FILE stream_file; FILE * pFile = &stream_file; setup_sendbuf(); fdev_setup_stream(pFile, stream_putchar, NULL, _FDEV_SETUP_WRITE); stream_file.udata = &client; #else FILE * pFile = fdopen(client.GetSocket(), "w"); #endif freeMemory(); trace(F("Got a client\n")); //ShowSockStatus(); KVPairs key_value_pairs; char sPage[35]; if (!ParseHTTPHeader(client, &key_value_pairs, sPage, sizeof(sPage))) { trace(F("ERROR!\n")); ServeError(pFile); } else { trace(F("Page:%s\n"), sPage); //ShowSockStatus(); if (strcmp(sPage, "bin/setSched") == 0) { if (SetSchedule(key_value_pairs)) { if (GetRunSchedules()) ReloadEvents(); ServeHeader(pFile, 200, "OK", false); } else ServeError(pFile); } else if (strcmp(sPage, "bin/setZones") == 0) { if (SetZones(key_value_pairs)) { ReloadEvents(); ServeHeader(pFile, 200, "OK", false); } else ServeError(pFile); } else if (strcmp(sPage, "bin/delSched") == 0) { if (DeleteSchedule(key_value_pairs)) { if (GetRunSchedules()) ReloadEvents(); ServeHeader(pFile, 200, "OK", false); } else ServeError(pFile); } else if (strcmp(sPage, "bin/setQSched") == 0) { if (SetQSched(key_value_pairs)) { ServeHeader(pFile, 200, "OK", false); } else ServeError(pFile); } else if (strcmp(sPage, "bin/settings") == 0) { if (SetSettings(key_value_pairs)) { ReloadEvents(); ServeHeader(pFile, 200, "OK", false); } else ServeError(pFile); } else if (strcmp(sPage, "bin/manual") == 0) { if (ManualZone(key_value_pairs)) { ServeHeader(pFile, 200, "OK", false); } else ServeError(pFile); } else if (strcmp(sPage, "bin/run") == 0) { if (RunSchedules(key_value_pairs)) { ReloadEvents(); ServeHeader(pFile, 200, "OK", false); } else ServeError(pFile); } else if (strcmp(sPage, "bin/factory") == 0) { ResetEEPROM(); ReloadEvents(); ServeHeader(pFile, 200, "OK", false); } else if (strcmp(sPage, "bin/reset") == 0) { ServeHeader(pFile, 200, "OK", false); bReset = true; } else if (strcmp(sPage, "json/schedules") == 0) { JSONSchedules(key_value_pairs, pFile); } else if (strcmp(sPage, "json/zones") == 0) { JSONZones(key_value_pairs, pFile); } else if (strcmp(sPage, "json/settings") == 0) { JSONSettings(key_value_pairs, pFile); } else if (strcmp(sPage, "json/state") == 0) { JSONState(key_value_pairs, pFile); } else if (strcmp(sPage, "json/schedule") == 0) { JSONSchedule(key_value_pairs, pFile); } else if (strcmp(sPage, "json/wcheck") == 0) { JSONwCheck(key_value_pairs, pFile); } #ifdef LOGGING else if (strcmp(sPage, "json/logs") == 0) { JSONLogs(key_value_pairs, pFile); } else if (strcmp(sPage, "json/tlogs") == 0) { JSONtLogs(key_value_pairs, pFile); } #endif else if (strcmp(sPage, "ShowSched") == 0) { freeMemory(); ServeSchedPage(pFile); } else if (strcmp(sPage, "ShowZones") == 0) { freeMemory(); ServeZonesPage(pFile); } else if (strcmp(sPage, "ShowEvent") == 0) { ServeEventPage(pFile); } else if (strcmp(sPage, "ReloadEvent") == 0) { ReloadEvents(true); ServeEventPage(pFile); } else { if (strlen(sPage) == 0) strcpy(sPage, "index.htm"); // prepend path memmove(sPage + 5, sPage, sizeof(sPage) - 5); memcpy(sPage, "/web/", 5); sPage[sizeof(sPage)-1] = 0; trace(F("Serving Page: %s\n"), sPage); SdFile theFile; if (!theFile.open(sPage, O_READ)) Serve404(pFile); else { if (theFile.isFile()) ServeFile(pFile, sPage, theFile, client); else Serve404(pFile); theFile.close(); } } } #ifdef ARDUINO flush_sendbuf(client); // give the web browser time to receive the data delay(1); #else fflush(pFile); fclose(pFile); #endif // close the connection: client.stop(); if (bReset) sysreset(); } }
// Main routine of the worker thread void do_work () { // Initialise structures needed for polling pollfd pfd [2]; pfd [0].fd = command_pipe; pfd [0].events = POLLIN; pfd [1].fd = sock; pfd [1].events = 0; bool stopping = false; bool pollin = true; bool pollout = xmode_blocked; // Main loop of the worker thread while (true) { // If the worker thread was requested to stop and there are // no more data to send, exit the main loop. if (stopping && !pollout) break; // Adjust the poll events according to 'pollin' and 'pollout' // variables, start the polling and check the results // for errors. pfd [1].events = (pollin ? POLLIN : 0) | (pollout ? POLLOUT : 0); int rc = poll (pfd, 2, -1); errno_assert (rc != -1); assert ((pfd [0].revents & (POLLERR | POLLHUP | POLLNVAL)) == 0); assert ((pfd [1].revents & POLLNVAL) == 0); // Command from client thread was received by the worker // thread. if (pfd [0].revents & POLLIN) { switch (command_pipe.read ()) { case command_stop: // Worker thread is scheduled to terminate. stopping = true; break; case command_resurrect: // Worker thread starts polling for write availability // of the socket after new data to send are available // in the send pipe. pollout = true; break; } } // Socket is available for reading if (pfd [1].revents & POLLIN) { // Allocate a memory chunk and read as much data as // possible. Only one chunk is read at once to cease the // control to writing part of the loop periodically even // if there is input on the socket all the time. unsigned char *chunk = (unsigned char*) malloc (ZMQ_RECV_CHUNK_SIZE); assert (chunk); ssize_t nbytes = recv (sock, chunk, ZMQ_RECV_CHUNK_SIZE, 0); errno_assert (nbytes >= 0); // Enqueue empty chunk to let the client thread know that // the other party have closed the socket. if (nbytes == 0) { pollin = false; free (chunk); chunk = NULL; } // Push the chunk to the receiver pipe. If the pipe was // dead, post the semaphore to unblock client thread // that is waiting for a message. msg_t ch = {chunk, nbytes, 0, free}; if (!receive_pipe.write (ch)) receive_sync.post (); // If batch mode is set by user and the received // chunk is too small, wait for a while to improve the // batching ratio. NB: This code needs a careful // reconsideration in the future releases. (See // nanosleep documentation for description of lack of // precision of this function on Linux.) if (xmode_blocked && nbytes < ZMQ_RECV_CHUNK_SIZE / 2) { timespec tmspc = {0, 10000}; int rc = nanosleep (&tmspc, NULL); errno_assert (rc == 0); } } // Socket is available for writing if (pfd [1].revents & POLLOUT) { // Flush the data remaining in the sendbuf to the socket. // If successful, get new data from send pipe to send // buffer. If none are available, stop polling for writing // availability of the socket. If there are data, try to // flush them to the socket and cease the control to other // tasks. if (flush_sendbuf ()) { if (!send_pipe.read (&sendbuf_first, &sendbuf_last)) { pollout = false; } else flush_sendbuf (); } } } }