ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/repos/Smersh/Daemon.cpp
(Generate patch)

Comparing Smersh/Daemon.cpp (file contents):
Revision 174 by Douglas Thrift, 2004-06-21T20:16:43-07:00 vs.
Revision 184 by Douglas Thrift, 2004-07-13T21:43:09-07:00

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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines