source: trollforge/lastmeasure/server/lmserver.cpp @ 577

Revision 577, 6.3 KB checked in by literalka, 16 months ago (diff)

mostly TODO stuff

  • Property svn:keywords set to Id
Line 
1
2#include <string>
3#include <sstream>
4#include <iostream>
5
6#include <cstdio>
7#include <cstring>
8#include <cstdlib>
9
10#include <arpa/inet.h> /* bind() */
11
12#define PORT 31337
13#define BACKLOG 1000
14#define THREADS 10
15
16#include "lmutil.h"
17#include "lmresource.h"
18
19class Server;
20
21Server *g_server;
22
23class Worker
24{
25public:
26    Worker(Queue<BACKLOG> *queue) :
27        m_ready(0),
28        m_die(0),
29        m_queue(queue)
30    {
31        pthread_attr_t attr;
32        pthread_attr_init(&attr);
33        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
34        pthread_create(&m_thread, &attr, Helper, this);
35
36        while (!m_ready)
37            ; /* spinlock is OK */
38    }
39
40    ~Worker()
41    {
42        pthread_join(m_thread, NULL);
43    }
44
45    void HandleRequest(int client);
46
47private:
48    static void *Helper(void *data)
49    {
50        Worker *that = (Worker *)data;
51        that->m_ready = 1; /* signal main thread spinlock */
52
53        for (;;)
54        {
55            int client = that->m_queue->Pop();
56            if (client < 0)
57                return NULL;
58
59            that->HandleRequest(client);
60        }
61        return NULL;
62    }
63
64    pthread_t m_thread;
65    volatile int m_ready, m_die;
66    Queue<BACKLOG> *m_queue;
67};
68
69class Server
70{
71public:
72    Server(int port, int backlog, int nworkers) :
73        m_fd(-1),
74        m_port(port),
75        m_nworkers(nworkers),
76        m_requests(0),
77        m_failures(0),
78        m_starttime(time(NULL))
79    {
80        m_fd = socket(AF_INET, SOCK_STREAM, 0);
81        if (m_fd < 0)
82            Utils::OhShitShitShit("cannot create socket");
83
84        int reuse = 1;
85        int ret = setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR,
86                             &reuse, (socklen_t)sizeof(reuse));
87        if (ret < 0)
88            Utils::OhShitShitShit("cannot set reuse flag on socket");
89
90        struct sockaddr_in saddr;
91        saddr.sin_family = AF_INET;
92        saddr.sin_port = htons(m_port);
93        saddr.sin_addr.s_addr = htonl(INADDR_ANY);
94        ret = bind(m_fd, (struct sockaddr *)&saddr, sizeof(saddr));
95        if (ret < 0)
96            Utils::OhShitShitShit("cannot bind socket");
97
98        ret = listen(m_fd, backlog);
99        if (ret < 0)
100            Utils::OhShitShitShit("cannot listen to socket");
101
102        m_queue = new Queue<BACKLOG>();
103        m_workers = new Worker *[m_nworkers];
104        for (int i = 0; i < m_nworkers; i++)
105            m_workers[i] = new Worker(m_queue);
106    }
107
108    ~Server()
109    {
110        close(m_fd);
111        for (int i = 0; i < m_nworkers; i++)
112            m_queue->Push(-1);
113        for (int i = 0; i < m_nworkers; i++)
114            delete m_workers[i];
115        delete[] m_workers;
116        delete m_queue;
117    }
118
119    void Run()
120    {
121        for (;;)
122        {
123            int client = GetNextRequest();
124            if (client < 0)
125                continue;
126            m_queue->Push(client);
127        }
128    }
129
130    int GetNextRequest()
131    {
132        struct sockaddr_in caddr;
133        struct in_addr client_ip_addr;
134        socklen_t addr_len;
135        addr_len = sizeof(caddr);
136
137        int ret = accept(m_fd, (struct sockaddr *)&caddr, &addr_len);
138        if (ret < 0)
139            m_failures++;
140        else
141            m_requests++;
142
143        return ret;
144    }
145
146    std::string Status()
147    {
148        std::stringstream ret("");
149        ret << "<p>Server status: " << m_requests << " requests served.</p>";
150        ret << "<p>Uptime: " << (long)difftime(time(NULL), m_starttime) << " seconds.</p>";
151
152        return ret.str();
153    }
154
155private:
156    int m_fd, m_port, m_nworkers;
157    uint64_t m_requests, m_failures;
158    time_t m_starttime;
159    Queue<BACKLOG> *m_queue;
160    Worker **m_workers;
161};
162
163int main(void)
164{
165    g_server = new Server(PORT, BACKLOG, THREADS);
166
167    Utils::Chroot();
168    Utils::DropPrivileges();
169
170    Resources::LoadAll();
171
172    g_server->Run();
173
174    delete g_server;
175
176    return 0;
177}
178
179void Worker::HandleRequest(int client)
180{
181    enum { REQUEST_GET, REQUEST_POST, REQUEST_HEAD, REQUEST_UNKNOWN };
182
183    char inbuf[BUFSIZ];
184    int ret = recv(client, inbuf, BUFSIZ, 0);
185    if (ret < 0)
186    {
187        close(client);
188        return;
189    }
190    inbuf[ret] = '\0';
191
192    int request = !strncmp(inbuf, "GET ", 4) ? REQUEST_GET
193                : !strncmp(inbuf, "POST ", 5) ? REQUEST_POST
194                : !strncmp(inbuf, "HEAD ", 5) ? REQUEST_HEAD
195                : REQUEST_UNKNOWN;
196    if (request == REQUEST_UNKNOWN)
197    {
198        close(client);
199        return;
200    }
201
202    /* Find information in the request */
203    /* TODO: Perhaps make `query' an array. Example:
204     *       `?u=dongs&popup=1'
205     *       * query[0] = "u=dongs";
206     *       * query[1] = "popup=1";
207     */
208    char const *resource = "/", *query = "",
209               *agent = "Unknown", *host = "127.0.0.1";
210
211    char *resourceparser = strstr(inbuf, " ");
212    char *queryparser = NULL;
213    if (resourceparser)
214    {
215        resource = resourceparser + 1;
216        queryparser = strstr(resourceparser + 1, "?");
217        resourceparser = strstr(resourceparser + 1, " ");
218
219        if (queryparser && resourceparser && queryparser < resourceparser)
220        {
221            query = queryparser + 1;
222        }
223    }
224
225    char *agentparser = strstr(inbuf, "\r\nUser-Agent: ");
226    if (agentparser)
227    {
228        agent = agentparser + 14;
229        agentparser = strstr(agentparser + 14, "\r\n");
230    }
231
232    char *hostparser = strstr(inbuf, "\r\nHost: ");
233    if (hostparser)
234    {
235        host = hostparser + 8;
236        hostparser = strstr(hostparser + 8, "\r\n");
237    }
238
239    /* Put zeroes in the request for convenience */
240    if (resourceparser)
241        resourceparser[0] = '\0';
242    if (queryparser)
243        queryparser[0] = '\0';
244    if (agentparser)
245        agentparser[0] = '\0';
246    if (hostparser)
247        hostparser[0] = '\0';
248
249    /* Now build the response */
250    std::stringstream response("");
251    Response data = Resources::Get(resource, query, host, agent);
252    response << "HTTP/1.1 200 OK\r\n"
253             << "Date: " << Utils::Time() << "\r\n"
254             << "Server: " << Random::ServerName() << "\r\n"
255             << "Content-Length: " << data.first.length() << "\r\n"
256             //<< "Connection: Keep-Alive\r\n"
257             << "Connection: Close\r\n"
258             << "Content-Type: " << data.second << "\r\n"
259             << "\r\n"
260             << data.first;
261
262    send(client, response.str().c_str(), response.str().length(), 0);
263
264    /* FIXME: we could reuse the connection here */
265    close(client);
266}
267
Note: See TracBrowser for help on using the repository browser.