]> git.tdb.fi Git - libs/net.git/blob - source/http/server.cpp
Adhere to the spec with connection keep-alive
[libs/net.git] / source / http / server.cpp
1 #include <exception>
2 #include <msp/core/maputils.h>
3 #include <msp/core/refptr.h>
4 #include <msp/net/inet.h>
5 #include <msp/net/resolve.h>
6 #include <msp/net/streamsocket.h>
7 #include <msp/strings/format.h>
8 #include <msp/strings/utils.h>
9 #include "request.h"
10 #include "response.h"
11 #include "server.h"
12
13 using namespace std;
14
15 namespace Msp {
16 namespace Http {
17
18 Server::Server(unsigned port):
19         sock(Net::INET),
20         event_disp(0)
21 {
22         sock.signal_data_available.connect(sigc::mem_fun(this, &Server::data_available));
23         RefPtr<Net::SockAddr> addr = Net::resolve("*", format("%d", port));
24         sock.listen(*addr, 8);
25 }
26
27 // Avoid emitting sigc::signal destructor in files including server.h
28 Server::~Server()
29 {
30 }
31
32 unsigned Server::get_port() const
33 {
34         const Net::SockAddr &addr = sock.get_local_address();
35         if(addr.get_family()==Net::INET)
36                 return static_cast<const Net::InetAddr &>(addr).get_port();
37         return 0;
38 }
39
40 void Server::use_event_dispatcher(IO::EventDispatcher *ed)
41 {
42         if(event_disp)
43         {
44                 event_disp->remove(sock);
45                 for(list<Client>::iterator i=clients.begin(); i!=clients.end(); ++i)
46                         event_disp->remove(*i->sock);
47         }
48         event_disp = ed;
49         if(event_disp)
50         {
51                 event_disp->add(sock);
52                 for(list<Client>::iterator i=clients.begin(); i!=clients.end(); ++i)
53                         event_disp->add(*i->sock);
54         }
55 }
56
57 void Server::delay_response(Response &resp)
58 {
59         get_client_by_response(resp).async = true;
60 }
61
62 void Server::submit_response(Response &resp)
63 {
64         Client &cl = get_client_by_response(resp);
65         if(cl.async)
66                 send_response(cl, *cl.response);
67 }
68
69 void Server::data_available()
70 {
71         Net::StreamSocket *csock = sock.accept();
72         clients.push_back(Client(csock));
73         csock->signal_data_available.connect(sigc::bind(sigc::mem_fun(this, &Server::client_data_available), sigc::ref(clients.back())));
74         csock->signal_end_of_file.connect(sigc::bind(sigc::mem_fun(this, &Server::client_end_of_file), sigc::ref(clients.back())));
75         if(event_disp)
76                 event_disp->add(*csock);
77 }
78
79 void Server::client_data_available(Client &cl)
80 {
81         for(list<Client>::iterator i=clients.begin(); i!=clients.end(); ++i)
82                 if(i->stale && &*i!=&cl)
83                 {
84                         clients.erase(i);
85                         break;
86                 }
87
88         char rbuf[4096];
89         unsigned len = cl.sock->read(rbuf, sizeof(rbuf));
90         if(cl.stale)
91                 return;
92         cl.in_buf.append(rbuf, len);
93
94         RefPtr<Response> response;
95         if(!cl.request)
96         {
97                 if(cl.in_buf.find("\r\n\r\n")!=string::npos || cl.in_buf.find("\n\n")!=string::npos)
98                 {
99                         try
100                         {
101                                 cl.request = new Request(Request::parse(cl.in_buf));
102
103                                 string addr_str = cl.sock->get_peer_address().str();
104                                 unsigned colon = addr_str.find(':');
105                                 cl.request->set_header("-Client-Host", addr_str.substr(0, colon));
106
107                                 if(cl.request->get_method()!="GET" && cl.request->get_method()!="POST")
108                                 {
109                                         response = new Response(NOT_IMPLEMENTED);
110                                         response->add_content("Method not implemented\n");
111                                 }
112                                 else if(cl.request->get_path()[0]!='/')
113                                 {
114                                         response = new Response(BAD_REQUEST);
115                                         response->add_content("Path must be absolute\n");
116                                 }
117                         }
118                         catch(const exception &e)
119                         {
120                                 response = new Response(BAD_REQUEST);
121                                 response->add_content(e.what());
122                         }
123                         cl.in_buf = string();
124                 }
125         }
126         else
127         {
128                 len = cl.request->parse_content(cl.in_buf);
129                 cl.in_buf.erase(0, len);
130         }
131
132         if(cl.request && cl.request->is_complete() && !response)
133         {
134                 cl.keepalive = false;
135                 if(cl.request->has_header("Connection"))
136                         cl.keepalive = !strcasecmp(cl.request->get_header("Connection"), "keep-alive");
137
138                 response = new Response(NONE);
139                 try
140                 {
141                         cl.response = response.get();
142                         responses[cl.response] = &cl;
143                         signal_request.emit(*cl.request, *response);
144                         if(cl.async)
145                                 response.release();
146                         else
147                         {
148                                 responses.erase(cl.response);
149                                 cl.response = 0;
150                                 if(response->get_status()==NONE)
151                                 {
152                                         response = new Response(NOT_FOUND);
153                                         response->add_content("The requested resource was not found\n");
154                                 }
155                         }
156                 }
157                 catch(const exception &e)
158                 {
159                         responses.erase(cl.response);
160                         cl.response = 0;
161                         response = new Response(INTERNAL_ERROR);
162                         response->add_content(e.what());
163                 }
164         }
165
166         if(response)
167                 send_response(cl, *response);
168 }
169
170 void Server::send_response(Client &cl, Response &resp)
171 {
172         if(cl.keepalive)
173                 resp.set_header("Connection", "keep-alive");
174         cl.sock->write(resp.str());
175         cl.async = false;
176         if(cl.keepalive)
177         {
178                 delete cl.request;
179                 cl.request = 0;
180                 delete cl.response;
181                 cl.response = 0;
182         }
183         else
184         {
185                 cl.sock->shutdown(IO::M_WRITE);
186                 cl.stale = true;
187         }
188 }
189
190 void Server::client_end_of_file(Client &cl)
191 {
192         cl.stale = true;
193 }
194
195 Server::Client &Server::get_client_by_response(Response &resp)
196 {
197         return *get_item(responses, &resp);
198 }
199
200
201 Server::Client::Client(RefPtr<Net::StreamSocket> s):
202         sock(s),
203         request(0),
204         response(0),
205         keepalive(false),
206         async(false),
207         stale(false)
208 { }
209
210 Server::Client::~Client()
211 {
212         delete request;
213         delete response;
214 }
215
216 } // namespace Http
217 } // namespace Msp