--- Smersh/Daemon.cpp 2004/06/26 08:26:41 177 +++ Smersh/Daemon.cpp 2004/09/12 03:59:56 241 @@ -4,22 +4,19 @@ // // $Id$ -#include - #include "Daemon.hpp" string Daemon::crlf("\r\n"); -void Daemon::serve(int port, bool fork, Daemon* self) +void Daemon::serve(bool fork, Daemon* self) { api::TcpSocket server; - server.Create(); server.SetAddress(api::InternetAddress(api::InternetAddress::Any, port)); if (fork) { - switch (::fork()) + switch (pid_t pid = ::fork()) { case -1: cerr << program << ": fork()\n"; @@ -28,6 +25,7 @@ void Daemon::serve(int port, bool fork, case 0: break; default: + cout << pid << '\n'; return; } } @@ -36,10 +34,7 @@ void Daemon::serve(int port, bool fork, while (true) { - api::TcpSocket* client(new api::TcpSocket()); - - server.Accept(*client); - + Client* client (new Client(server)); api::Thread thread(etl::BindAll(&Daemon::handle, self, client)); } } @@ -48,7 +43,7 @@ Daemon::Status Daemon::request(istream& ostream& log) { string line; - Matcher request("^([A-Z]+) .*?(\\?.+)? HTTP/(\\d+)\\.(\\d+)$"); + Matcher request("^([A-Z]+) (.*?)(\\?.+)? HTTP/(\\d+)\\.(\\d+)$"); getline(sin, line); @@ -59,41 +54,104 @@ Daemon::Status Daemon::request(istream& if (line == request) { - if (lexical_cast(request[3]) > 1) return version; + if (lexical_cast(request[4]) > 1) return version; Matcher method("^GET|HEAD|POST$"); if (request[1] != method) return notImplemented; env.set("REQUEST_METHOD", method); + env.set("REQUEST_URI", request[2] + request[3]); - if (!request[2].empty()) env.set("QUERY_STRING", request[2].substr(1)); + if (!request[3].empty()) env.set("QUERY_STRING", request[3].substr(1)); headers(sin, env); + if (env.get("HTTP_HOST").empty()) return bad; if (method[0] == "POST") return message(sin, env, post); return ok; } - return notFound; + return bad; } void Daemon::response(ostream& sout, Status status) { sout << "HTTP/1.1 " << status << ' ' << reason(status) << crlf << "Date: " - << date() << crlf << "Server: Smersh/0.9" << crlf + << date() << crlf << "Server: " << server() << crlf << "Connection: close" << crlf; } -streamsize Daemon::error(ostream& sout, Status status) +string Daemon::reason(Status status) +{ + ostringstream sout; + + switch (status) + { + case ok: + sout << "OK"; + break; + case found: + sout << "Found"; + break; + case seeOther: + sout << "See Other"; + break; + case bad: + sout << "Bad Request"; + break; + case notFound: + sout << "Not Found"; + break; + case lengthRequired: + sout << "Length Required"; + break; + case mediaType: + sout << "Unsupported Media Type"; + break; + case serverError: + sout << "Internal Server Error"; + break; + case notImplemented: + sout << "Not Implemented"; + break; + case version: + sout << "HTTP Version not supported"; + } + + return sout.str(); +} + +string Daemon::server() +{ + utsname system; + + uname(&system); + + return string("Smersh/0.9 (") + system.sysname + ')'; +} + +string Daemon::server(const Environment& env) +{ + ostringstream server; + string port(env.get("SERVER_PORT")); + + server << this->server() << " Server at " << env.get("SERVER_NAME") << " Po" + << "rt " << (port.empty() ? lexical_cast(this->port) : port); + + return server.str(); +} + +streamsize Daemon::error(ostream& sout, Status status, const Environment& env) { string reason(this->reason(status)); ostringstream error; error << "" << status << ' ' << reason << "

" << reason << "

Mistakes were made, deal with t" - << "hem.


Smersh/0.9
\n"; + << "hem.


" << server(env) << "
\r\n"; sout << "Content-Length: " << error.str().length() << crlf << "Content-Type: text/html; charset=UTF-8\r\n\r\n" << error.str(); @@ -115,52 +173,56 @@ string Daemon::date(bool log) return when; } -string Daemon::ip(const api::TcpSocket& socket) +int Daemon::handle(Client* client) { - api::InternetAddress address(socket.GetAddress()); - - return inet_ntoa(address->sin_addr); -} - -int Daemon::handle(api::TcpSocket* client) -{ - ios::InputOutputStreamBufAdapter adapter(*client); - iostream sio(&adapter); + ios::ToIoStream sio(&client->socket, &client->socket); Environment env; stringstream post; ostringstream log; Status code(request(sio, env, post, log)); + if (env.get("REQUEST_URI") == "/favicon.ico") code = notFound; + response(sio, code); bool head(env.get("REQUEST_METHOD") == "HEAD"); streamsize sent(0); - if (code == ok && !head) + if (code == ok && env.get("REQUEST_URI") == "/robots.txt") { - ostringstream output; - Smersh smersh(post, output, env); - string content(output.str().substr(40)); + sio << "Content-Length: 28\r\nContent-Type: text/plain; charset=UTF-8\r" + << "\n\r\n"; - sio << "Content-Length: " << content.length() << crlf + if (!head) { sio << "User-agent: *\r\nDisallow: /\r\n"; sent = 28; } + } + else if (code == ok && !head) + { + ostringstream content; + Smersh smersh(post, content, env); + + sio << "Content-Length: " << content.str().length() << crlf << "Content-Type: text/html; charset=UTF-8\r\n\r\n"; - sio.write(content.data(), content.size()); + sio.write(content.str().data(), content.str().size()); - sent = content.size(); + sent = content.str().size(); } else if (head) sio << "Content-Type: text/html; charset=UTF-8\r\n\r\n"; - else sent = error(sio, code); - - log << code << ' ' << (sent > 0 ? lexical_cast(sent) : string("0")) - << " \"" << env.get("HTTP_REFERER") << "\" \"" - << env.get("HTTP_USER_AGENT") << '"'; + else sent = error(sio, code, env); ofstream fout(this->log.c_str(), ios_base::app); - fout << ip(*client) << " - - " << date(true) << ' ' << log.str() << '\n'; + fout << inet_ntoa(client->ip->sin_addr) << " - - " << date(true) << ' ' + << log.str() << code << ' ' << lexical_cast(sent) << " \"" + << env.get("HTTP_REFERER") << "\" \"" << env.get("HTTP_USER_AGENT") + << "\"\n"; + sio << flush; + + client->socket.ShutdownWrite(); delete client; + + return 0; } void Daemon::headers(istream& sin, Environment& env) @@ -176,7 +238,7 @@ void Daemon::headers(istream& sin, Envir istringstream input(line); string name, value; - ::getline(input, name, ':'); + std::getline(input, name, ':'); getline(input, value); for (char next(sin.peek()); next == ' ' || next == '\t'; next = @@ -197,6 +259,20 @@ void Daemon::headers(istream& sin, Envir { if (value == matcher) env.set("CONTENT_TYPE", matcher[1]); } + else if (name == "Host") + { + Matcher matcher("^\\s*(.+?)(:[0-9]+)?\\s*$"); + + if (value == matcher) + { + bool port(matcher.size() > 2); + + env.set("HTTP_HOST", matcher[1] + (port ? matcher[2] : "")); + env.set("SERVER_NAME", matcher[1]); + + if (port) env.set("SERVER_PORT", matcher[2].substr(1)); + } + } else if (name == "Referer") { if (value == matcher) env.set("HTTP_REFERER", matcher[1]); @@ -227,43 +303,3 @@ Daemon::Status Daemon::message(istream& return ok; } - -string Daemon::reason(Status status) -{ - ostringstream sout; - - switch (status) - { - case ok: - sout << "OK"; - break; - case found: - sout << "Found"; - break; - case seeOther: - sout << "See Other"; - break; - case bad: - sout << "Bad Request"; - break; - case notFound: - sout << "Not Found"; - break; - case lengthRequired: - sout << "Length Required"; - break; - case mediaType: - sout << "Unsupported Media Type"; - break; - case serverError: - sout << "Internal Server Error"; - break; - case notImplemented: - sout << "Not Implemented"; - break; - case version: - sout << "HTTP Version not supported"; - } - - return sout.str(); -}