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

Revision 568, 6.4 KB checked in by literalka, 16 months ago (diff)

tested, i think it works fine now

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