1/* 2 * Copyright 2009, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Michael Lotz <mmlr@mlotz.ch> 7 * François Revol <revol@free.fr> 8 */ 9 10#include "WebHandler.h" 11#include "WebServer.h" 12#include "WebWorker.h" 13 14#include "StreamingRingBuffer.h" 15 16#include <NetEndpoint.h> 17#include <Autolock.h> 18 19#include <stdio.h> 20#include <stdlib.h> 21#include <string.h> 22 23#define TRACE(x...) debug_printf("WebServer: "x) 24#define TRACE_ERROR(x...) debug_printf("WebServer: "x) 25 26 27WebServer::WebServer(BNetEndpoint *listener) 28 : 29 fListener(listener), 30 fReceiverThread(-1), 31 fStopThread(false), 32 fLocker("WebServer locker") 33{ 34 fReceiverThread = spawn_thread(_NetworkReceiverEntry, "html5 server", 35 B_NORMAL_PRIORITY, this); 36 resume_thread(fReceiverThread); 37} 38 39 40WebServer::~WebServer() 41{ 42 fStopThread = true; 43 44 if (fListener != NULL) 45 fListener->Close(); 46 47 //int32 result; 48 //wait_for_thread(fReceiverThread, &result); 49 // TODO: find out why closing the endpoint doesn't notify the waiter 50 51 kill_thread(fReceiverThread); 52} 53 54 55void 56WebServer::AddHandler(WebHandler *handler) 57{ 58 BAutolock lock(fLocker); 59 fHandlers.AddItem(handler); 60} 61 62int32 63WebServer::_NetworkReceiverEntry(void *data) 64{ 65 return ((WebServer *)data)->_NetworkReceiver(); 66} 67 68 69status_t 70WebServer::_NetworkReceiver() 71{ 72 status_t result = fListener->Listen(); 73 if (result != B_OK) { 74 TRACE_ERROR("failed to listen on port: %s\n", strerror(result)); 75 return result; 76 } 77 78 fHandlers.SortItems(&WebHandler::_CallbackCompare); 79 80 while (!fStopThread) { 81 BNetEndpoint *endpoint = fListener->Accept(1000); 82 if (endpoint == NULL) 83 continue; 84 85 TRACE("new endpoint connection: %p\n", endpoint); 86 while (!fStopThread) { 87 int32 errorCount = 0; 88 uint8 buffer[4096]; 89 int32 readSize = endpoint->Receive(buffer, sizeof(buffer) - 1); 90 if (readSize < 0) { 91 TRACE_ERROR("read failed, closing connection: %s\n", 92 strerror(readSize)); 93 delete endpoint; 94 break; 95 } 96 97 if (readSize == 0) { 98 TRACE("read 0 bytes, retrying\n"); 99 snooze(100 * 1000); 100 errorCount++; 101 if (errorCount == 5) { 102 TRACE_ERROR("failed to read, assuming disconnect\n"); 103 delete endpoint; 104 break; 105 } 106 continue; 107 } 108 buffer[readSize] = '\0'; 109 110 const char err404[] = "HTTP/1.1 404 Not found\r\n\r\n"; 111 112 // XXX: HACK HACK HACK 113 const char *p = (const char *)buffer; 114 if (strncmp(p, "GET ", 4) == 0) { 115 p += 4; 116 const char *s = strchr(p, ' '); 117 if (s && strncmp(s, " HTTP/", 6) == 0) { 118 if (*p == '/') 119 p++; 120 BString path(p, s - p); 121 if (p == s) 122 path = "desktop.html"; 123 TRACE("searching handler for '%s'\n", path.String()); 124 WebHandler *handler = fHandlers.BinarySearchByKey( 125 path, &WebHandler::_CallbackCompare); 126 if (handler) { 127 TRACE("found handler '%s'\n", handler->Name().String()); 128 WebWorker *worker = 129 new (std::nothrow) WebWorker(endpoint, handler); 130 if (worker) { 131 fWorkers.AddItem(worker); 132 break; 133 } 134 } 135 } 136 } 137 138 // some error 139 endpoint->Send(err404, sizeof(err404) - 1); 140 delete endpoint; 141 break; 142 143#if 0 144 status_t result = fTarget->Write(buffer, readSize); 145 if (result != B_OK) { 146 TRACE_ERROR("writing to ring buffer failed: %s\n", 147 strerror(result)); 148 return result; 149 } 150#endif 151 } 152 } 153 154 return B_OK; 155} 156