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 253 by Douglas Thrift, 2004-09-13T23:31:24-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(Daemon* self)
12   {
13 <        api::TcpSocket server;
13 >        ext::StackReference<api::TcpSocket> server;
14  
13        server.Create();
15          server.SetAddress(api::InternetAddress(api::InternetAddress::Any, port));
16 +        server.Listen(50);
17  
18 <        if (fork)
18 >        while (true)
19          {
20 <                switch (::fork())
21 <                {
20 <                case -1:
21 <                        cerr << program << ": fork()\n";
22 <
23 <                        exit(1);
24 <                case 0:
25 <                        break;
26 <                default:
27 <                        return;
28 <                }
20 >                Client* client(new Client(server));
21 >                api::Thread thread(etl::BindAll(&Daemon::handle, self, client));
22          }
23 + }
24  
25 <        server.Listen(50);
25 > Daemon::Status Daemon::request(istream& sin, Environment& env, ostream& post,
26 >        ostream& log)
27 > {
28 >        string line;
29 >        Matcher request("^([A-Z]+) (.*?)(\\?.+)? HTTP/(\\d+)\\.(\\d+)$");
30  
31 <        while (true)
31 >        getline(sin, line);
32 >
33 >        log << '"' << line << "\" ";
34 >
35 >        env.put("HTTP_REFERER=-");
36 >        env.put("HTTP_USER_AGENT=-");
37 >
38 >        if (line == request)
39          {
40 <                api::TcpSocket* client(new api::TcpSocket());
40 >                if (lexical_cast<unsigned>(request[4]) > 1) return version;
41  
42 <                server.Accept(*client);
42 >                Matcher method("^GET|HEAD|POST$");
43  
44 <                api::Thread thread(etl::BindAll(&Daemon::handle, self, client));
44 >                if (request[1] != method) return notImplemented;
45 >
46 >                env.set("REQUEST_METHOD", method);
47 >                env.set("REQUEST_URI", request[2] + request[3]);
48 >
49 >                if (!request[3].empty()) env.set("QUERY_STRING", request[3].substr(1));
50 >
51 >                headers(sin, env);
52 >
53 >                if (env.get("HTTP_HOST").empty()) return bad;
54 >                if (method[0] == "POST") return message(sin, env, post);
55 >
56 >                return ok;
57 >        }
58 >
59 >        return bad;
60 > }
61 >
62 > void Daemon::response(ostream& sout, Status status)
63 > {
64 >        sout << "HTTP/1.1 " << status << ' ' << reason(status) << crlf << "Date: "
65 >                << date() << crlf << "Server: " << server() << crlf
66 >                << "Connection: close" << crlf;
67 > }
68 >
69 > 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 + string Daemon::server()
110 + {
111 +        utsname system;
112 +
113 +        uname(&system);
114 +
115 +        return string("Smersh/0.9 (") + system.sysname + ')';
116 + }
117 +
118 + string Daemon::server(const Environment& env)
119 + {
120 +        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 +        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 +                << "hem.</p><hr /><address>" << server(env) << "</address></body></html"
137 +                << ">\r\n";
138 +        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 + }
143 +
144 + 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 < int Daemon::handle(api::TcpSocket* client)
159 > int Daemon::handle(Client* client)
160   {
161 <        ios::InputOutputStreamBufAdapter adapter(*client);
162 <        iostream socket(&adapter);
161 >        ios::ToIoStream sio(&client->socket, &client->socket);
162 >        Environment env;
163 >        stringstream post;
164 >        ostringstream log;
165 >        Status code(request(sio, env, post, log));
166 >
167 >        if (env.get("REQUEST_URI") == "/favicon.ico") code = notFound;
168 >
169 >        response(sio, code);
170 >
171 >        bool head(env.get("REQUEST_METHOD") == "HEAD");
172 >        streamsize sent(0);
173 >
174 >        if (code == ok && env.get("REQUEST_URI") == "/robots.txt")
175 >        {
176 >                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 >                ostringstream content;
184 >                Smersh smersh(post, content, env);
185 >
186 >                sio << "Content-Length: " << content.str().length() << crlf
187 >                        << "Content-Type: text/html; charset=UTF-8\r\n\r\n";
188 >
189 >                sio.write(content.str().data(), content.str().size());
190 >
191 >                sent = content.str().size();
192 >        }
193 >        else if (head) sio << "Content-Type: text/html; charset=UTF-8\r\n\r\n";
194 >        else sent = error(sio, code, env);
195  
196 <        //
196 >        ofstream fout(this->log.c_str(), ios_base::app);
197 >
198 >        fout << inet_ntoa(client->ip->sin_addr) << " - - " << date(true) << ' '
199 >                << log.str() << code << ' ' << lexical_cast<string>(sent) << " \""
200 >                << env.get("HTTP_REFERER") << "\" \"" << env.get("HTTP_USER_AGENT")
201 >                << "\"\n";
202 >        sio << flush;
203 >
204 >        client->socket.ShutdownWrite();
205  
206          delete client;
207 +
208 +        return 0;
209 + }
210 +
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 +                std::getline(input, name, ':');
225 +                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 +                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 +                                if (port) env.set("SERVER_PORT", matcher[2].substr(1));
257 +                        }
258 +                }
259 +                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 +
275 +        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   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines