fastcgi++
http.hpp
Go to the documentation of this file.
00001 
00002 /***************************************************************************
00003 * Copyright (C) 2007 Eddie Carle [eddie@erctech.org]                       *
00004 *                                                                          *
00005 * This file is part of fastcgi++.                                          *
00006 *                                                                          *
00007 * fastcgi++ is free software: you can redistribute it and/or modify it     *
00008 * under the terms of the GNU Lesser General Public License as  published   *
00009 * by the Free Software Foundation, either version 3 of the License, or (at *
00010 * your option) any later version.                                          *
00011 *                                                                          *
00012 * fastcgi++ is distributed in the hope that it will be useful, but WITHOUT *
00013 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    *
00014 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public     *
00015 * License for more details.                                                *
00016 *                                                                          *
00017 * You should have received a copy of the GNU Lesser General Public License *
00018 * along with fastcgi++.  If not, see <http://www.gnu.org/licenses/>.       *
00019 ****************************************************************************/
00020 
00021 
00022 #ifndef HTTP_HPP
00023 #define HTTP_HPP
00024 
00025 #include <string>
00026 #include <boost/shared_array.hpp>
00027 #include <boost/scoped_array.hpp>
00028 #include <boost/date_time/posix_time/posix_time.hpp>
00029 #include <ostream>
00030 #include <istream>
00031 #include <cstring>
00032 #include <sstream>
00033 #include <algorithm>
00034 #include <map>
00035 #include <vector>
00036 
00037 #include <fastcgi++/exceptions.hpp>
00038 #include <fastcgi++/protocol.hpp>
00039 
00041 namespace Fastcgipp
00042 {
00044    namespace Http
00045    {
00047 
00058       template<class charT> struct Post
00059       {
00061          enum Type { file, form } type;
00063          std::basic_string<charT> value;
00065          std::basic_string<charT>& filename;
00067          std::basic_string<charT> contentType;
00068 
00070          const char* data() const { return m_data; }
00072          size_t size() const { return m_size; }
00074          char* steal() const { char* ptr=m_data; m_data=0; m_size=0; return ptr; }
00075 
00076          Post(): filename(value), m_data(0), m_size(0) {}
00077          Post(const Post& x):
00078             type(x.type),
00079             value(x.value),
00080             filename(value),
00081             contentType(x.contentType),
00082             m_data(x.steal()),
00083             m_size(x.m_size)
00084          {}
00085          ~Post() { delete [] m_data; }
00086       private:
00088          mutable char* m_data;
00090          mutable size_t m_size;
00091          template<class T> friend class Environment;
00092       };
00093 
00095       enum RequestMethod
00096       {
00097          HTTP_METHOD_ERROR,
00098          HTTP_METHOD_HEAD,
00099          HTTP_METHOD_GET,
00100          HTTP_METHOD_POST,
00101          HTTP_METHOD_PUT,
00102          HTTP_METHOD_DELETE,
00103          HTTP_METHOD_TRACE,
00104          HTTP_METHOD_OPTIONS,
00105          HTTP_METHOD_CONNECT
00106       };
00107       extern const char* requestMethodLabels[];
00108       template<class charT, class Traits> inline std::basic_ostream<charT, Traits>& operator<<(std::basic_ostream<charT, Traits>& os, const RequestMethod requestMethod) { return os << requestMethodLabels[requestMethod]; }
00109    
00111 
00119       class Address
00120       {
00121       public:
00123          static const size_t size=16;
00124 
00126 
00129          const unsigned char* data() const { return m_data; }
00130 
00132 
00135          unsigned char* data() { return m_data; }
00136 
00138 
00141          Address operator=(const unsigned char* data_) { std::memcpy(m_data, data_, size); return *this; }
00142 
00143          Address operator=(const Address& address) { std::memcpy(m_data, address.m_data, size); return *this; }
00144          Address(const Address& address) { std::memcpy(m_data, address.m_data, size); }
00145          Address() {}
00146 
00148 
00151          explicit Address(const unsigned char* data_) { std::memcpy(m_data, data_, size); }
00152 
00154 
00161          void assign(const char* start, const char* end);
00162          inline bool operator==(const Address& x) const { return std::memcmp(m_data, x.m_data, size)==0; }
00163          inline bool operator>(const Address& x) const { return std::memcmp(m_data, x.m_data, size)>0; }
00164          inline bool operator<(const Address& x) const { return std::memcmp(m_data, x.m_data, size)<0; }
00165          inline bool operator<=(const Address& x) const { return !(std::memcmp(m_data, x.m_data, size)>0); }
00166          inline bool operator>=(const Address& x) const { return !(std::memcmp(m_data, x.m_data, size)<0); }
00168          operator bool() const;
00169          Address operator&(const Address& x) const;
00170 
00171          Address& operator&=(const Address& x);
00172 
00174          void zero() { std::memset(m_data, 0, size); }
00175 
00176       private:
00178          unsigned char m_data[size];
00179       };
00180 
00182 
00186       template<class charT, class Traits> std::basic_ostream<charT, Traits>& operator<<(std::basic_ostream<charT, Traits>& os, const Address& address);
00188 
00193       template<class charT, class Traits> std::basic_istream<charT, Traits>& operator>>(std::basic_istream<charT, Traits>& is, Address& address);
00194 
00196 
00202       template<class charT> struct Environment
00203       {
00205          std::basic_string<charT> host;
00207          std::basic_string<charT> userAgent;
00209          std::basic_string<charT> acceptContentTypes;
00211          std::basic_string<charT> acceptLanguages;
00213          std::basic_string<charT> acceptCharsets;
00215          std::basic_string<charT> referer;
00217          std::basic_string<charT> contentType;
00219          std::basic_string<charT> root;
00221          std::basic_string<charT> scriptName;
00223          RequestMethod requestMethod;
00225          std::basic_string<charT> requestUri;
00227          typedef std::vector<std::basic_string<charT> > PathInfo;
00228          PathInfo pathInfo;
00230          int etag;
00232          int keepAlive;
00234          unsigned int contentLength;
00236          Address serverAddress;
00238          Address remoteAddress;
00240          uint16_t serverPort;
00242          uint16_t remotePort;
00244          boost::posix_time::ptime ifModifiedSince;
00245 
00246          typedef std::map<std::basic_string<charT>, std::basic_string<charT> > Cookies;
00248          Cookies cookies;
00250          const std::basic_string<charT>& findCookie(const charT* key) const;
00251 
00252          typedef std::map<std::basic_string<charT>, std::basic_string<charT> > Gets;
00254          Gets gets;
00255 
00257 
00262          const std::basic_string<charT>& findGet(const charT* key) const;
00263 
00265 
00269          bool checkForGet(const charT* key) const;
00270 
00271          typedef std::map<std::basic_string<charT>, Post<charT> > Posts;
00273          Posts posts;
00274 
00276 
00281          const Post<charT>& findPost(const charT* key) const;
00282 
00284 
00288          bool checkForPost(const charT* key) const;
00289 
00291 
00299          void fill(const char* data, size_t size);
00300 
00302 
00310          bool fillPostBuffer(const char* data, size_t size);
00311 
00313          void parsePostsMultipart();
00314 
00316          void parsePostsUrlEncoded();
00317 
00319          void clearPostBuffer() { postBuffer.reset(); pPostBuffer=0; }
00320 
00321          Environment(): etag(0), keepAlive(0), contentLength(0), serverPort(0), remotePort(0) {}
00322       private:
00324          boost::scoped_array<char> boundary;
00326          size_t boundarySize;
00327 
00329          boost::scoped_array<char> postBuffer;
00331          char* pPostBuffer;
00333          size_t minPostBufferSize(const size_t size) { return std::min(size, size_t(postBuffer.get()+contentLength-pPostBuffer)); }
00334       };
00335 
00337 
00343       void charToString(const char* data, size_t size, std::wstring& string);
00344 
00346 
00352       inline void charToString(const char* data, size_t size, std::string& string) { string.assign(data, size); }
00353 
00355 
00365       int atoi(const char* start, const char* end);
00366 
00368 
00375       template<class charT> void decodeUrlEncoded(const char* data, size_t size, std::map<std::basic_string<charT>, std::basic_string<charT> >& output, const char fieldSeperator='&');
00376 
00378 
00388       size_t percentEscapedToRealBytes(const char* source, char* destination, size_t size);
00389 
00393       extern const char base64Characters[];
00394 
00407       template<class In, class Out> void base64Encode(In start, In end, Out destination);
00408 
00425       template<class In, class Out> Out base64Decode(In start, In end, Out destination);
00426 
00430       class SessionId
00431       {
00435          public: static const int size=12;
00436 
00437       private:
00441          char data[size];
00442 
00446          boost::posix_time::ptime timestamp;
00447 
00451          static bool seeded;
00452 
00453          template<class T> friend class Sessions;
00454       public:
00458          SessionId();
00459 
00460          SessionId(const SessionId& x): timestamp(x.timestamp) { std::memcpy(data, x.data, size); }
00461          const SessionId& operator=(const SessionId& x) { std::memcpy(data, x.data, size); timestamp=x.timestamp; return *this; }
00462 
00470          template<class charT> const SessionId& operator=(charT* data_);
00471 
00479          template<class charT> SessionId(charT* data_) { *this=data_; }
00480 
00481          template<class charT, class Traits> friend std::basic_ostream<charT, Traits>& operator<<(std::basic_ostream<charT, Traits>& os, const SessionId& x);
00482 
00483          bool operator<(const SessionId& x) const { return std::memcmp(data, x.data, SessionId::size)<0; }
00484          bool operator==(const SessionId& x) const { return std::memcmp(data, x.data, SessionId::size)==0; }
00485 
00489          void refresh() const { *const_cast<boost::posix_time::ptime*>(&timestamp)=boost::posix_time::second_clock::universal_time(); }
00490 
00491          const char* getInternalPointer() const { return data; }
00492       };
00493 
00497       template<class charT, class Traits> std::basic_ostream<charT, Traits>& operator<<(std::basic_ostream<charT, Traits>& os, const SessionId& x) { base64Encode(x.data, x.data+SessionId::size, std::ostream_iterator<charT, charT, Traits>(os)); return os; }
00498 
00509       template<class T> class Sessions: public std::map<SessionId, T>
00510       {
00511       private:
00515          const boost::posix_time::seconds keepAlive;
00516 
00520          const boost::posix_time::seconds cleanupFrequency;
00521 
00525          boost::posix_time::ptime cleanupTime;
00526       public:
00527          typedef typename std::map<SessionId, T>::iterator iterator;
00528          typedef typename std::map<SessionId, T>::const_iterator const_iterator;
00535          Sessions(int keepAlive_, int cleanupFrequency_): keepAlive(boost::posix_time::seconds(keepAlive_)), cleanupFrequency(boost::posix_time::seconds(cleanupFrequency_)), cleanupTime(boost::posix_time::second_clock::universal_time()+cleanupFrequency) { }
00536             
00542          void cleanup();
00543 
00551          iterator generate(const T& value_ = T());
00552 
00553          boost::posix_time::ptime getExpiry(const_iterator it) const { return it->first.timestamp+keepAlive; }
00554       };
00555    }
00556 }
00557 
00558 template<class T> void Fastcgipp::Http::Sessions<T>::cleanup()
00559 {
00560    if(boost::posix_time::second_clock::universal_time() < cleanupTime)
00561       return;
00562    boost::posix_time::ptime oldest(boost::posix_time::second_clock::universal_time()-keepAlive);
00563    iterator it=this->begin();
00564    while(it!=this->end())
00565    {
00566       if(it->first.timestamp < oldest)
00567          erase(it++);
00568       else
00569          ++it;
00570    }
00571    cleanupTime=boost::posix_time::second_clock::universal_time()+cleanupFrequency;
00572 }
00573 
00574 template<class In, class Out> Out Fastcgipp::Http::base64Decode(In start, In end, Out destination)
00575 {
00576    Out dest=destination;
00577 
00578    for(int buffer, bitPos=-8, padStart; start!=end || bitPos>-6; ++dest)
00579    {
00580       if(bitPos==-8)
00581       {
00582          bitPos=18;
00583          padStart=-9;
00584          buffer=0;
00585          while(bitPos!=-6)
00586          {
00587             if(start==end) return destination;
00588             int value=*start++;
00589             if(value >= 'A' && 'Z' >= value) value -= 'A';
00590             else if(value >= 'a' && 'z' >= value) value -= 'a' - 26;
00591             else if(value >= '0' && '9' >= value) value -= '0' - 52;
00592             else if(value == '+') value = 62;
00593             else if(value == '/') value = 63;
00594             else if(value == '=') { padStart=bitPos; break; }
00595             else return destination;
00596 
00597             buffer |= value << bitPos;
00598             bitPos-=6;
00599          }
00600          bitPos=16;
00601       }
00602 
00603       *dest = (buffer >> bitPos) & 0xff;
00604       bitPos-=8;
00605       if(padStart>=bitPos)
00606       {
00607          if( (padStart-bitPos)/6 )
00608             return dest;
00609          else
00610             return ++dest;
00611       }
00612    }
00613 
00614    return dest;
00615 }
00616 
00617 template<class In, class Out> void Fastcgipp::Http::base64Encode(In start, In end, Out destination)
00618 {
00619    for(int buffer, bitPos=-6, padded; start!=end || bitPos>-6; ++destination)
00620    {
00621       if(bitPos==-6)
00622       {
00623          bitPos=16;
00624          buffer=0;
00625          padded=-6;
00626          while(bitPos!=-8)
00627          {
00628             if(start!=end) 
00629                buffer |= (uint32_t)*(unsigned char*)start++ << bitPos;
00630             else padded+=6;
00631             bitPos-=8;
00632          }
00633          bitPos=18;
00634       }
00635 
00636       if(padded == bitPos)
00637       {
00638          *destination='=';
00639          padded-=6;
00640       }
00641       else *destination=base64Characters[ (buffer >> bitPos)&0x3f ];
00642       bitPos -= 6;
00643    }
00644 }
00645 
00646 template<class T> typename Fastcgipp::Http::Sessions<T>::iterator Fastcgipp::Http::Sessions<T>::generate(const T& value_)
00647 {
00648    std::pair<iterator,bool> retVal;
00649    retVal.second=false;
00650    while(!retVal.second)
00651       retVal=insert(std::pair<SessionId, T>(SessionId(), value_));
00652    return retVal.first;
00653 }
00654 
00655 #endif