From 8346d0774d2d1e076038db27f65f1d082a460f16 Mon Sep 17 00:00:00 2001 From: Pixel Date: Thu, 20 Sep 2001 23:27:01 +0000 Subject: Initial revision --- lib/HttpServ.cc | 297 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 lib/HttpServ.cc (limited to 'lib/HttpServ.cc') diff --git a/lib/HttpServ.cc b/lib/HttpServ.cc new file mode 100644 index 0000000..dfead28 --- /dev/null +++ b/lib/HttpServ.cc @@ -0,0 +1,297 @@ +#include "Action.h" +#include "HttpServ.h" +#include "Socket.h" +#include "config.h" + +String endhl = "\r\n", endnl = "\n"; + +HttpServ::HttpServ(int port, const String & nname) : name(nname), localport(port) { + Listener.SetLocal("", port); + Listener.Listen(); + cerr << "Mini HTTP-Server '" << name << "' ready and listening for port " << port << endl; +} + +void HttpServ::MainLoop(Action * p) { + while (1) { + ProcessRequest(p); + } +} + +void HttpServ::ProcessRequest(Action * p) { + Socket s(Listener.Accept()); + String file, domain, t; + Action * f; + int len; + + bad = false; + + if (!s.IsConnected()) return; + + cerr << "Got a request\n----\n"; + + bool post = ParseUri(file, domain, s); + + + len = -1; + do { + s >> t; + cerr << t << endl; + if ((t.strstr("Content-Length: ") == 0) || (t.strstr("Content-length: ") == 0)) { + cerr << "Saw 'Content-Lenght:', reading length from '" << t.to_charp(16) << "'\n"; + len = String(t.to_charp(16)).to_int(); + } + } while (t.strlen()); + + if (post) { + // On a pas eu de ligne 'Content-Length' mais on a eu une méthode POST. + // Cela est une erreur. + if (len == -1) { + bad = true; + Vars = new Variables(0); + } else { + cerr << "Got a POST request. Parsing variables. (len = " << len << ")\n"; + // Les variables seront initialisées ici. + ParseVars(s, len); + } + } else { + Vars = new Variables(0); + } + + cerr << " Domain = '" << domain << "' - File = '" << file << "'\n"; + + if (!bad) { + // Nous vérifions le domaine. + if (domain != "") { + bad = true; + // Les domaines valides sont '/', '/bin' et '/image'. + if (domain == "/image") bad = false; + if (domain == "/bin") bad = false; + if (domain == "/") bad = false; + } else { + // L'url sans domaine ni fichier est valide. (cela arrive sur certains navigateurs...) + bad = (file != ""); + } + } + + if (bad) { + ShowError(s); + } else { + if (((domain == "") || (domain == "/")) && (file == "")) { + // Si le navigateur a demandé l'URL '/', alors on renvoie une notification + // de redirection. + SendRedirect(s); + } else if (domain == "/bin") { + // Le domaine 'bin' est réservé aux actions. On cherche donc l'action à effectuer. + if ((f = p->Look4URL(file))) { + SendHeads(s, "text/html"); + f->Do(Vars, &s); + } else { + ShowError(s); + } + } else { + // Dans tous les autres cas de domaine, on cherche le fichier dans le répertoire datas. + // On utilise try au cas où le fichier n'existe pas et donc que le constructeur + // d'input renvoie une erreur. + try { + Input i(String("datas/") + file); + SendHeads(s, GetMime(file)); + s.ReadFile(i); + cerr << "File found, dumped.\n"; + } + catch (IOGeneral e) { + ShowError(s); + cerr << "File not found, error showed.\n"; + } + } + } + + delete Vars; + + cerr << "----\n"; +} + +void HttpServ::ParseVars(Socket & s, int len) { + String t, v; + char conv[3], l; + int hconv, nbvars; + ssize_t pos = 0, next; + + t = ""; + for (int i = 0; i < len; i++) { + s.read(&l, 1); + t += l; + } + cerr << "Post variables line: '" <SetTo(i, v); + } + pos++; + } +} + +/* + * Cette fonction renverra true si la méthode est une méthode POST. + * Les Strings domain et file seront modifiées afin de renvoyer le domaine + * et le fichier lut. La string s doit donner la première ligne de la requète, + * c'est à dire la méthode demandée par le client. + */ + +bool HttpServ::ParseUri(String & file, String & domain, Socket & s) { + String t, Uri; + bool post = false; + char * p; + ssize_t sppos; + + s >> t; + cerr << t << endl; + + bad = false; + + // p nous indiquera la position de la chaîne URL. + switch (t[0]) { + case 'P': /* POST? */ + p = t.to_charp(1, 4); + if (!strcmp(p, "OST ")) { + p = t.to_charp(5); + post = true; + break; + } + case 'G': /* GET? */ + p = t.to_charp(1, 3); + if (!strcmp(p, "ET ")) { + p = t.to_charp(4); + break; + } + default: + bad = true; + } + + if (!bad) { + ssize_t poshttp, posslash; + Uri = p; + sppos = Uri.strrchr(' '); + p = Uri.to_charp(0, sppos - 1); + Uri = p; + // On enlève tout le host spécifié éventuellement dans la requete. + if ((poshttp = Uri.strstr("http://")) > 0) { + Uri = Uri.to_charp(poshttp + 7); + posslash = Uri.strchr('/'); + // Certains navigateurs indiqueront uniquement http://host comme URL. + if (posslash >= 0) { + Uri = Uri.to_charp(posslash); + } else { + Uri = ""; + } + } + posslash = Uri.strrchr('/'); + file = Uri.to_charp(posslash + 1); + if (posslash > 0) { + domain = Uri.to_charp(0, posslash - 1); + } else { + domain = ""; + } + } + return post; +} + +/* + * Ceci sert à rediriger le navigateur vers l'url de démarrage. + */ +void HttpServ::SendRedirect(Socket & s) { + s << "HTTP/1.1 301 Moved Permanently" << endhl << + "Server: " << name << endhl << + "Location: http://127.0.0.1:" << localport << "/bin/start" << endhl << + "Cache-Control: no-cache" << endhl << + "Connection-Type: closed" << endhl << + "Content-Type: text/html" << endhl << endhl << + "301 - Moved Permanently" << endnl << + "

You should be redirected to the " << endnl << endnl << + "start page

" << endnl << + "" << endnl; +} + +/* + * Nous envoyons les entetes de réponse HTTP. + */ + +void HttpServ::SendHeads(Socket & s, const String & mime) { + s << "HTTP/1.1 200 OK" << endhl << + "Server: " << name << endhl << + "Cache-Control: no-cache" << endhl << + "Connection-Type: closed" << endhl << + "Content-Type: " << mime << endhl << endhl; +} + +/* + * Affichage d'une erreur 404. + */ + +void HttpServ::ShowError(Socket & s) { + s << "HTTP/1.1 404 Not Found" << endhl << + "Server: " << name << endhl << + "Cache-Control: no-cache" << endhl << + "Connection-Type: closed" << endhl << + "Content-Type: text/html" << endhl << endhl << + "404 - Error" << endnl << + "

The server was unable to process your query

" << endnl << + "Click here to go the main page." << + "" << endnl; +} + +/* + * Sert à déterminer le type mime à partir de l'extension du fichier. + * Par défaut, nous mettons "text/plain". + */ + +String HttpServ::GetMime(const String & f) { + String ext; + size_t ppos; + + ppos = f.strrchr('.'); + + if (ppos >= 0) { + ext = f.to_charp(ppos + 1); + if (ext == "jpg") return "image/jpeg"; + if (ext == "jpeg") return "image/jpeg"; + if (ext == "htm") return "text/html"; + if (ext == "html") return "text/html"; + if (ext == "gif") return "image/gif"; + if (ext == "png") return "image/png"; + } + + return "text/plain"; +} -- cgit v1.2.3