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

# User Rev Content
1 Douglas Thrift 164 // Smersh
2     //
3     // Douglas Thrift
4     //
5     // $Id$
6    
7 Douglas Thrift 177 #include <arpa/inet.h>
8    
9 Douglas Thrift 164 #include "Daemon.hpp"
10    
11 Douglas Thrift 175 string Daemon::crlf("\r\n");
12    
13 Douglas Thrift 173 void Daemon::serve(int port, bool fork, Daemon* self)
14 Douglas Thrift 164 {
15 Douglas Thrift 174 api::TcpSocket server;
16 Douglas Thrift 172
17 Douglas Thrift 174 server.Create();
18     server.SetAddress(api::InternetAddress(api::InternetAddress::Any, port));
19    
20 Douglas Thrift 172 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 Douglas Thrift 174 server.Listen(50);
36 Douglas Thrift 173
37     while (true)
38     {
39 Douglas Thrift 174 api::TcpSocket* client(new api::TcpSocket());
40 Douglas Thrift 173
41 Douglas Thrift 174 server.Accept(*client);
42    
43     api::Thread thread(etl::BindAll(&Daemon::handle, self, client));
44 Douglas Thrift 173 }
45 Douglas Thrift 164 }
46    
47 Douglas Thrift 175 Daemon::Status Daemon::request(istream& sin, Environment& env, ostream& post,
48     ostream& log)
49     {
50     string line;
51 Douglas Thrift 176 Matcher request("^([A-Z]+) .*?(\\?.+)? HTTP/(\\d+)\\.(\\d+)$");
52 Douglas Thrift 175
53     getline(sin, line);
54    
55     log << '"' << line << "\" ";
56    
57 Douglas Thrift 176 env.put("HTTP_REFERER=-");
58     env.put("HTTP_USER_AGENT=-");
59    
60 Douglas Thrift 175 if (line == request)
61     {
62 Douglas Thrift 176 if (lexical_cast<unsigned>(request[3]) > 1) return version;
63 Douglas Thrift 175
64 Douglas Thrift 176 Matcher method("^GET|HEAD|POST$");
65 Douglas Thrift 175
66 Douglas Thrift 176 if (request[1] != method) return notImplemented;
67 Douglas Thrift 175
68 Douglas Thrift 176 env.set("REQUEST_METHOD", method);
69 Douglas Thrift 175
70 Douglas Thrift 176 if (!request[2].empty()) env.set("QUERY_STRING", request[2].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     return notFound;
80     }
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     << date() << crlf << "Server: Smersh/0.9" << crlf
86 Douglas Thrift 176 << "Connection: close" << crlf;
87     }
88    
89 Douglas Thrift 177 streamsize Daemon::error(ostream& sout, Status status)
90 Douglas Thrift 176 {
91 Douglas Thrift 177 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 Douglas Thrift 175 }
102    
103 Douglas Thrift 176 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 Douglas Thrift 177 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 Douglas Thrift 174 int Daemon::handle(api::TcpSocket* client)
126 Douglas Thrift 164 {
127 Douglas Thrift 174 ios::InputOutputStreamBufAdapter adapter(*client);
128 Douglas Thrift 175 iostream sio(&adapter);
129     Environment env;
130     stringstream post;
131     ostringstream log;
132     Status code(request(sio, env, post, log));
133 Douglas Thrift 174
134 Douglas Thrift 176 response(sio, code);
135 Douglas Thrift 174
136 Douglas Thrift 177 bool head(env.get("REQUEST_METHOD") == "HEAD");
137     streamsize sent(0);
138    
139     if (code == ok && !head)
140 Douglas Thrift 176 {
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 Douglas Thrift 177 << "Content-Type: text/html; charset=UTF-8\r\n\r\n";
147 Douglas Thrift 176
148     sio.write(content.data(), content.size());
149 Douglas Thrift 177
150     sent = content.size();
151 Douglas Thrift 176 }
152 Douglas Thrift 177 else if (head) sio << "Content-Type: text/html; charset=UTF-8\r\n\r\n";
153     else sent = error(sio, code);
154 Douglas Thrift 176
155 Douglas Thrift 177 log << code << ' ' << (sent > 0 ? lexical_cast<string>(sent) : string("0"))
156     << " \"" << env.get("HTTP_REFERER") << "\" \""
157 Douglas Thrift 175 << env.get("HTTP_USER_AGENT") << '"';
158    
159 Douglas Thrift 177 ofstream fout(this->log.c_str(), ios_base::app);
160 Douglas Thrift 175
161 Douglas Thrift 177 fout << ip(*client) << " - - " << date(true) << ' ' << log.str() << '\n';
162    
163 Douglas Thrift 174 delete client;
164 Douglas Thrift 164 }
165 Douglas Thrift 176
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 Douglas Thrift 177
216 Douglas Thrift 176 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 Douglas Thrift 177
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