ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/repos/Smersh/Daemon.cpp
Revision: 178
Committed: 2004-06-29T13:15:51-07:00 (20 years, 11 months ago) by Douglas Thrift
File size: 5689 byte(s)
Log Message:
Works better, but dies.

File Contents

# User Rev Content
1 Douglas Thrift 164 // Smersh
2     //
3     // Douglas Thrift
4     //
5     // $Id$
6    
7 Douglas Thrift 177 #include <arpa/inet.h>
8 Douglas Thrift 178 #include <sys/utsname.h>
9 Douglas Thrift 177
10 Douglas Thrift 164 #include "Daemon.hpp"
11    
12 Douglas Thrift 175 string Daemon::crlf("\r\n");
13    
14 Douglas Thrift 173 void Daemon::serve(int port, bool fork, Daemon* self)
15 Douglas Thrift 164 {
16 Douglas Thrift 174 api::TcpSocket server;
17 Douglas Thrift 172
18 Douglas Thrift 174 server.Create();
19     server.SetAddress(api::InternetAddress(api::InternetAddress::Any, port));
20    
21 Douglas Thrift 172 if (fork)
22     {
23     switch (::fork())
24     {
25     case -1:
26     cerr << program << ": fork()\n";
27    
28     exit(1);
29     case 0:
30     break;
31     default:
32     return;
33     }
34     }
35    
36 Douglas Thrift 174 server.Listen(50);
37 Douglas Thrift 173
38     while (true)
39     {
40 Douglas Thrift 174 api::TcpSocket* client(new api::TcpSocket());
41 Douglas Thrift 173
42 Douglas Thrift 174 server.Accept(*client);
43    
44     api::Thread thread(etl::BindAll(&Daemon::handle, self, client));
45 Douglas Thrift 173 }
46 Douglas Thrift 164 }
47    
48 Douglas Thrift 175 Daemon::Status Daemon::request(istream& sin, Environment& env, ostream& post,
49     ostream& log)
50     {
51     string line;
52 Douglas Thrift 176 Matcher request("^([A-Z]+) .*?(\\?.+)? HTTP/(\\d+)\\.(\\d+)$");
53 Douglas Thrift 175
54     getline(sin, line);
55    
56     log << '"' << line << "\" ";
57    
58 Douglas Thrift 176 env.put("HTTP_REFERER=-");
59     env.put("HTTP_USER_AGENT=-");
60    
61 Douglas Thrift 175 if (line == request)
62     {
63 Douglas Thrift 176 if (lexical_cast<unsigned>(request[3]) > 1) return version;
64 Douglas Thrift 175
65 Douglas Thrift 176 Matcher method("^GET|HEAD|POST$");
66 Douglas Thrift 175
67 Douglas Thrift 176 if (request[1] != method) return notImplemented;
68 Douglas Thrift 175
69 Douglas Thrift 176 env.set("REQUEST_METHOD", method);
70 Douglas Thrift 175
71 Douglas Thrift 176 if (!request[2].empty()) env.set("QUERY_STRING", request[2].substr(1));
72 Douglas Thrift 175
73 Douglas Thrift 176 headers(sin, env);
74 Douglas Thrift 175
75 Douglas Thrift 176 if (method[0] == "POST") return message(sin, env, post);
76 Douglas Thrift 175
77     return ok;
78     }
79    
80     return notFound;
81     }
82    
83 Douglas Thrift 176 void Daemon::response(ostream& sout, Status status)
84 Douglas Thrift 175 {
85 Douglas Thrift 177 sout << "HTTP/1.1 " << status << ' ' << reason(status) << crlf << "Date: "
86 Douglas Thrift 178 << date() << crlf << "Server: " << server() << crlf
87 Douglas Thrift 176 << "Connection: close" << crlf;
88     }
89    
90 Douglas Thrift 178 string Daemon::server()
91     {
92     utsname system;
93    
94     uname(&system);
95    
96     return string("Smersh/0.9 (") + system.sysname + ')';
97     }
98    
99 Douglas Thrift 177 streamsize Daemon::error(ostream& sout, Status status)
100 Douglas Thrift 176 {
101 Douglas Thrift 177 string reason(this->reason(status));
102     ostringstream error;
103    
104     error << "<html><head><title>" << status << ' ' << reason << "</title></hea"
105     << "d><body><h1>" << reason << "</h1><p>Mistakes were made, deal with t"
106 Douglas Thrift 178 << "hem.</p><hr /><address>" << server() + "</address></body></html>\n";
107 Douglas Thrift 177 sout << "Content-Length: " << error.str().length() << crlf
108     << "Content-Type: text/html; charset=UTF-8\r\n\r\n" << error.str();
109    
110     return error.str().size();
111 Douglas Thrift 175 }
112    
113 Douglas Thrift 176 string Daemon::date(bool log)
114     {
115     time_t now(time(NULL));
116     tm time;
117    
118     if (log) localtime_r(&now, &time); else gmtime_r(&now, &time);
119    
120     const char* format(log ? "[%m/%d/%Y:%T %z]" : "%a, %d %b %Y %T GMT");
121     char when[log ? 29 : 30];
122    
123     strftime(when, log ? 29 : 30, format, &time);
124    
125     return when;
126     }
127    
128 Douglas Thrift 177 string Daemon::ip(const api::TcpSocket& socket)
129     {
130     api::InternetAddress address(socket.GetAddress());
131    
132     return inet_ntoa(address->sin_addr);
133     }
134    
135 Douglas Thrift 174 int Daemon::handle(api::TcpSocket* client)
136 Douglas Thrift 164 {
137 Douglas Thrift 174 ios::InputOutputStreamBufAdapter adapter(*client);
138 Douglas Thrift 175 iostream sio(&adapter);
139     Environment env;
140     stringstream post;
141     ostringstream log;
142     Status code(request(sio, env, post, log));
143 Douglas Thrift 174
144 Douglas Thrift 176 response(sio, code);
145 Douglas Thrift 174
146 Douglas Thrift 177 bool head(env.get("REQUEST_METHOD") == "HEAD");
147     streamsize sent(0);
148    
149     if (code == ok && !head)
150 Douglas Thrift 176 {
151     ostringstream output;
152     Smersh smersh(post, output, env);
153     string content(output.str().substr(40));
154    
155     sio << "Content-Length: " << content.length() << crlf
156 Douglas Thrift 177 << "Content-Type: text/html; charset=UTF-8\r\n\r\n";
157 Douglas Thrift 176
158     sio.write(content.data(), content.size());
159 Douglas Thrift 177
160     sent = content.size();
161 Douglas Thrift 176 }
162 Douglas Thrift 177 else if (head) sio << "Content-Type: text/html; charset=UTF-8\r\n\r\n";
163     else sent = error(sio, code);
164 Douglas Thrift 176
165 Douglas Thrift 177 log << code << ' ' << (sent > 0 ? lexical_cast<string>(sent) : string("0"))
166     << " \"" << env.get("HTTP_REFERER") << "\" \""
167 Douglas Thrift 175 << env.get("HTTP_USER_AGENT") << '"';
168    
169 Douglas Thrift 177 ofstream fout(this->log.c_str(), ios_base::app);
170 Douglas Thrift 175
171 Douglas Thrift 177 fout << ip(*client) << " - - " << date(true) << ' ' << log.str() << '\n';
172 Douglas Thrift 178 sio << flush;
173 Douglas Thrift 177
174 Douglas Thrift 178 client->ShutdownWrite();
175    
176 Douglas Thrift 174 delete client;
177 Douglas Thrift 164 }
178 Douglas Thrift 176
179     void Daemon::headers(istream& sin, Environment& env)
180     {
181     do
182     {
183     string line;
184    
185     getline(sin, line);
186    
187     if (line.empty()) break;
188    
189     istringstream input(line);
190     string name, value;
191    
192 Douglas Thrift 178 std::getline(input, name, ':');
193 Douglas Thrift 176 getline(input, value);
194    
195     for (char next(sin.peek()); next == ' ' || next == '\t'; next =
196     sin.peek())
197     {
198     getline(sin, line);
199    
200     value += ' ' + line;
201     }
202    
203     Matcher matcher("^\\s*(.+)\\s*$");
204    
205     if (name == "Content-Length")
206     {
207     if (value == matcher) env.set("CONTENT_LENGTH", matcher[1]);
208     }
209     else if (name == "Content-Type")
210     {
211     if (value == matcher) env.set("CONTENT_TYPE", matcher[1]);
212     }
213     else if (name == "Referer")
214     {
215     if (value == matcher) env.set("HTTP_REFERER", matcher[1]);
216     }
217     else if (name == "User-Agent")
218     {
219     if (value == matcher) env.set("HTTP_USER_AGENT", matcher[1]);
220     }
221     }
222     while (sin.good());
223     }
224    
225     Daemon::Status Daemon::message(istream& sin, Environment& env, ostream& post)
226     {
227     string contentLength(env.get("CONTENT_LENGTH"));
228 Douglas Thrift 177
229 Douglas Thrift 176 if (env.get("CONTENT_TYPE") != "application/x-www-form-urlencoded") return
230     mediaType;
231     if (contentLength.empty()) return lengthRequired;
232    
233     streamsize length(lexical_cast<streamsize>(contentLength));
234     char* content(new char[length]);
235    
236     sin.read(content, length);
237     post.write(content, length);
238    
239     delete [] content;
240    
241     return ok;
242     }
243 Douglas Thrift 177
244     string Daemon::reason(Status status)
245     {
246     ostringstream sout;
247    
248     switch (status)
249     {
250     case ok:
251     sout << "OK";
252     break;
253     case found:
254     sout << "Found";
255     break;
256     case seeOther:
257     sout << "See Other";
258     break;
259     case bad:
260     sout << "Bad Request";
261     break;
262     case notFound:
263     sout << "Not Found";
264     break;
265     case lengthRequired:
266     sout << "Length Required";
267     break;
268     case mediaType:
269     sout << "Unsupported Media Type";
270     break;
271     case serverError:
272     sout << "Internal Server Error";
273     break;
274     case notImplemented:
275     sout << "Not Implemented";
276     break;
277     case version:
278     sout << "HTTP Version not supported";
279     }
280    
281     return sout.str();
282     }

Properties

Name Value
svn:eol-style native
svn:keywords Id