ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/repos/Smersh/Daemon.cpp
Revision: 176
Committed: 2004-06-25T20:32:24-07:00 (20 years, 11 months ago) by Douglas Thrift
File size: 4611 byte(s)
Log Message:
Almost working, needs error display, and a working api::TcpSocket::Close().

File Contents

# Content
1 // Smersh
2 //
3 // Douglas Thrift
4 //
5 // $Id$
6
7 #include "Daemon.hpp"
8
9 string Daemon::crlf("\r\n");
10
11 void Daemon::serve(int port, bool fork, Daemon* self)
12 {
13 api::TcpSocket server;
14
15 server.Create();
16 server.SetAddress(api::InternetAddress(api::InternetAddress::Any, port));
17
18 if (fork)
19 {
20 switch (::fork())
21 {
22 case -1:
23 cerr << program << ": fork()\n";
24
25 exit(1);
26 case 0:
27 break;
28 default:
29 return;
30 }
31 }
32
33 server.Listen(50);
34
35 while (true)
36 {
37 api::TcpSocket* client(new api::TcpSocket());
38
39 server.Accept(*client);
40
41 api::Thread thread(etl::BindAll(&Daemon::handle, self, client));
42 }
43 }
44
45 Daemon::Status Daemon::request(istream& sin, Environment& env, ostream& post,
46 ostream& log)
47 {
48 string line;
49 Matcher request("^([A-Z]+) .*?(\\?.+)? HTTP/(\\d+)\\.(\\d+)$");
50
51 getline(sin, line);
52
53 log << '"' << line << "\" ";
54
55 env.put("HTTP_REFERER=-");
56 env.put("HTTP_USER_AGENT=-");
57
58 if (line == request)
59 {
60 if (lexical_cast<unsigned>(request[3]) > 1) return version;
61
62 Matcher method("^GET|HEAD|POST$");
63
64 if (request[1] != method) return notImplemented;
65
66 env.set("REQUEST_METHOD", method);
67
68 if (!request[2].empty()) env.set("QUERY_STRING", request[2].substr(1));
69
70 headers(sin, env);
71
72 if (method[0] == "POST") return message(sin, env, post);
73
74 return ok;
75 }
76
77 return notFound;
78 }
79
80 void Daemon::response(ostream& sout, Status status)
81 {
82 sout << "HTTP/1.1 " << status << ' ';
83
84 switch (status)
85 {
86 case ok:
87 sout << "OK";
88 break;
89 case found:
90 sout << "Found";
91 break;
92 case seeOther:
93 sout << "See Other";
94 break;
95 case bad:
96 sout << "Bad Request";
97 break;
98 case notFound:
99 sout << "Not Found";
100 break;
101 case lengthRequired:
102 sout << "Length Required";
103 break;
104 case mediaType:
105 sout << "Unsupported Media Type";
106 break;
107 case serverError:
108 sout << "Internal Server Error";
109 break;
110 case notImplemented:
111 sout << "Not Implemented";
112 break;
113 case version:
114 sout << "HTTP Version not supported";
115 }
116
117 sout << crlf << "Date: " << date() << crlf << "Server: Smersh/0.9" << crlf
118 << "Connection: close" << crlf;
119 }
120
121 void Daemon::error(ostream& sout, Status status)
122 {
123 sout << crlf;
124 }
125
126 string Daemon::date(bool log)
127 {
128 time_t now(time(NULL));
129 tm time;
130
131 if (log) localtime_r(&now, &time); else gmtime_r(&now, &time);
132
133 const char* format(log ? "[%m/%d/%Y:%T %z]" : "%a, %d %b %Y %T GMT");
134 char when[log ? 29 : 30];
135
136 strftime(when, log ? 29 : 30, format, &time);
137
138 return when;
139 }
140
141 int Daemon::handle(api::TcpSocket* client)
142 {
143 ios::InputOutputStreamBufAdapter adapter(*client);
144 iostream sio(&adapter);
145 Environment env;
146 stringstream post;
147 ostringstream log;
148 Status code(request(sio, env, post, log));
149
150 response(sio, code);
151
152 if (code == ok)
153 {
154 ostringstream output;
155 Smersh smersh(post, output, env);
156 string content(output.str().substr(40));
157
158 sio << "Content-Length: " << content.length() << crlf
159 << "Content-Type: text/html; charset=UTF-8" << crlf << crlf;
160
161 sio.write(content.data(), content.size());
162 }
163 else if (env.get("REQUEST_METHOD") != "HEAD") error(sio, code);
164 else sio << "Content-Type: text/html; charset=UTF-8" << crlf << crlf;
165
166 log << code << ' ' << 0 << " \"" << env.get("HTTP_REFERER") << "\" \""
167 << env.get("HTTP_USER_AGENT") << '"';
168
169 if (debug) cerr << date(true) << ' ' << log.str() << '\n';
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 ::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 }

Properties

Name Value
svn:eol-style native
svn:keywords Id