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 172 by Douglas Thrift, 2004-06-20T22:47:13-07:00 vs.
Revision 179 by Douglas Thrift, 2004-07-02T21:16:12-07:00

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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines