ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/repos/Smersh/Daemon.cpp
Revision: 181
Committed: 2004-07-03T05:41:00-07:00 (20 years, 11 months ago) by Douglas Thrift
File size: 6575 byte(s)
Log Message:
Better server signature, HTTP Host, correct robots.txt.

File Contents

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

Properties

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