ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/repos/Smersh/Daemon.cpp
Revision: 182
Committed: 2004-07-03T06:52:04-07:00 (20 years, 11 months ago) by Douglas Thrift
File size: 6543 byte(s)
Log Message:
Redirector redirects, nicely!

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