// ---------------------------------------------------------------------------
// - 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 <Output*> (obj);
if (os != nilp) {
write (*os);
return nilp;
}
// check for a buffer
Buffer* buf = dynamic_cast <Buffer*> (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 <Literal*> (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);
}
}
syntax highlighted by Code2HTML, v. 0.9.1