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 177 by Douglas Thrift, 2004-06-26T01:26:41-07:00

# Line 4 | Line 4
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;
# Line 40 | Line 44 | void Daemon::serve(int port, bool fork,
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 socket(&adapter);
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 <        //
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 + }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines