// --------------------------------------------------------------------------- // - HttppProto.cpp - // - afnix:nwg module - http protocol class implementation - // --------------------------------------------------------------------------- // - This program is free software; you can redistribute it and/or modify - // - it provided that this copyright notice is kept intact. - // - - // - This program is distributed in the hope that it will be useful, but - // - without any warranty; without even the implied warranty of - // - merchantability or fitness for a particular purpose. In no event shall - // - the copyright holder be liable for any direct, indirect, incidental or - // - special damages arising in any way out of the use of this software. - // --------------------------------------------------------------------------- // - copyright (c) 1999-2007 amaury darsch - // --------------------------------------------------------------------------- #include "Vector.hpp" #include "Utility.hpp" #include "Runnable.hpp" #include "HttpProto.hpp" #include "QuarkZone.hpp" #include "Exception.hpp" namespace afnix { // ------------------------------------------------------------------------- // - private section - // ------------------------------------------------------------------------- // the http status code static const String HTTP_STAT_C100 = "100 Continue"; static const String HTTP_STAT_C101 = "101 Switching Protocols"; static const String HTTP_STAT_C200 = "200 OK"; static const String HTTP_STAT_C201 = "201 Created"; static const String HTTP_STAT_C202 = "202 Accepted"; static const String HTTP_STAT_C203 = "203 Non-Authoritative Information"; static const String HTTP_STAT_C204 = "204 No Content"; static const String HTTP_STAT_C205 = "205 Reset Content"; static const String HTTP_STAT_C206 = "206 Partial Content"; static const String HTTP_STAT_C300 = "300 Multiple Choices"; static const String HTTP_STAT_C301 = "301 Moved Permanently"; static const String HTTP_STAT_C302 = "302 Found"; static const String HTTP_STAT_C303 = "303 See Other"; static const String HTTP_STAT_C304 = "304 Not Modified"; static const String HTTP_STAT_C305 = "305 Use Proxy"; static const String HTTP_STAT_C307 = "307 Temporary Redirect"; static const String HTTP_STAT_C400 = "400 Bad Request"; static const String HTTP_STAT_C401 = "401 Unauthorized"; static const String HTTP_STAT_C402 = "402 Payment Required"; static const String HTTP_STAT_C403 = "403 Forbidden"; static const String HTTP_STAT_C404 = "404 Not Found"; static const String HTTP_STAT_C405 = "405 Method Not Allowed"; static const String HTTP_STAT_C406 = "406 Not Acceptable"; static const String HTTP_STAT_C407 = "407 Proxy Authentication Required"; static const String HTTP_STAT_C408 = "408 Request Time-out"; static const String HTTP_STAT_C409 = "409 Conflict"; static const String HTTP_STAT_C410 = "410 Gone"; static const String HTTP_STAT_C411 = "411 Length Required"; static const String HTTP_STAT_C412 = "412 Precondition Failed"; static const String HTTP_STAT_C413 = "413 Request Entity Too Large"; static const String HTTP_STAT_C414 = "414 Request-URI Too Large"; static const String HTTP_STAT_C415 = "415 Unsupported Media Type"; static const String HTTP_STAT_C416 = "416 Requested range not satisfiable"; static const String HTTP_STAT_C417 = "417 Expectation Failed"; static const String HTTP_STAT_C500 = "500 Internal Server Error"; static const String HTTP_STAT_C501 = "501 Not Implemented"; static const String HTTP_STAT_C502 = "502 Bad Gateway"; static const String HTTP_STAT_C503 = "503 Service Unavailable"; static const String HTTP_STAT_C504 = "504 Gateway Time-out"; static const String HTTP_STAT_C505 = "505 HTTP Version not supported"; // this procedure format a property into a string header static String get_header_string (const Property& prop) { // get the property name String result = prop.getname (); // add separator result += ": "; // add value result += prop.getpval (); // here we are return result; } // ------------------------------------------------------------------------- // - public section - // ------------------------------------------------------------------------- // map a http status code to a message String HttpProto::mapcode (const long code) { switch (code) { case 100: return HTTP_STAT_C100; case 101: return HTTP_STAT_C101; case 200: return HTTP_STAT_C200; case 201: return HTTP_STAT_C201; case 202: return HTTP_STAT_C202; case 203: return HTTP_STAT_C203; case 204: return HTTP_STAT_C204; case 205: return HTTP_STAT_C205; case 206: return HTTP_STAT_C206; case 300: return HTTP_STAT_C300; case 301: return HTTP_STAT_C301; case 302: return HTTP_STAT_C302; case 303: return HTTP_STAT_C303; case 304: return HTTP_STAT_C304; case 305: return HTTP_STAT_C305; case 307: return HTTP_STAT_C307; case 400: return HTTP_STAT_C400; case 401: return HTTP_STAT_C401; case 402: return HTTP_STAT_C402; case 403: return HTTP_STAT_C403; case 404: return HTTP_STAT_C404; case 405: return HTTP_STAT_C405; case 406: return HTTP_STAT_C406; case 407: return HTTP_STAT_C407; case 408: return HTTP_STAT_C408; case 409: return HTTP_STAT_C409; case 410: return HTTP_STAT_C410; case 411: return HTTP_STAT_C411; case 412: return HTTP_STAT_C412; case 413: return HTTP_STAT_C413; case 414: return HTTP_STAT_C414; case 415: return HTTP_STAT_C415; case 416: return HTTP_STAT_C416; case 417: return HTTP_STAT_C417; case 500: return HTTP_STAT_C500; case 501: return HTTP_STAT_C501; case 502: return HTTP_STAT_C502; case 503: return HTTP_STAT_C503; case 504: return HTTP_STAT_C504; case 505: return HTTP_STAT_C505; default: break; } throw Exception ("http-error", "invalid http status code", Utility::tostring (code)); } // ------------------------------------------------------------------------- // - class section - // ------------------------------------------------------------------------- // add a header property void HttpProto::sethead (const String& name, const Literal& lval) { wrlock (); try { d_head.set (name, lval.tostring ()); unlock (); } catch (...) { unlock (); throw; } } // write the http header to the output stream void HttpProto:: write (Output& os) const { rdlock (); try { long hlen = d_head.length (); for (long i = 0; i < hlen; i++) { // get the property Property* prop = d_head.get (i); if (prop == nilp) continue; // format the string and write os.writeln (get_header_string (*prop), true); } os.newline (true); // done unlock (); } catch (...) { unlock (); throw; } } // write the http reply to a buffer void HttpProto:: write (Buffer& buf) const { rdlock (); try { long hlen = d_head.length (); for (long i = 0; i < hlen; i++) { // get the property Property* prop = d_head.get (i); if (prop == nilp) continue; // format the string and write buf.add (get_header_string (*prop)); buf.add (crlc); buf.add (eolc); } buf.add (crlc); buf.add (eolc); // done unlock (); } catch (...) { unlock (); throw; } } // ------------------------------------------------------------------------- // - object section - // ------------------------------------------------------------------------- // the quark zone static const long QUARK_ZONE_LENGTH = 2; static QuarkZone zone (QUARK_ZONE_LENGTH); // the object supported quarks static const long QUARK_WRITE = zone.intern ("write"); static const long QUARK_SETHEAD = zone.intern ("set-header"); // return true if the given quark is defined bool HttpProto::isquark (const long quark, const bool hflg) const { rdlock (); if (zone.exists (quark) == true) { unlock (); return true; } bool result = hflg ? Object::isquark (quark, hflg) : false; unlock (); return result; } // apply this object with a set of arguments and a quark Object* HttpProto::apply (Runnable* robj, Nameset* nset, const long quark, Vector* argv) { // get the number of arguments long argc = (argv == nilp) ? 0 : argv->length (); // dispatch 0 argument if (argc == 0) { if (quark == QUARK_WRITE) { Output* os = (robj == nilp) ? nilp : robj->getos (); if (os == nilp) return nilp; write (*os); return nilp; } } // dispatch 1 argument if (argc == 1) { if (quark == QUARK_WRITE) { Object* obj = argv->get (0); // check for an output stream Output* os = dynamic_cast (obj); if (os != nilp) { write (*os); return nilp; } // check for a buffer Buffer* buf = dynamic_cast (obj); if (buf != nilp) { write (*buf); return nilp; } throw Exception ("type-error", "invalid object with write", Object::repr (obj)); } } // dispatch 2 argument if (argc == 2) { if (quark == QUARK_SETHEAD) { String name = argv->getstring (0); Object* obj = argv->get (1); Literal* lobj = dynamic_cast (obj); if (lobj == nilp) { throw Exception ("type-error", "invalid object with set-header", Object::repr (obj)); } sethead (name, *lobj); return nilp; } } // call the object method return Object::apply (robj, nset, quark, argv); } }