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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines