net/src/HTTPTypes.cpp

00001 // ------------------------------------------------------------------
00002 // pion-net: a C++ framework for building lightweight HTTP interfaces
00003 // ------------------------------------------------------------------
00004 // Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
00005 //
00006 // Distributed under the Boost Software License, Version 1.0.
00007 // See http://www.boost.org/LICENSE_1_0.txt
00008 //
00009 
00010 #include <boost/lexical_cast.hpp>
00011 #include <boost/thread/mutex.hpp>
00012 #include <pion/net/HTTPTypes.hpp>
00013 #include <cstdio>
00014 #include <ctime>
00015 
00016 
00017 namespace pion {        // begin namespace pion
00018 namespace net {     // begin namespace net (Pion Network Library)
00019 
00020 
00021 // generic strings used by HTTP
00022 const std::string   HTTPTypes::STRING_EMPTY;
00023 const std::string   HTTPTypes::STRING_CRLF("\x0D\x0A");
00024 const std::string   HTTPTypes::STRING_HTTP_VERSION("HTTP/");
00025 const std::string   HTTPTypes::HEADER_NAME_VALUE_DELIMITER(": ");
00026 
00027 // common HTTP header names
00028 const std::string   HTTPTypes::HEADER_HOST("Host");
00029 const std::string   HTTPTypes::HEADER_COOKIE("Cookie");
00030 const std::string   HTTPTypes::HEADER_SET_COOKIE("Set-Cookie");
00031 const std::string   HTTPTypes::HEADER_CONNECTION("Connection");
00032 const std::string   HTTPTypes::HEADER_CONTENT_TYPE("Content-Type");
00033 const std::string   HTTPTypes::HEADER_CONTENT_LENGTH("Content-Length");
00034 const std::string   HTTPTypes::HEADER_CONTENT_LOCATION("Content-Location");
00035 const std::string   HTTPTypes::HEADER_CONTENT_ENCODING("Content-Encoding");
00036 const std::string   HTTPTypes::HEADER_LAST_MODIFIED("Last-Modified");
00037 const std::string   HTTPTypes::HEADER_IF_MODIFIED_SINCE("If-Modified-Since");
00038 const std::string   HTTPTypes::HEADER_TRANSFER_ENCODING("Transfer-Encoding");
00039 const std::string   HTTPTypes::HEADER_LOCATION("Location");
00040 const std::string   HTTPTypes::HEADER_AUTHORIZATION("Authorization");
00041 const std::string   HTTPTypes::HEADER_REFERER("Referer");
00042 const std::string   HTTPTypes::HEADER_USER_AGENT("User-Agent");
00043 const std::string   HTTPTypes::HEADER_X_FORWARDED_FOR("X-Forwarded-For");
00044 
00045 // common HTTP content types
00046 const std::string   HTTPTypes::CONTENT_TYPE_HTML("text/html");
00047 const std::string   HTTPTypes::CONTENT_TYPE_TEXT("text/plain");
00048 const std::string   HTTPTypes::CONTENT_TYPE_XML("text/xml");
00049 const std::string   HTTPTypes::CONTENT_TYPE_URLENCODED("application/x-www-form-urlencoded");
00050 
00051 // common HTTP request methods
00052 const std::string   HTTPTypes::REQUEST_METHOD_HEAD("HEAD");
00053 const std::string   HTTPTypes::REQUEST_METHOD_GET("GET");
00054 const std::string   HTTPTypes::REQUEST_METHOD_PUT("PUT");
00055 const std::string   HTTPTypes::REQUEST_METHOD_POST("POST");
00056 const std::string   HTTPTypes::REQUEST_METHOD_DELETE("DELETE");
00057 
00058 // common HTTP response messages
00059 const std::string   HTTPTypes::RESPONSE_MESSAGE_OK("OK");
00060 const std::string   HTTPTypes::RESPONSE_MESSAGE_CREATED("Created");
00061 const std::string   HTTPTypes::RESPONSE_MESSAGE_NO_CONTENT("No Content");
00062 const std::string   HTTPTypes::RESPONSE_MESSAGE_FOUND("Found");
00063 const std::string   HTTPTypes::RESPONSE_MESSAGE_UNAUTHORIZED("Unauthorized");
00064 const std::string   HTTPTypes::RESPONSE_MESSAGE_FORBIDDEN("Forbidden");
00065 const std::string   HTTPTypes::RESPONSE_MESSAGE_NOT_FOUND("Not Found");
00066 const std::string   HTTPTypes::RESPONSE_MESSAGE_METHOD_NOT_ALLOWED("Method Not Allowed");
00067 const std::string   HTTPTypes::RESPONSE_MESSAGE_NOT_MODIFIED("Not Modified");
00068 const std::string   HTTPTypes::RESPONSE_MESSAGE_BAD_REQUEST("Bad Request");
00069 const std::string   HTTPTypes::RESPONSE_MESSAGE_SERVER_ERROR("Server Error");
00070 const std::string   HTTPTypes::RESPONSE_MESSAGE_NOT_IMPLEMENTED("Not Implemented");
00071 const std::string   HTTPTypes::RESPONSE_MESSAGE_CONTINUE("Continue");
00072 
00073 // common HTTP response codes
00074 const unsigned int  HTTPTypes::RESPONSE_CODE_OK = 200;
00075 const unsigned int  HTTPTypes::RESPONSE_CODE_CREATED = 201;
00076 const unsigned int  HTTPTypes::RESPONSE_CODE_NO_CONTENT = 204;
00077 const unsigned int  HTTPTypes::RESPONSE_CODE_FOUND = 302;
00078 const unsigned int  HTTPTypes::RESPONSE_CODE_UNAUTHORIZED = 401;
00079 const unsigned int  HTTPTypes::RESPONSE_CODE_FORBIDDEN = 403;
00080 const unsigned int  HTTPTypes::RESPONSE_CODE_NOT_FOUND = 404;
00081 const unsigned int  HTTPTypes::RESPONSE_CODE_METHOD_NOT_ALLOWED = 405;
00082 const unsigned int  HTTPTypes::RESPONSE_CODE_NOT_MODIFIED = 304;
00083 const unsigned int  HTTPTypes::RESPONSE_CODE_BAD_REQUEST = 400;
00084 const unsigned int  HTTPTypes::RESPONSE_CODE_SERVER_ERROR = 500;
00085 const unsigned int  HTTPTypes::RESPONSE_CODE_NOT_IMPLEMENTED = 501;
00086 const unsigned int  HTTPTypes::RESPONSE_CODE_CONTINUE = 100;
00087 
00088 
00089 // static member functions
00090     
00091 bool HTTPTypes::base64_decode(const std::string &input, std::string &output)
00092 {
00093     static const char nop = -1; 
00094     static const char decoding_data[] = {
00095         nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop,
00096         nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop,
00097         nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop, 62, nop,nop,nop, 63,
00098         52, 53, 54,  55,  56, 57, 58, 59,  60, 61,nop,nop, nop,nop,nop,nop,
00099         nop, 0,  1,   2,   3,  4,  5,  6,   7,  8,  9, 10,  11, 12, 13, 14,
00100         15, 16, 17,  18,  19, 20, 21, 22,  23, 24, 25,nop, nop,nop,nop,nop,
00101         nop,26, 27,  28,  29, 30, 31, 32,  33, 34, 35, 36,  37, 38, 39, 40,
00102         41, 42, 43,  44,  45, 46, 47, 48,  49, 50, 51,nop, nop,nop,nop,nop,
00103         nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop,
00104         nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop,
00105         nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop,
00106         nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop,
00107         nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop,
00108         nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop,
00109         nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop,
00110         nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop, nop,nop,nop,nop
00111         };
00112 
00113     unsigned int input_length=input.size();
00114     const char * input_ptr = input.data();
00115 
00116     // allocate space for output string
00117     output.clear();
00118     output.reserve(((input_length+2)/3)*4);
00119 
00120     // for each 4-bytes sequence from the input, extract 4 6-bits sequences by droping first two bits
00121     // and regenerate into 3 8-bits sequence
00122 
00123     for (unsigned int i=0; i<input_length;i++) {
00124         char base64code0;
00125         char base64code1;
00126         char base64code2 = 0;   // initialized to 0 to suppress warnings
00127         char base64code3;
00128 
00129         base64code0 = decoding_data[static_cast<int>(input_ptr[i])];
00130         if(base64code0==nop)            // non base64 character
00131             return false;
00132         if(!(++i<input_length)) // we need at least two input bytes for first byte output
00133             return false;
00134         base64code1 = decoding_data[static_cast<int>(input_ptr[i])];
00135         if(base64code1==nop)            // non base64 character
00136             return false;
00137 
00138         output += ((base64code0 << 2) | ((base64code1 >> 4) & 0x3));
00139 
00140         if(++i<input_length) {
00141             char c = input_ptr[i];
00142             if(c =='=') { // padding , end of input
00143                 BOOST_ASSERT( (base64code1 & 0x0f)==0);
00144                 return true;
00145             }
00146             base64code2 = decoding_data[static_cast<int>(input_ptr[i])];
00147             if(base64code2==nop)            // non base64 character
00148                 return false;
00149 
00150             output += ((base64code1 << 4) & 0xf0) | ((base64code2 >> 2) & 0x0f);
00151         }
00152 
00153         if(++i<input_length) {
00154             char c = input_ptr[i];
00155             if(c =='=') { // padding , end of input
00156                 BOOST_ASSERT( (base64code2 & 0x03)==0);
00157                 return true;
00158             }
00159             base64code3 = decoding_data[static_cast<int>(input_ptr[i])];
00160             if(base64code3==nop)            // non base64 character
00161                 return false;
00162 
00163             output += (((base64code2 << 6) & 0xc0) | base64code3 );
00164         }
00165 
00166     }
00167 
00168     return true;
00169 }
00170 
00171 bool HTTPTypes::base64_encode(const std::string &input, std::string &output)
00172 {
00173     static const char encoding_data[] = 
00174         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
00175 
00176     unsigned int input_length=input.size();
00177     const char * input_ptr = input.data();
00178 
00179     // allocate space for output string
00180     output.clear();
00181     output.reserve(((input_length+2)/3)*4);
00182 
00183     // for each 3-bytes sequence from the input, extract 4 6-bits sequences and encode using 
00184     // encoding_data lookup table.
00185     // if input do not contains enough chars to complete 3-byte sequence,use pad char '=' 
00186     for (unsigned int i=0; i<input_length;i++) {
00187         int base64code0=0;
00188         int base64code1=0;
00189         int base64code2=0;
00190         int base64code3=0;
00191 
00192         base64code0 = (input_ptr[i] >> 2)  & 0x3f;  // 1-byte 6 bits
00193         output += encoding_data[base64code0];
00194         base64code1 = (input_ptr[i] << 4 ) & 0x3f;  // 1-byte 2 bits +
00195 
00196         if (++i < input_length) {
00197             base64code1 |= (input_ptr[i] >> 4) & 0x0f; // 2-byte 4 bits
00198             output += encoding_data[base64code1];
00199             base64code2 = (input_ptr[i] << 2) & 0x3f;  // 2-byte 4 bits + 
00200 
00201             if (++i < input_length) {
00202                 base64code2 |= (input_ptr[i] >> 6) & 0x03; // 3-byte 2 bits
00203                 base64code3  = input_ptr[i] & 0x3f;       // 3-byte 6 bits
00204                 output += encoding_data[base64code2];
00205                 output += encoding_data[base64code3];
00206             } else {
00207                 output += encoding_data[base64code2];
00208                 output += '=';
00209             }
00210         } else {
00211             output += encoding_data[base64code1];
00212             output += '=';
00213             output += '=';
00214         }
00215     }
00216 
00217     return true;
00218 }
00219 
00220 std::string HTTPTypes::url_decode(const std::string& str)
00221 {
00222     char decode_buf[3];
00223     std::string result;
00224     result.reserve(str.size());
00225     
00226     for (std::string::size_type pos = 0; pos < str.size(); ++pos) {
00227         switch(str[pos]) {
00228         case '+':
00229             // convert to space character
00230             result += ' ';
00231             break;
00232         case '%':
00233             // decode hexidecimal value
00234             if (pos + 2 < str.size()) {
00235                 decode_buf[0] = str[++pos];
00236                 decode_buf[1] = str[++pos];
00237                 decode_buf[2] = '\0';
00238                 result += static_cast<char>( strtol(decode_buf, 0, 16) );
00239             } else {
00240                 // recover from error by not decoding character
00241                 result += '%';
00242             }
00243             break;
00244         default:
00245             // character does not need to be escaped
00246             result += str[pos];
00247         }
00248     };
00249     
00250     return result;
00251 }
00252     
00253 std::string HTTPTypes::url_encode(const std::string& str)
00254 {
00255     char encode_buf[4];
00256     std::string result;
00257     encode_buf[0] = '%';
00258     result.reserve(str.size());
00259 
00260     // character selection for this algorithm is based on the following url:
00261     // http://www.blooberry.com/indexdot/html/topics/urlencoding.htm
00262     
00263     for (std::string::size_type pos = 0; pos < str.size(); ++pos) {
00264         switch(str[pos]) {
00265         default:
00266             if (str[pos] > 32 && str[pos] < 127) {
00267                 // character does not need to be escaped
00268                 result += str[pos];
00269                 break;
00270             }
00271             // else pass through to next case
00272         case ' ':   
00273         case '$': case '&': case '+': case ',': case '/': case ':':
00274         case ';': case '=': case '?': case '@': case '"': case '<':
00275         case '>': case '#': case '%': case '{': case '}': case '|':
00276         case '\\': case '^': case '~': case '[': case ']': case '`':
00277             // the character needs to be encoded
00278             sprintf(encode_buf+1, "%.2X", (unsigned char)(str[pos]));
00279             result += encode_buf;
00280             break;
00281         }
00282     };
00283     
00284     return result;
00285 }   
00286 
00287 std::string HTTPTypes::get_date_string(const time_t t)
00288 {
00289     // use mutex since time functions are normally not thread-safe
00290     static boost::mutex time_mutex;
00291     static const char *TIME_FORMAT = "%a, %d %b %Y %H:%M:%S GMT";
00292     static const unsigned int TIME_BUF_SIZE = 100;
00293     char time_buf[TIME_BUF_SIZE+1];
00294 
00295     boost::mutex::scoped_lock time_lock(time_mutex);
00296     if (strftime(time_buf, TIME_BUF_SIZE, TIME_FORMAT, gmtime(&t)) == 0)
00297         time_buf[0] = '\0'; // failed; resulting buffer is indeterminate
00298     time_lock.unlock();
00299 
00300     return std::string(time_buf);
00301 }
00302 
00303 std::string HTTPTypes::make_query_string(const QueryParams& query_params)
00304 {
00305     std::string query_string;
00306     for (QueryParams::const_iterator i = query_params.begin(); i != query_params.end(); ++i) {
00307         if (i != query_params.begin())
00308             query_string += '&';
00309         query_string += url_encode(i->first);
00310         query_string += '=';
00311         query_string += url_encode(i->second);
00312     }
00313     return query_string;
00314 }
00315 
00316 std::string HTTPTypes::make_set_cookie_header(const std::string& name,
00317                                               const std::string& value,
00318                                               const std::string& path,
00319                                               const bool has_max_age,
00320                                               const unsigned long max_age)
00321 {
00322     std::string set_cookie_header(name);
00323     set_cookie_header += "=\"";
00324     set_cookie_header += value;
00325     set_cookie_header += "\"; Version=\"1\"";
00326     if (! path.empty()) {
00327         set_cookie_header += "; Path=\"";
00328         set_cookie_header += path;
00329         set_cookie_header += '\"';
00330     }
00331     if (has_max_age) {
00332         set_cookie_header += "; Max-Age=\"";
00333         set_cookie_header += boost::lexical_cast<std::string>(max_age);
00334         set_cookie_header += '\"';
00335     }
00336     return set_cookie_header;
00337 }
00338 
00339     
00340 }   // end namespace net
00341 }   // end namespace pion
00342 

Generated on Fri Apr 30 14:48:53 2010 for pion-net by  doxygen 1.4.7