ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/repos/Smersh/Daemon.cpp
Revision: 349
Committed: 2004-12-16T18:15:34-08:00 (20 years, 6 months ago) by douglas
File size: 6304 byte(s)
Log Message:
Horribly broken, including parts of menes.

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

Properties

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