ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/repos/Smersh/Daemon.cpp
Revision: 177
Committed: 2004-06-26T01:26:41-07:00 (20 years, 11 months ago) by Douglas Thrift
File size: 5490 byte(s)
Log Message:
z00t, need to make redirector and close sockets still!

File Contents

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

Properties

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