ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/repos/Smersh/Daemon.cpp
Revision: 180
Committed: 2004-07-03T04:12:01-07:00 (20 years, 11 months ago) by Douglas Thrift
File size: 5782 byte(s)
Log Message:
Going nowhere, fast?

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 173 void Daemon::serve(int port, 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 176 if (method[0] == "POST") return message(sin, env, post);
75 Douglas Thrift 175
76     return ok;
77     }
78    
79 Douglas Thrift 180 return bad;
80 Douglas Thrift 175 }
81    
82 Douglas Thrift 176 void Daemon::response(ostream& sout, Status status)
83 Douglas Thrift 175 {
84 Douglas Thrift 177 sout << "HTTP/1.1 " << status << ' ' << reason(status) << crlf << "Date: "
85 Douglas Thrift 178 << date() << crlf << "Server: " << server() << crlf
86 Douglas Thrift 176 << "Connection: close" << crlf;
87     }
88    
89 Douglas Thrift 180 string Daemon::reason(Status status)
90     {
91     ostringstream sout;
92    
93     switch (status)
94     {
95     case ok:
96     sout << "OK";
97     break;
98     case found:
99     sout << "Found";
100     break;
101     case seeOther:
102     sout << "See Other";
103     break;
104     case bad:
105     sout << "Bad Request";
106     break;
107     case notFound:
108     sout << "Not Found";
109     break;
110     case lengthRequired:
111     sout << "Length Required";
112     break;
113     case mediaType:
114     sout << "Unsupported Media Type";
115     break;
116     case serverError:
117     sout << "Internal Server Error";
118     break;
119     case notImplemented:
120     sout << "Not Implemented";
121     break;
122     case version:
123     sout << "HTTP Version not supported";
124     }
125    
126     return sout.str();
127     }
128    
129 Douglas Thrift 178 string Daemon::server()
130     {
131     utsname system;
132    
133     uname(&system);
134    
135     return string("Smersh/0.9 (") + system.sysname + ')';
136     }
137    
138 Douglas Thrift 177 streamsize Daemon::error(ostream& sout, Status status)
139 Douglas Thrift 176 {
140 Douglas Thrift 177 string reason(this->reason(status));
141     ostringstream error;
142    
143     error << "<html><head><title>" << status << ' ' << reason << "</title></hea"
144     << "d><body><h1>" << reason << "</h1><p>Mistakes were made, deal with t"
145 Douglas Thrift 178 << "hem.</p><hr /><address>" << server() + "</address></body></html>\n";
146 Douglas Thrift 177 sout << "Content-Length: " << error.str().length() << crlf
147     << "Content-Type: text/html; charset=UTF-8\r\n\r\n" << error.str();
148    
149     return error.str().size();
150 Douglas Thrift 175 }
151    
152 Douglas Thrift 176 string Daemon::date(bool log)
153     {
154     time_t now(time(NULL));
155     tm time;
156    
157     if (log) localtime_r(&now, &time); else gmtime_r(&now, &time);
158    
159     const char* format(log ? "[%m/%d/%Y:%T %z]" : "%a, %d %b %Y %T GMT");
160     char when[log ? 29 : 30];
161    
162     strftime(when, log ? 29 : 30, format, &time);
163    
164     return when;
165     }
166    
167 Douglas Thrift 179 int Daemon::handle(Client* client)
168 Douglas Thrift 177 {
169 Douglas Thrift 179 ios::InputOutputStreamBufAdapter adapter(client->socket);
170 Douglas Thrift 175 iostream sio(&adapter);
171     Environment env;
172     stringstream post;
173     ostringstream log;
174     Status code(request(sio, env, post, log));
175 Douglas Thrift 174
176 Douglas Thrift 180 if (env.get("REQUEST_URI") == "/favicon.ico") code = notFound;
177    
178 Douglas Thrift 176 response(sio, code);
179 Douglas Thrift 174
180 Douglas Thrift 177 bool head(env.get("REQUEST_METHOD") == "HEAD");
181     streamsize sent(0);
182    
183     if (code == ok && !head)
184 Douglas Thrift 176 {
185 Douglas Thrift 180 ostringstream content;
186 Douglas Thrift 176
187 Douglas Thrift 180 if (env.get("REQUEST_URI") == "/robots.txt")
188     {
189     content << string(40, '#') << "User-agent: *\r\nDisallow: /\r\n";
190     }
191     else Smersh smersh(post, content, env);
192    
193     sio << "Content-Length: " << content.str().substr(40).length() << crlf
194 Douglas Thrift 177 << "Content-Type: text/html; charset=UTF-8\r\n\r\n";
195 Douglas Thrift 176
196 Douglas Thrift 180 sio.write(content.str().substr(40).data(), content.str().size() - 40);
197 Douglas Thrift 177
198 Douglas Thrift 180 sent = content.str().size() - 40;
199 Douglas Thrift 176 }
200 Douglas Thrift 177 else if (head) sio << "Content-Type: text/html; charset=UTF-8\r\n\r\n";
201     else sent = error(sio, code);
202 Douglas Thrift 176
203 Douglas Thrift 177 ofstream fout(this->log.c_str(), ios_base::app);
204 Douglas Thrift 175
205 Douglas Thrift 179 fout << inet_ntoa(client->ip->sin_addr) << " - - " << date(true) << ' '
206 Douglas Thrift 180 << log.str() << code << ' ' << lexical_cast<string>(sent) << " \""
207     << env.get("HTTP_REFERER") << "\" \"" << env.get("HTTP_USER_AGENT")
208     << "\"\n";
209 Douglas Thrift 178 sio << flush;
210 Douglas Thrift 177
211 Douglas Thrift 179 client->socket.ShutdownWrite();
212 Douglas Thrift 178
213 Douglas Thrift 174 delete client;
214 Douglas Thrift 164 }
215 Douglas Thrift 176
216     void Daemon::headers(istream& sin, Environment& env)
217     {
218     do
219     {
220     string line;
221    
222     getline(sin, line);
223    
224     if (line.empty()) break;
225    
226     istringstream input(line);
227     string name, value;
228    
229 Douglas Thrift 178 std::getline(input, name, ':');
230 Douglas Thrift 176 getline(input, value);
231    
232     for (char next(sin.peek()); next == ' ' || next == '\t'; next =
233     sin.peek())
234     {
235     getline(sin, line);
236    
237     value += ' ' + line;
238     }
239    
240     Matcher matcher("^\\s*(.+)\\s*$");
241    
242     if (name == "Content-Length")
243     {
244     if (value == matcher) env.set("CONTENT_LENGTH", matcher[1]);
245     }
246     else if (name == "Content-Type")
247     {
248     if (value == matcher) env.set("CONTENT_TYPE", matcher[1]);
249     }
250     else if (name == "Referer")
251     {
252     if (value == matcher) env.set("HTTP_REFERER", matcher[1]);
253     }
254     else if (name == "User-Agent")
255     {
256     if (value == matcher) env.set("HTTP_USER_AGENT", matcher[1]);
257     }
258     }
259     while (sin.good());
260     }
261    
262     Daemon::Status Daemon::message(istream& sin, Environment& env, ostream& post)
263     {
264     string contentLength(env.get("CONTENT_LENGTH"));
265 Douglas Thrift 177
266 Douglas Thrift 176 if (env.get("CONTENT_TYPE") != "application/x-www-form-urlencoded") return
267     mediaType;
268     if (contentLength.empty()) return lengthRequired;
269    
270     streamsize length(lexical_cast<streamsize>(contentLength));
271     char* content(new char[length]);
272    
273     sin.read(content, length);
274     post.write(content, length);
275    
276     delete [] content;
277    
278     return ok;
279     }

Properties

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