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