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

# User Rev Content
1 Douglas Thrift 164 // Smersh
2     //
3     // Douglas Thrift
4     //
5     // $Id$
6    
7     #include "Daemon.hpp"
8    
9 Douglas Thrift 175 string Daemon::crlf("\r\n");
10    
11 Douglas Thrift 181 void Daemon::serve(bool fork, Daemon* self)
12 Douglas Thrift 164 {
13 Douglas Thrift 174 api::TcpSocket server;
14 Douglas Thrift 172
15 Douglas Thrift 174 server.SetAddress(api::InternetAddress(api::InternetAddress::Any, port));
16    
17 Douglas Thrift 172 if (fork)
18     {
19 Douglas Thrift 179 switch (pid_t pid = ::fork())
20 Douglas Thrift 172 {
21     case -1:
22     cerr << program << ": fork()\n";
23    
24     exit(1);
25     case 0:
26     break;
27     default:
28 Douglas Thrift 179 cout << pid << '\n';
29 Douglas Thrift 172 return;
30     }
31     }
32    
33 Douglas Thrift 174 server.Listen(50);
34 Douglas Thrift 173
35     while (true)
36     {
37 Douglas Thrift 184 Client* client (new Client(server));
38 Douglas Thrift 174 api::Thread thread(etl::BindAll(&Daemon::handle, self, client));
39 Douglas Thrift 173 }
40 Douglas Thrift 164 }
41    
42 Douglas Thrift 175 Daemon::Status Daemon::request(istream& sin, Environment& env, ostream& post,
43     ostream& log)
44     {
45     string line;
46 Douglas Thrift 180 Matcher request("^([A-Z]+) (.*?)(\\?.+)? HTTP/(\\d+)\\.(\\d+)$");
47 Douglas Thrift 175
48     getline(sin, line);
49    
50     log << '"' << line << "\" ";
51    
52 Douglas Thrift 176 env.put("HTTP_REFERER=-");
53     env.put("HTTP_USER_AGENT=-");
54    
55 Douglas Thrift 175 if (line == request)
56     {
57 Douglas Thrift 180 if (lexical_cast<unsigned>(request[4]) > 1) return version;
58 Douglas Thrift 175
59 Douglas Thrift 176 Matcher method("^GET|HEAD|POST$");
60 Douglas Thrift 175
61 Douglas Thrift 176 if (request[1] != method) return notImplemented;
62 Douglas Thrift 175
63 Douglas Thrift 176 env.set("REQUEST_METHOD", method);
64 Douglas Thrift 180 env.set("REQUEST_URI", request[2] + request[3]);
65 Douglas Thrift 175
66 Douglas Thrift 180 if (!request[3].empty()) env.set("QUERY_STRING", request[3].substr(1));
67 Douglas Thrift 175
68 Douglas Thrift 176 headers(sin, env);
69 Douglas Thrift 175
70 Douglas Thrift 181 if (env.get("HTTP_HOST").empty()) return bad;
71 Douglas Thrift 176 if (method[0] == "POST") return message(sin, env, post);
72 Douglas Thrift 175
73     return ok;
74     }
75    
76 Douglas Thrift 180 return bad;
77 Douglas Thrift 175 }
78    
79 Douglas Thrift 176 void Daemon::response(ostream& sout, Status status)
80 Douglas Thrift 175 {
81 Douglas Thrift 177 sout << "HTTP/1.1 " << status << ' ' << reason(status) << crlf << "Date: "
82 Douglas Thrift 178 << date() << crlf << "Server: " << server() << crlf
83 Douglas Thrift 176 << "Connection: close" << crlf;
84     }
85    
86 Douglas Thrift 180 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 Douglas Thrift 178 string Daemon::server()
127     {
128     utsname system;
129    
130     uname(&system);
131    
132     return string("Smersh/0.9 (") + system.sysname + ')';
133     }
134    
135 Douglas Thrift 181 string Daemon::server(const Environment& env)
136 Douglas Thrift 176 {
137 Douglas Thrift 181 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 Douglas Thrift 177 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 Douglas Thrift 181 << "hem.</p><hr /><address>" << server(env) << "</address></body></html"
154     << ">\r\n";
155 Douglas Thrift 177 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 Douglas Thrift 175 }
160    
161 Douglas Thrift 176 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 Douglas Thrift 179 int Daemon::handle(Client* client)
177 Douglas Thrift 177 {
178 Douglas Thrift 205 ios::ToIoStream sio(&client->socket, &client->socket);
179 Douglas Thrift 175 Environment env;
180     stringstream post;
181     ostringstream log;
182     Status code(request(sio, env, post, log));
183 Douglas Thrift 174
184 Douglas Thrift 180 if (env.get("REQUEST_URI") == "/favicon.ico") code = notFound;
185    
186 Douglas Thrift 176 response(sio, code);
187 Douglas Thrift 174
188 Douglas Thrift 177 bool head(env.get("REQUEST_METHOD") == "HEAD");
189     streamsize sent(0);
190    
191 Douglas Thrift 181 if (code == ok && env.get("REQUEST_URI") == "/robots.txt")
192 Douglas Thrift 176 {
193 Douglas Thrift 181 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 Douglas Thrift 180 ostringstream content;
201 Douglas Thrift 181 Smersh smersh(post, content, env);
202 Douglas Thrift 176
203 Douglas Thrift 182 sio << "Content-Length: " << content.str().length() << crlf
204 Douglas Thrift 177 << "Content-Type: text/html; charset=UTF-8\r\n\r\n";
205 Douglas Thrift 176
206 Douglas Thrift 182 sio.write(content.str().data(), content.str().size());
207 Douglas Thrift 177
208 Douglas Thrift 182 sent = content.str().size();
209 Douglas Thrift 176 }
210 Douglas Thrift 177 else if (head) sio << "Content-Type: text/html; charset=UTF-8\r\n\r\n";
211 Douglas Thrift 181 else sent = error(sio, code, env);
212 Douglas Thrift 176
213 Douglas Thrift 177 ofstream fout(this->log.c_str(), ios_base::app);
214 Douglas Thrift 175
215 Douglas Thrift 179 fout << inet_ntoa(client->ip->sin_addr) << " - - " << date(true) << ' '
216 Douglas Thrift 180 << log.str() << code << ' ' << lexical_cast<string>(sent) << " \""
217     << env.get("HTTP_REFERER") << "\" \"" << env.get("HTTP_USER_AGENT")
218     << "\"\n";
219 Douglas Thrift 178 sio << flush;
220 Douglas Thrift 177
221 Douglas Thrift 179 client->socket.ShutdownWrite();
222 Douglas Thrift 178
223 Douglas Thrift 174 delete client;
224 Douglas Thrift 164 }
225 Douglas Thrift 176
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 Douglas Thrift 178 std::getline(input, name, ':');
240 Douglas Thrift 176 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 Douglas Thrift 181 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 Douglas Thrift 176 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 Douglas Thrift 177
290 Douglas Thrift 176 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