diff options
author | Pixel <Pixel> | 2001-09-20 23:27:01 +0000 |
---|---|---|
committer | Pixel <Pixel> | 2001-09-20 23:27:01 +0000 |
commit | 8346d0774d2d1e076038db27f65f1d082a460f16 (patch) | |
tree | 132f84cf1ef45d5006a2b1d52d4d40b1e8e51abc /lib/HttpServ.cc |
Initial revision
Diffstat (limited to 'lib/HttpServ.cc')
-rw-r--r-- | lib/HttpServ.cc | 297 |
1 files changed, 297 insertions, 0 deletions
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: '" <<t << "'\n"; + + + // Les variables sont sous la forme 'var1=val1&var2=val2&val3=var3'. Donc le nombre d'occurences + // du caractère '=' indique le nombre de variables. + nbvars = t.strchrcnt('='); + Vars = new Variables(nbvars); + + for (int i = 0; i < nbvars; i++) { + // Les variables sont sous la forme 'var1=val1&var2=val2&val3=var3'. Donc on cherche le caractère + // & dans la chaine POST. + next = t.strchr('&', pos); + if (next < 0) next = t.strlen(); + v = ""; + while (pos != next) { + switch (t[pos]) { + // Le navigateur encode les caractères spéciaux à l'aide du format %XX où XX indique + // la valeur hexadécimale du caractère. Nous encodons surtout les caractères + // ' ', '=', '%', et '/' avec cette technique. + case '%': + pos++; + conv[0] = t[pos++]; + conv[1] = t[pos++]; + conv[2] = '\0'; + sscanf(conv, "%x", &hconv); + v += ((char) hconv); + break; + // Certains navigateurs utilisent '+' pour indiquer ' ' (qui est illégal) au lieu + // d'utiliser %20. + case '+': + v += ' '; + pos++; + break; + default: + v += t[pos++]; + } + Vars->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 << + "<HTML><HEAD><TITLE>301 - Moved Permanently</TITLE></HEAD>" << endnl << + "<BODY><center><b><h2>You should be redirected to the " << endnl << endnl << + "<a href=\"http://localhost/bin/start\">start page</a></h2></b></center>" << endnl << + "</BODY></HTML>" << 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 << + "<HTML><HEAD><TITLE>404 - Error</TITLE></HEAD>" << endnl << + "<BODY><center><b><h2>The server was unable to process your query</h2></b></center>" << endnl << + "Click <A HREF=\"/\">here</A> to go the main page." << + "</BODY></HTML>" << 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"; +} |