ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/repos/Smersh/Daemon.cpp
Revision: 205
Committed: 2004-09-01T02:31:03-07:00 (20 years, 9 months ago) by Douglas Thrift
File size: 6457 byte(s)
Log Message:
Update to latest menes API, remove Linux stuff from GNUmakefile.

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

Properties

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