// --------------------------------------------------------------------------- // - HttpReply.cpp - // - afnix:nwg module - http reply 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 "Runnable.hpp" #include "HttpReply.hpp" #include "QuarkZone.hpp" #include "Exception.hpp" namespace afnix { // ------------------------------------------------------------------------- // - private section - // ------------------------------------------------------------------------- // the content type header as a mime property static const String HTTP_MIME_NAME = "Content-Type"; static const String HTTP_MIME_PVAL = "text/plain"; // the http location name static const String HTTP_RLOC_NAME = "Location"; // the http cookie version 0 name static const String HTTP_COK0_NAME = "Set-Cookie"; // the http cookie version 1 name static const String HTTP_COK1_NAME = "Set-Cookie2"; // the http status name static const String HTTP_STAT_NAME = "Status"; // this procedure returns true if the header contains a status code that // prohibits the existence of a message body static bool is_body_valid (const Plist& head) { // check if the status property is defined if (head.exists (HTTP_STAT_NAME) == false) return true; // get the status value and check String mesg = head.getpval (HTTP_STAT_NAME); if (mesg == HttpProto::mapcode (100)) return false; if (mesg == HttpProto::mapcode (101)) return false; if (mesg == HttpProto::mapcode (204)) return false; if (mesg == HttpProto::mapcode (304)) return false; return true; } // ------------------------------------------------------------------------- // - class section - // ------------------------------------------------------------------------- // create an empty http reply HttpReply::HttpReply (void) { // add the content-type property sethead (HTTP_MIME_NAME, HTTP_MIME_PVAL); } // create an empty http reply with a type HttpReply::HttpReply (const String& type) { sethead (HTTP_MIME_NAME, type); } // return the class name String HttpReply::repr (void) const { return "HttpReply"; } // set a cookie in the header void HttpReply::setcook (const Cookie& cook) { wrlock (); try { if (cook.getvers () == 0) { sethead (HTTP_COK0_NAME, cook.tostring ()); } else if (cook.getvers () == 1) { sethead (HTTP_COK1_NAME, cook.tostring ()); } else { throw Exception ("http-error", "invalid cookie version"); } unlock (); } catch (...) { unlock (); throw; } } // add a string in the http buffer void HttpReply::addhbuf (const Literal& lval) { wrlock (); try { d_hbuf.add (lval.tostring ()); unlock (); } catch (...) { unlock (); throw; } } // add a mime document in the http buffer void HttpReply::addhbuf (const Mime& mime) { wrlock (); try { // get he mime type String type = mime.getmime (); sethead (HTTP_MIME_NAME, type); // while the mime dcoument to the http buffer mime.write (d_hbuf); unlock (); } catch (...) { unlock (); throw; } } // add a buffer in the http buffer void HttpReply::addhbuf (const Buffer& buf) { wrlock (); try { d_hbuf.add (buf); unlock (); } catch (...) { unlock (); throw; } } // set a http status by code void HttpReply::setstatus (const long code) { wrlock (); try { // get the status message sethead (HTTP_STAT_NAME, HttpProto::mapcode (code)); unlock (); } catch (...) { unlock (); throw; } } // redirect a to a certain location void HttpReply::redirect (const String& url) { wrlock (); try { // set a status 303 (see other) setstatus (303); // set the redirection location sethead (HTTP_RLOC_NAME, url); unlock (); } catch (...) { unlock (); throw; } } // write the http reply to an output stream void HttpReply:: write (Output& os) const { rdlock (); try { // write the header HttpProto::write (os); // write the body if (is_body_valid (d_head) == true) { d_hbuf.write (os); } // done unlock (); } catch (...) { unlock (); throw; } } // write the http reply to a buffer void HttpReply:: write (Buffer& buf) const { rdlock (); try { // write the header HttpProto::write (buf); // write the body if (is_body_valid (d_head) == true) { buf.add (d_hbuf); } // done unlock (); } catch (...) { unlock (); throw; } } // ------------------------------------------------------------------------- // - object section - // ------------------------------------------------------------------------- // the quark zone static const long QUARK_ZONE_LENGTH = 4; static QuarkZone zone (QUARK_ZONE_LENGTH); // the object supported quarks static const long QUARK_SETCOOK = zone.intern ("set-cookie"); static const long QUARK_ADDHBUF = zone.intern ("add-buffer"); static const long QUARK_REDIRECT = zone.intern ("redirect"); static const long QUARK_SETSTATUS = zone.intern ("set-status"); // create a new object in a generic way Object* HttpReply::mknew (Vector* argv) { long argc = (argv == nilp) ? 0 : argv->length (); // check for 0 argument if (argc == 0) return new HttpReply; //check for 1 argument if (argc == 1) { String type = argv->getstring (0); return new HttpReply (type); } // wrong arguments throw Exception ("argument-error", "too many arguments with http reply constructor"); } // return true if the given quark is defined bool HttpReply::isquark (const long quark, const bool hflg) const { rdlock (); if (zone.exists (quark) == true) { unlock (); return true; } bool result = hflg ? HttpProto::isquark (quark, hflg) : false; unlock (); return result; } // apply this object with a set of arguments and a quark Object* HttpReply::apply (Runnable* robj, Nameset* nset, const long quark, Vector* argv) { // get the number of arguments long argc = (argv == nilp) ? 0 : argv->length (); // dispatch 1 argument if (argc == 1) { if (quark == QUARK_ADDHBUF) { Object* obj = argv->get (0); // check for a literal Literal* lobj = dynamic_cast (obj); if (lobj != nilp) { addhbuf (*lobj); return nilp; } // check for a buffer Buffer* buf = dynamic_cast (obj); if (buf != nilp) { addhbuf (*buf); return nilp; } // check for a mime document Mime* mobj = dynamic_cast (obj); if (mobj != nilp) { addhbuf (*mobj); return nilp; } throw Exception ("type-error", "invalid object with add-buffer", Object::repr (obj)); } if (quark == QUARK_REDIRECT) { String url = argv->getstring (0); redirect (url); return nilp; } if (quark == QUARK_SETSTATUS) { long code = argv->getint (0); setstatus (code); return nilp; } if (quark == QUARK_SETCOOK) { Object* obj = argv->get (0); Cookie* cobj = dynamic_cast (obj); if (cobj == nilp) { throw Exception ("type-error", "invalid object with set-cookie", Object::repr (obj)); } setcook (*cobj); return nilp; } } // call the http proto method return HttpProto::apply (robj, nset, quark, argv); } }