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          const char* postBuffer() const { return m_postBuffer.get(); }
00320 
00322          void clearPostBuffer() { m_postBuffer.reset(); pPostBuffer=0; }
00323 
00324          Environment(): requestMethod(HTTP_METHOD_ERROR), etag(0), keepAlive(0), contentLength(0), serverPort(0), remotePort(0) {}
00325       private:
00327          boost::scoped_array<char> boundary;
00329          size_t boundarySize;
00330 
00332          boost::scoped_array<char> m_postBuffer;
00334          char* pPostBuffer;
00336          size_t minPostBufferSize(const size_t size) { return std::min(size, size_t(m_postBuffer.get()+contentLength-pPostBuffer)); }
00337       };
00338 
00340 
00346       void charToString(const char* data, size_t size, std::wstring& string);
00347 
00349 
00355       inline void charToString(const char* data, size_t size, std::string& string) { string.assign(data, size); }
00356 
00358 
00368       int atoi(const char* start, const char* end);
00369 
00371 
00378       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='&');
00379 
00381 
00391       size_t percentEscapedToRealBytes(const char* source, char* destination, size_t size);
00392 
00396       extern const char base64Characters[];
00397 
00410       template<class In, class Out> void base64Encode(In start, In end, Out destination);
00411 
00428       template<class In, class Out> Out base64Decode(In start, In end, Out destination);
00429 
00433       class SessionId
00434       {
00438          public: static const int size=12;
00439 
00440       private:
00444          char data[size];
00445 
00449          boost::posix_time::ptime timestamp;
00450 
00454          static bool seeded;
00455 
00456          template<class T> friend class Sessions;
00457       public:
00461          SessionId();
00462 
00463          SessionId(const SessionId& x): timestamp(x.timestamp) { std::memcpy(data, x.data, size); }
00464          const SessionId& operator=(const SessionId& x) { std::memcpy(data, x.data, size); timestamp=x.timestamp; return *this; }
00465 
00473          template<class charT> const SessionId& operator=(charT* data_);
00474 
00482          template<class charT> SessionId(charT* data_) { *this=data_; }
00483 
00484          template<class charT, class Traits> friend std::basic_ostream<charT, Traits>& operator<<(std::basic_ostream<charT, Traits>& os, const SessionId& x);
00485 
00486          bool operator<(const SessionId& x) const { return std::memcmp(data, x.data, SessionId::size)<0; }
00487          bool operator==(const SessionId& x) const { return std::memcmp(data, x.data, SessionId::size)==0; }
00488 
00492          void refresh() const { *const_cast<boost::posix_time::ptime*>(&timestamp)=boost::posix_time::second_clock::universal_time(); }
00493 
00494          const char* getInternalPointer() const { return data; }
00495       };
00496 
00500       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; }
00501 
00512       template<class T> class Sessions: public std::map<SessionId, T>
00513       {
00514       private:
00518          const boost::posix_time::seconds keepAlive;
00519 
00523          const boost::posix_time::seconds cleanupFrequency;
00524 
00528          boost::posix_time::ptime cleanupTime;
00529       public:
00530          typedef typename std::map<SessionId, T>::iterator iterator;
00531          typedef typename std::map<SessionId, T>::const_iterator const_iterator;
00538          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) { }
00539             
00545          void cleanup();
00546 
00554          iterator generate(const T& value_ = T());
00555 
00556          boost::posix_time::ptime getExpiry(const_iterator it) const { return it->first.timestamp+keepAlive; }
00557       };
00558    }
00559 }
00560 
00561 template<class T> void Fastcgipp::Http::Sessions<T>::cleanup()
00562 {
00563    if(boost::posix_time::second_clock::universal_time() < cleanupTime)
00564       return;
00565    boost::posix_time::ptime oldest(boost::posix_time::second_clock::universal_time()-keepAlive);
00566    iterator it=this->begin();
00567    while(it!=this->end())
00568    {
00569       if(it->first.timestamp < oldest)
00570          erase(it++);
00571       else
00572          ++it;
00573    }
00574    cleanupTime=boost::posix_time::second_clock::universal_time()+cleanupFrequency;
00575 }
00576 
00577 template<class In, class Out> Out Fastcgipp::Http::base64Decode(In start, In end, Out destination)
00578 {
00579    Out dest=destination;
00580 
00581    for(int buffer, bitPos=-8, padStart; start!=end || bitPos>-6; ++dest)
00582    {
00583       if(bitPos==-8)
00584       {
00585          bitPos=18;
00586          padStart=-9;
00587          buffer=0;
00588          while(bitPos!=-6)
00589          {
00590             if(start==end) return destination;
00591             int value=*start++;
00592             if(value >= 'A' && 'Z' >= value) value -= 'A';
00593             else if(value >= 'a' && 'z' >= value) value -= 'a' - 26;
00594             else if(value >= '0' && '9' >= value) value -= '0' - 52;
00595             else if(value == '+') value = 62;
00596             else if(value == '/') value = 63;
00597             else if(value == '=') { padStart=bitPos; break; }
00598             else return destination;
00599 
00600             buffer |= value << bitPos;
00601             bitPos-=6;
00602          }
00603          bitPos=16;
00604       }
00605 
00606       *dest = (buffer >> bitPos) & 0xff;
00607       bitPos-=8;
00608       if(padStart>=bitPos)
00609       {
00610          if( (padStart-bitPos)/6 )
00611             return dest;
00612          else
00613             return ++dest;
00614       }
00615    }
00616 
00617    return dest;
00618 }
00619 
00620 template<class In, class Out> void Fastcgipp::Http::base64Encode(In start, In end, Out destination)
00621 {
00622    for(int buffer, bitPos=-6, padded; start!=end || bitPos>-6; ++destination)
00623    {
00624       if(bitPos==-6)
00625       {
00626          bitPos=16;
00627          buffer=0;
00628          padded=-6;
00629          while(bitPos!=-8)
00630          {
00631             if(start!=end) 
00632                buffer |= (uint32_t)*(unsigned char*)start++ << bitPos;
00633             else padded+=6;
00634             bitPos-=8;
00635          }
00636          bitPos=18;
00637       }
00638 
00639       if(padded == bitPos)
00640       {
00641          *destination='=';
00642          padded-=6;
00643       }
00644       else *destination=base64Characters[ (buffer >> bitPos)&0x3f ];
00645       bitPos -= 6;
00646    }
00647 }
00648 
00649 template<class T> typename Fastcgipp::Http::Sessions<T>::iterator Fastcgipp::Http::Sessions<T>::generate(const T& value_)
00650 {
00651    std::pair<iterator,bool> retVal;
00652    retVal.second=false;
00653    while(!retVal.second)
00654       retVal=insert(std::pair<SessionId, T>(SessionId(), value_));
00655    return retVal.first;
00656 }
00657 
00658 #endif