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