fastcgi++
|
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*>(×tamp)=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