fastcgi++
http.cpp
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 #include <boost/date_time/posix_time/posix_time.hpp>
00023 
00024 #include <fastcgi++/http.hpp>
00025 #include <fastcgi++/protocol.hpp>
00026 
00027 #include "utf8_codecvt.hpp"
00028 
00029 void Fastcgipp::Http::charToString(const char* data, size_t size, std::wstring& string)
00030 {
00031    const size_t bufferSize=512;
00032    wchar_t buffer[bufferSize];
00033    using namespace std;
00034 
00035    if(size)
00036    {
00037       codecvt_base::result cr=codecvt_base::partial;
00038       while(cr==codecvt_base::partial)
00039       {{
00040          wchar_t* it;
00041          const char* tmpData;
00042          mbstate_t conversionState = mbstate_t();
00043          cr=use_facet<codecvt<wchar_t, char, mbstate_t> >(locale(locale::classic(), new utf8CodeCvt::utf8_codecvt_facet)).in(conversionState, data, data+size, tmpData, buffer, buffer+bufferSize, it);
00044          string.append(buffer, it);
00045          size-=tmpData-data;
00046          data=tmpData;
00047       }}
00048       if(cr==codecvt_base::error) throw Exceptions::CodeCvt();
00049    }
00050 }
00051 
00052 int Fastcgipp::Http::atoi(const char* start, const char* end)
00053 {
00054    bool neg=false;
00055    if(*start=='-')
00056    {
00057       neg=true;
00058       ++start;
00059    }
00060    int result=0;
00061    for(; 0x30 <= *start && *start <= 0x39 && start<end; ++start)
00062       result=result*10+(*start&0x0f);
00063 
00064    return neg?-result:result;
00065 }
00066 
00067 size_t Fastcgipp::Http::percentEscapedToRealBytes(const char* source, char* destination, size_t size)
00068 {
00069    if (size < 1) return 0;
00070 
00071    unsigned int i=0;
00072    char* start=destination;
00073    while(1)
00074    {
00075       if(*source=='%')
00076       {
00077          *destination=0;
00078          for(int shift=4; shift>=0; shift-=4)
00079          {
00080             if(++i>=size) break;
00081             ++source;
00082             if((*source|0x20) >= 'a' && (*source|0x20) <= 'f')
00083                *destination|=((*source|0x20)-0x57)<<shift;
00084             else if(*source >= '0' && *source <= '9')
00085                *destination|=(*source&0x0f)<<shift;
00086          }
00087          ++source;
00088          ++destination;
00089       }
00090       else if(*source=='+')
00091       {
00092          *destination++=' ';
00093          ++source;
00094       }
00095       else
00096          *destination++=*source++;
00097 
00098       if(++i>=size) break;
00099    }
00100    return destination-start;
00101 }
00102 
00103 template void Fastcgipp::Http::Environment<char>::fill(const char* data, size_t size);
00104 template void Fastcgipp::Http::Environment<wchar_t>::fill(const char* data, size_t size);
00105 template<class charT> void Fastcgipp::Http::Environment<charT>::fill(const char* data, size_t size)
00106 {
00107    using namespace std;
00108    using namespace boost;
00109 
00110    while(size)
00111    {{
00112       size_t nameSize;
00113       size_t valueSize;
00114       const char* name;
00115       const char* value;
00116       Protocol::processParamHeader(data, size, name, nameSize, value, valueSize);
00117       size-=value-data+valueSize;
00118       data=value+valueSize;
00119 
00120       switch(nameSize)
00121       {
00122       case 9:
00123          if(!memcmp(name, "HTTP_HOST", 9))
00124             charToString(value, valueSize, host);
00125          else if(!memcmp(name, "PATH_INFO", 9))
00126          {
00127             boost::scoped_array<char> buffer(new char[valueSize]);
00128             const char* source=value;
00129             int size=-1;
00130             for(; source<value+valueSize+1; ++source, ++size)
00131             {
00132                if(*source == '/' || source == value+valueSize)
00133                {
00134                   if(size > 0)
00135                   {
00136                      percentEscapedToRealBytes(source-size, buffer.get(), size);
00137                      pathInfo.push_back(std::basic_string<charT>());
00138                      charToString(buffer.get(), size, pathInfo.back());
00139                   }
00140                   size=-1;                
00141                }
00142             }
00143          }
00144          break;
00145       case 11:
00146          if(!memcmp(name, "HTTP_ACCEPT", 11))
00147             charToString(value, valueSize, acceptContentTypes);
00148          else if(!memcmp(name, "HTTP_COOKIE", 11))
00149             decodeUrlEncoded(value, valueSize, cookies, ';');
00150          else if(!memcmp(name, "SERVER_ADDR", 11))
00151             serverAddress.assign(value, value+valueSize);
00152          else if(!memcmp(name, "REMOTE_ADDR", 11))
00153             remoteAddress.assign(value, value+valueSize);
00154          else if(!memcmp(name, "SERVER_PORT", 11))
00155             serverPort=atoi(value, value+valueSize);
00156          else if(!memcmp(name, "REMOTE_PORT", 11))
00157             remotePort=atoi(value, value+valueSize);
00158          else if(!memcmp(name, "SCRIPT_NAME", 11))
00159             charToString(value, valueSize, scriptName);
00160          else if(!memcmp(name, "REQUEST_URI", 11))
00161             charToString(value, valueSize, requestUri);
00162          break;
00163       case 12:
00164          if(!memcmp(name, "HTTP_REFERER", 12) && valueSize)
00165             charToString(value, valueSize, referer);
00166          else if(!memcmp(name, "CONTENT_TYPE", 12))
00167          {
00168             const char* end=(char*)memchr(value, ';', valueSize);
00169             charToString(value, end?end-value:valueSize, contentType);
00170             if(end)
00171             {
00172                const char* start=(char*)memchr(end, '=', valueSize-(end-data));
00173                if(start)
00174                {
00175                   boundarySize=valueSize-(++start-value);
00176                   boundary.reset(new char[boundarySize]);
00177                   memcpy(boundary.get(), start, boundarySize);
00178                }
00179             }
00180          }
00181          else if(!memcmp(name, "QUERY_STRING", 12) && valueSize)
00182             decodeUrlEncoded(value, valueSize, gets);
00183          break;
00184       case 13:
00185          if(!memcmp(name, "DOCUMENT_ROOT", 13))
00186             charToString(value, valueSize, root);
00187          break;
00188       case 14:
00189          if(!memcmp(name, "REQUEST_METHOD", 14))
00190          {
00191             requestMethod = HTTP_METHOD_ERROR;
00192             switch(valueSize)
00193             {
00194             case 3:
00195                if(!memcmp(value, requestMethodLabels[HTTP_METHOD_GET], 3)) requestMethod = HTTP_METHOD_GET;
00196                else if(!memcmp(value, requestMethodLabels[HTTP_METHOD_PUT], 3)) requestMethod = HTTP_METHOD_PUT;
00197                break;
00198             case 4:
00199                if(!memcmp(value, requestMethodLabels[HTTP_METHOD_HEAD], 4)) requestMethod = HTTP_METHOD_HEAD;
00200                else if(!memcmp(value, requestMethodLabels[HTTP_METHOD_POST], 4)) requestMethod = HTTP_METHOD_POST;
00201                break;
00202             case 5:
00203                if(!memcmp(value, requestMethodLabels[HTTP_METHOD_TRACE], 5)) requestMethod = HTTP_METHOD_TRACE;
00204                break;
00205             case 6:
00206                if(!memcmp(value, requestMethodLabels[HTTP_METHOD_DELETE], 6)) requestMethod = HTTP_METHOD_DELETE;
00207                break;
00208             case 7:
00209                if(!memcmp(value, requestMethodLabels[HTTP_METHOD_OPTIONS], 7)) requestMethod = HTTP_METHOD_OPTIONS;
00210                else if(!memcmp(value, requestMethodLabels[HTTP_METHOD_OPTIONS], 7)) requestMethod = HTTP_METHOD_CONNECT;
00211                break;
00212             }
00213          }
00214          else if(!memcmp(name, "CONTENT_LENGTH", 14))
00215             contentLength=atoi(value, value+valueSize);
00216          break;
00217       case 15:
00218          if(!memcmp(name, "HTTP_USER_AGENT", 15))
00219             charToString(value, valueSize, userAgent);
00220          else if(!memcmp(name, "HTTP_KEEP_ALIVE", 15))
00221             keepAlive=atoi(value, value+valueSize);
00222          break;
00223       case 18:
00224          if(!memcmp(name, "HTTP_IF_NONE_MATCH", 18))
00225             etag=atoi(value, value+valueSize);
00226          break;
00227       case 19:
00228          if(!memcmp(name, "HTTP_ACCEPT_CHARSET", 19))
00229             charToString(value, valueSize, acceptCharsets);
00230          break;
00231       case 20:
00232          if(!memcmp(name, "HTTP_ACCEPT_LANGUAGE", 20))
00233             charToString(value, valueSize, acceptLanguages);
00234          break;
00235       case 22:
00236          if(!memcmp(name, "HTTP_IF_MODIFIED_SINCE", 22))
00237          {
00238             stringstream dateStream;
00239             dateStream.write(value, valueSize);
00240             dateStream.imbue(locale(locale::classic(), new posix_time::time_input_facet("%a, %d %b %Y %H:%M:%S GMT")));
00241             dateStream >> ifModifiedSince;
00242          }
00243          break;
00244       }
00245    }}
00246 }
00247 
00248 template bool Fastcgipp::Http::Environment<char>::fillPostBuffer(const char* data, size_t size);
00249 template bool Fastcgipp::Http::Environment<wchar_t>::fillPostBuffer(const char* data, size_t size);
00250 template<class charT> bool Fastcgipp::Http::Environment<charT>::fillPostBuffer(const char* data, size_t size)
00251 {
00252    if(!m_postBuffer)
00253    {
00254       m_postBuffer.reset(new char[contentLength]);
00255       pPostBuffer=m_postBuffer.get();
00256    }
00257 
00258    size_t trueSize=minPostBufferSize(size);
00259    if(trueSize)
00260    {
00261       std::memcpy(pPostBuffer, data, trueSize);
00262       pPostBuffer+=trueSize;
00263       return true;
00264    }
00265    else
00266       return false;
00267 }
00268 
00269 template void Fastcgipp::Http::Environment<char>::parsePostsMultipart();
00270 template void Fastcgipp::Http::Environment<wchar_t>::parsePostsMultipart();
00271 template<class charT> void Fastcgipp::Http::Environment<charT>::parsePostsMultipart()
00272 {
00273    using namespace std;
00274 
00275    if(!m_postBuffer)
00276       return;
00277 
00278    const char cName[] = "name=\"";
00279    const char cFilename[] = "filename=\"";
00280    const char cContentType[] = "Content-Type: ";
00281    const char cBodyStart[] = "\r\n\r\n";
00282 
00283    pPostBuffer=m_postBuffer.get()+boundarySize+1;
00284    const char* contentTypeStart=0;
00285    ssize_t contentTypeSize=-1;
00286    const char* nameStart=0;
00287    ssize_t nameSize=-1;
00288    const char* filenameStart=0;
00289    ssize_t filenameSize=-1;
00290    const char* bodyStart=0;
00291    ssize_t bodySize=-1;
00292    enum ParseState { HEADER, NAME, FILENAME, CONTENT_TYPE, BODY } parseState=HEADER;
00293    for(pPostBuffer=m_postBuffer.get()+boundarySize+2; pPostBuffer<m_postBuffer.get()+contentLength; ++pPostBuffer)
00294    {
00295       switch(parseState)
00296       {
00297          case HEADER:
00298          {
00299             if(nameSize == -1)
00300             {
00301                const size_t size=minPostBufferSize(sizeof(cName)-1);
00302                if(!memcmp(pPostBuffer, cName, size))
00303                {
00304                   pPostBuffer+=size-1;
00305                   nameStart=pPostBuffer+1;
00306                   parseState=NAME;
00307                   continue;
00308                }
00309             }
00310             if(filenameSize == -1)
00311             {
00312                const size_t size=minPostBufferSize(sizeof(cFilename)-1);
00313                if(!memcmp(pPostBuffer, cFilename, size))
00314                {
00315                   pPostBuffer+=size-1;
00316                   filenameStart=pPostBuffer+1;
00317                   parseState=FILENAME;
00318                   continue;
00319                }
00320             }
00321             if(contentTypeSize == -1)
00322             {
00323                const size_t size=minPostBufferSize(sizeof(cContentType)-1);
00324                if(!memcmp(pPostBuffer, cContentType, size))
00325                {
00326                   pPostBuffer+=size-1;
00327                   contentTypeStart=pPostBuffer+1;
00328                   parseState=CONTENT_TYPE;
00329                   continue;
00330                }
00331             }
00332             if(bodySize == -1)
00333             {
00334                const size_t size=minPostBufferSize(sizeof(cBodyStart)-1);
00335                if(!memcmp(pPostBuffer, cBodyStart, size))
00336                {
00337                   pPostBuffer+=size-1;
00338                   bodyStart=pPostBuffer+1;
00339                   parseState=BODY;
00340                   continue;
00341                }
00342             }
00343             continue;
00344          }
00345 
00346          case NAME:
00347          {
00348             if(*pPostBuffer == '"')
00349             {
00350                nameSize=pPostBuffer-nameStart;
00351                parseState=HEADER;
00352             }
00353             continue;
00354          }
00355 
00356          case FILENAME:
00357          {
00358             if(*pPostBuffer == '"')
00359             {
00360                filenameSize=pPostBuffer-filenameStart;
00361                parseState=HEADER;
00362             }
00363             continue;
00364          }
00365 
00366          case CONTENT_TYPE:
00367          {
00368             if(*pPostBuffer == '\r' || *pPostBuffer == '\n')
00369             {
00370                contentTypeSize=pPostBuffer-contentTypeStart;
00371                --pPostBuffer;
00372                parseState=HEADER;
00373             }
00374             continue;
00375          }
00376 
00377          case BODY:
00378          {
00379             const size_t size=minPostBufferSize(sizeof(boundarySize)-1);
00380             if(!memcmp(pPostBuffer, boundary.get(), size))
00381             {
00382                bodySize=pPostBuffer-bodyStart-2;
00383                if(bodySize<0) bodySize=0;
00384                else if(bodySize>=2 && *(bodyStart+bodySize-1)=='\n' && *(bodyStart+bodySize-2)=='\r')
00385                   bodySize -= 2;
00386 
00387                if(nameSize != -1)
00388                {
00389                   basic_string<charT> name;
00390                   charToString(nameStart, nameSize, name);
00391 
00392                   Post<charT>& thePost=posts[name];
00393                   if(contentTypeSize != -1)
00394                   {
00395                      thePost.type=Post<charT>::file;
00396                      charToString(contentTypeStart, contentTypeSize, thePost.contentType);
00397                      if(filenameSize != -1) charToString(filenameStart, filenameSize, thePost.filename);
00398                      thePost.m_size=bodySize;
00399                      if(bodySize)
00400                      {
00401                         thePost.m_data = new char[bodySize];
00402                         memcpy(thePost.m_data, bodyStart, bodySize);
00403                      }
00404                   }
00405                   else
00406                   {
00407                      thePost.type=Post<charT>::form;
00408                      charToString(bodyStart, bodySize, thePost.value);
00409                   }
00410                }
00411 
00412                pPostBuffer+=size;
00413                parseState=HEADER;
00414                contentTypeStart=0;
00415                contentTypeSize=-1;
00416                nameStart=0;
00417                nameSize=-1;
00418                filenameStart=0;
00419                filenameSize=-1;
00420                bodyStart=0;
00421                bodySize=-1;
00422             }
00423             continue;
00424          }
00425       }
00426    }
00427 }
00428 
00429 template void Fastcgipp::Http::Environment<char>::parsePostsUrlEncoded();
00430 template void Fastcgipp::Http::Environment<wchar_t>::parsePostsUrlEncoded();
00431 template<class charT> void Fastcgipp::Http::Environment<charT>::parsePostsUrlEncoded()
00432 {
00433    if(!m_postBuffer)
00434       return;
00435 
00436    char* nameStart=m_postBuffer.get();
00437    size_t nameSize;
00438    char* valueStart=0;
00439    size_t valueSize;
00440 
00441    for(char* i=m_postBuffer.get(); i<=m_postBuffer.get()+contentLength; ++i)
00442    {
00443       if(*i == '=' && nameStart && !valueStart)
00444       {
00445          nameSize=percentEscapedToRealBytes(nameStart, nameStart, i-nameStart);
00446          valueStart=i+1;
00447       }
00448       else if( (i==m_postBuffer.get()+contentLength || *i == '&') && nameStart && valueStart)
00449       {
00450          valueSize=percentEscapedToRealBytes(valueStart, valueStart, i-valueStart);
00451 
00452          std::basic_string<charT> name;
00453          charToString(nameStart, nameSize, name);
00454          nameStart=i+1;
00455          Post<charT>& thePost=posts[name];
00456          thePost.type=Post<charT>::form;
00457          charToString(valueStart, valueSize, thePost.value);
00458          valueStart=0;
00459       }
00460    }
00461 }
00462 
00463 bool Fastcgipp::Http::SessionId::seeded=false;
00464 
00465 Fastcgipp::Http::SessionId::SessionId()
00466 {
00467    if(!seeded)
00468    {
00469       std::srand(boost::posix_time::microsec_clock::universal_time().time_of_day().fractional_seconds());
00470       seeded=true;
00471    }
00472 
00473    for(char* i=data; i<data+size; ++i)
00474       *i=char(rand()%256);
00475    timestamp = boost::posix_time::second_clock::universal_time();
00476 }
00477 
00478 template const Fastcgipp::Http::SessionId& Fastcgipp::Http::SessionId::operator=<const char>(const char* data_);
00479 template const Fastcgipp::Http::SessionId& Fastcgipp::Http::SessionId::operator=<const wchar_t>(const wchar_t* data_);
00480 template<class charT> const Fastcgipp::Http::SessionId& Fastcgipp::Http::SessionId::operator=(charT* data_)
00481 {
00482    std::memset(data, 0, size);
00483    base64Decode(data_, data_+size*4/3, data);
00484    timestamp = boost::posix_time::second_clock::universal_time();
00485    return *this;
00486 }
00487 
00488 template void Fastcgipp::Http::decodeUrlEncoded<char>(const char* data, size_t size, std::map<std::basic_string<char>, std::basic_string<char> >& output, const char fieldSeperator);
00489 template void Fastcgipp::Http::decodeUrlEncoded<wchar_t>(const char* data, size_t size, std::map<std::basic_string<wchar_t>, std::basic_string<wchar_t> >& output, const char fieldSeperator);
00490 template<class charT> void Fastcgipp::Http::decodeUrlEncoded(const char* data, size_t size, std::map<std::basic_string<charT>, std::basic_string<charT> >& output, const char fieldSeperator)
00491 {
00492    using namespace std;
00493 
00494    boost::scoped_array<char> buffer(new char[size]);
00495    memcpy(buffer.get(), data, size);
00496 
00497    char* nameStart=buffer.get();
00498    size_t nameSize;
00499    char* valueStart=0;
00500    size_t valueSize;
00501    for(char* i=buffer.get(); i<=buffer.get()+size; ++i)
00502    {
00503       if(i==buffer.get()+size || *i == fieldSeperator)
00504       {
00505          if(nameStart && valueStart)
00506          {
00507             valueSize=percentEscapedToRealBytes(valueStart, valueStart, i-valueStart);
00508 
00509             basic_string<charT> name;
00510             charToString(nameStart, nameSize, name);
00511             nameStart=i+1;
00512             basic_string<charT>& value=output[name];
00513             charToString(valueStart, valueSize, value);
00514             valueStart=0;
00515          }
00516       }
00517 
00518       else if(*i == ' ' && nameStart && !valueStart)
00519          ++nameStart;
00520 
00521       else if(*i == '=' && nameStart && !valueStart)
00522       {
00523          nameSize=percentEscapedToRealBytes(nameStart, nameStart, i-nameStart);
00524          valueStart=i+1;
00525       }
00526    }
00527 }
00528 
00529 const char Fastcgipp::Http::base64Characters[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
00530 const char* Fastcgipp::Http::requestMethodLabels[]= {
00531    "ERROR",
00532    "HEAD",
00533    "GET",
00534    "POST",
00535    "PUT",
00536    "DELETE",
00537    "TRACE",
00538    "OPTIONS",
00539    "CONNECT"
00540 };
00541 
00542 template const std::basic_string<char>& Fastcgipp::Http::Environment<char>::findCookie(const char* key) const;
00543 template const std::basic_string<wchar_t>& Fastcgipp::Http::Environment<wchar_t>::findCookie(const wchar_t* key) const;
00544 template<class charT> const std::basic_string<charT>& Fastcgipp::Http::Environment<charT>::findCookie(const charT* key) const
00545 {
00546    static const std::basic_string<charT> emptyString;
00547    typename Cookies::const_iterator it=cookies.find(key);
00548    if(it==cookies.end())
00549       return emptyString;
00550    else
00551       return it->second;
00552 }
00553 
00554 template const std::basic_string<char>& Fastcgipp::Http::Environment<char>::findGet(const char* key) const;
00555 template const std::basic_string<wchar_t>& Fastcgipp::Http::Environment<wchar_t>::findGet(const wchar_t* key) const;
00556 template<class charT> const std::basic_string<charT>& Fastcgipp::Http::Environment<charT>::findGet(const charT* key) const
00557 {
00558    static const std::basic_string<charT> emptyString;
00559    typename Gets::const_iterator it=gets.find(key);
00560    if(it==gets.end())
00561       return emptyString;
00562    else
00563       return it->second;
00564 }
00565 
00566 template const Fastcgipp::Http::Post<char>& Fastcgipp::Http::Environment<char>::findPost(const char* key) const;
00567 template const Fastcgipp::Http::Post<wchar_t>& Fastcgipp::Http::Environment<wchar_t>::findPost(const wchar_t* key) const;
00568 template<class charT> const Fastcgipp::Http::Post<charT>& Fastcgipp::Http::Environment<charT>::findPost(const charT* key) const
00569 {
00570    static const Post<charT> emptyPost;
00571    typename Posts::const_iterator it=posts.find(key);
00572    if(it==posts.end())
00573       return emptyPost;
00574    else
00575       return it->second;
00576 }
00577 
00578 template bool Fastcgipp::Http::Environment<char>::checkForGet(const char* key) const;
00579 template bool Fastcgipp::Http::Environment<wchar_t>::checkForGet(const wchar_t* key) const;
00580 template<class charT> bool Fastcgipp::Http::Environment<charT>::checkForGet(const charT* key) const
00581 {
00582    typename Gets::const_iterator it=gets.find(key);
00583    if(it==gets.end())
00584       return false;
00585    else
00586       return true;
00587 }
00588 
00589 template bool Fastcgipp::Http::Environment<char>::checkForPost(const char* key) const;
00590 template bool Fastcgipp::Http::Environment<wchar_t>::checkForPost(const wchar_t* key) const;
00591 template<class charT> bool Fastcgipp::Http::Environment<charT>::checkForPost(const charT* key) const
00592 {
00593    typename Posts::const_iterator it=posts.find(key);
00594    if(it==posts.end())
00595       return false;
00596    else
00597       return true;
00598 }
00599 
00600 Fastcgipp::Http::Address& Fastcgipp::Http::Address::operator&=(const Address& x)
00601 {
00602    *(uint64_t*)m_data &= *(const uint64_t*)x.m_data;
00603    *(uint64_t*)(m_data+size/2) &= *(const uint64_t*)(x.m_data+size/2);
00604 
00605    return *this;
00606 }
00607 
00608 Fastcgipp::Http::Address Fastcgipp::Http::Address::operator&(const Address& x) const
00609 {
00610    Address address(*this);
00611    address &= x;
00612 
00613    return address;
00614 }
00615 
00616 void Fastcgipp::Http::Address::assign(const char* start, const char* end)
00617 {
00618    const char* read=start-1;
00619    unsigned char* write=m_data;
00620    unsigned char* pad=0;
00621    unsigned char offset;
00622    uint16_t chunk=0;
00623    bool error=false;
00624 
00625    while(1)
00626    {
00627       ++read;
00628       if(read >= end || *read == ':')
00629       {
00630          if(read == start || *(read-1) == ':')
00631          {
00632             if(pad && pad != write)
00633             {
00634                error=true;
00635                break;
00636             }
00637             else
00638                pad = write;
00639          }
00640          else
00641          {
00642             *write = (chunk&0xff00)>>8;
00643             *(write+1) = chunk&0x00ff;
00644             chunk = 0;
00645             write += 2;
00646             if(write>=m_data+size || read >= end)
00647                break;
00648          }
00649          continue;
00650       }
00651       else if('0' <= *read && *read <= '9')
00652          offset = '0';
00653       else if('A' <= *read && *read <= 'F')
00654          offset = 'A'-10;
00655       else if('a' <= *read && *read <= 'f')
00656          offset = 'a'-10;
00657       else if(*read == '.')
00658       {
00659          if(write == m_data)
00660          {
00661             // We must be getting a pure ipv4 formatted address. Not an ::ffff:xxx.xxx.xxx.xxx style ipv4 address.
00662             *(uint16_t*)write = 0xffff;
00663             pad = m_data;
00664             write+=2;
00665          }
00666          else if(write - m_data > 12)
00667          {
00668             // We don't have enought space for an ipv4 address
00669             error=true;
00670             break;
00671          }
00672 
00673          // First convert the value stored in chunk to the first part of the ipv4 address
00674          *write = 0;
00675          for(int i=0; i<3; ++i)
00676          {
00677             *write = *write * 10 + ((chunk&0x0f00)>>8);
00678             chunk <<= 4;
00679          }
00680          ++write;
00681 
00682          // Now we'll get the remaining pieces
00683          for(int i=0; i<3 && read<end; ++i)
00684          {
00685             const char* point=(const char*)memchr(read, '.', end-read);
00686             if(point && point<end-1)
00687                read=point;
00688             else
00689             {
00690                error=true;
00691                break;
00692             }
00693             *write++ = atoi(++read, end);
00694          }
00695          break;
00696       }
00697       else
00698       {
00699          error=true;
00700          break;
00701       }
00702       chunk <<= 4;
00703       chunk |= *read-offset;
00704    }
00705 
00706    if(error)
00707       std::memset(m_data, 0, size);
00708    else if(pad)
00709    {
00710       if(pad==write)
00711          std::memset(write, 0, size-(write-m_data));
00712       else
00713       {
00714          const size_t padSize=m_data+size-write;
00715          std::memmove(pad+padSize, pad, write-pad);
00716          std::memset(pad, 0, padSize);
00717       }
00718    }
00719 }
00720 
00721 template std::basic_ostream<char, std::char_traits<char> >& Fastcgipp::Http::operator<< <char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >& os, const Address& address);
00722 template std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& Fastcgipp::Http::operator<< <wchar_t, std::char_traits<wchar_t> >(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& os, const Address& address);
00723 template<class charT, class Traits> std::basic_ostream<charT, Traits>& Fastcgipp::Http::operator<<(std::basic_ostream<charT, Traits>& os, const Address& address)
00724 {
00725    using namespace std;
00726    if(!os.good()) return os;
00727    
00728    try
00729    {
00730       typename basic_ostream<charT, Traits>::sentry opfx(os);
00731       if(opfx)
00732       {
00733          streamsize fieldWidth=os.width(0);
00734          charT buffer[40];
00735          charT* bufPtr=buffer;
00736          locale loc(os.getloc(), new num_put<charT, charT*>);
00737 
00738          const uint16_t* subStart=0;
00739          const uint16_t* subEnd=0;
00740          {
00741             const uint16_t* subStartCandidate;
00742             const uint16_t* subEndCandidate;
00743             bool inZero = false;
00744 
00745             for(const uint16_t* it = (const uint16_t*)address.data(); it < (const uint16_t*)(address.data()+Address::size); ++it)
00746             {
00747                if(*it == 0)
00748                {
00749                   if(!inZero)
00750                   {
00751                      subStartCandidate = it;
00752                      subEndCandidate = it;
00753                      inZero=true;
00754                   }
00755                   ++subEndCandidate;
00756                }
00757                else if(inZero)
00758                {
00759                   if(subEndCandidate-subStartCandidate > subEnd-subStart)
00760                   {
00761                      subStart=subStartCandidate;
00762                      subEnd=subEndCandidate-1;
00763                   }
00764                   inZero=false;
00765                }
00766             }
00767             if(inZero)
00768             {
00769                if(subEndCandidate-subStartCandidate > subEnd-subStart)
00770                {
00771                   subStart=subStartCandidate;
00772                   subEnd=subEndCandidate-1;
00773                }
00774                inZero=false;
00775             }
00776          }
00777 
00778          ios_base::fmtflags oldFlags = os.flags();
00779          os.setf(ios::hex, ios::basefield);
00780 
00781          if(subStart==(const uint16_t*)address.data() && subEnd==(const uint16_t*)address.data()+4 && *((const uint16_t*)address.data()+5) == 0xffff)
00782          {
00783             // It is an ipv4 address
00784             *bufPtr++=os.widen(':');
00785             *bufPtr++=os.widen(':');
00786             bufPtr=use_facet<num_put<charT, charT*> >(loc).put(bufPtr, os, os.fill(), static_cast<unsigned long int>(0xffff));
00787             *bufPtr++=os.widen(':');
00788             os.setf(ios::dec, ios::basefield);
00789 
00790             for(const unsigned char* it = address.data()+12; it < address.data()+Address::size; ++it)
00791             {
00792                bufPtr=use_facet<num_put<charT, charT*> >(loc).put(bufPtr, os, os.fill(), static_cast<unsigned long int>(*it));
00793                *bufPtr++=os.widen('.');
00794             }
00795             --bufPtr;
00796          }
00797          else
00798          {
00799             // It is an ipv6 address
00800             for(const uint16_t* it = (const uint16_t*)address.data(); it < (const uint16_t*)(address.data()+Address::size); ++it)
00801             {
00802                if(subStart <= it && it <= subEnd)
00803                {
00804                   if(it == subStart && it == (const uint16_t*)address.data())
00805                      *bufPtr++=os.widen(':');
00806                   if(it == subEnd)
00807                      *bufPtr++=os.widen(':');
00808                }
00809                else
00810                {
00811                   bufPtr=use_facet<num_put<charT, charT*> >(loc).put(bufPtr, os, os.fill(), static_cast<unsigned long int>(Protocol::readBigEndian(*it)));
00812 
00813                   if(it < (const uint16_t*)(address.data()+Address::size)-1)
00814                      *bufPtr++=os.widen(':');
00815                }
00816             }
00817          }
00818 
00819          os.flags(oldFlags);
00820 
00821          charT* ptr=buffer;
00822          ostreambuf_iterator<charT,Traits> sink(os);
00823          if(os.flags() & ios_base::left)
00824             for(int i=max(fieldWidth, bufPtr-buffer); i>0; i--)
00825             {
00826                if(ptr!=bufPtr) *sink++=*ptr++;
00827                else *sink++=os.fill();
00828             }
00829          else
00830             for(int i=fieldWidth-(bufPtr-buffer); ptr!=bufPtr;)
00831             {
00832                if(i>0) { *sink++=os.fill(); --i; }
00833                else *sink++=*ptr++;
00834             }
00835 
00836          if(sink.failed()) os.setstate(ios_base::failbit);
00837       }
00838    }
00839    catch(bad_alloc&)
00840    {
00841       ios_base::iostate exception_mask = os.exceptions();
00842       os.exceptions(ios_base::goodbit);
00843       os.setstate(ios_base::badbit);
00844       os.exceptions(exception_mask);
00845       if(exception_mask & ios_base::badbit) throw;
00846    }
00847    catch(...)
00848    {
00849       ios_base::iostate exception_mask = os.exceptions();
00850       os.exceptions(ios_base::goodbit);
00851       os.setstate(ios_base::failbit);
00852       os.exceptions(exception_mask);
00853       if(exception_mask & ios_base::failbit) throw;
00854    }
00855    return os;
00856 }
00857 
00858 template std::basic_istream<char, std::char_traits<char> >& Fastcgipp::Http::operator>> <char, std::char_traits<char> >(std::basic_istream<char, std::char_traits<char> >& is, Address& address);
00859 template std::basic_istream<wchar_t, std::char_traits<wchar_t> >& Fastcgipp::Http::operator>> <wchar_t, std::char_traits<wchar_t> >(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& is, Address& address);
00860 template<class charT, class Traits> std::basic_istream<charT, Traits>& Fastcgipp::Http::operator>>(std::basic_istream<charT, Traits>& is, Address& address)
00861 {
00862    using namespace std;
00863    if(!is.good()) return is;
00864 
00865    ios_base::iostate err = ios::goodbit;
00866    try
00867    {
00868       typename basic_istream<charT, Traits>::sentry ipfx(is);
00869       if(ipfx)
00870       {
00871          istreambuf_iterator<charT, Traits> read(is);
00872          unsigned char buffer[Address::size];
00873          unsigned char* write=buffer;
00874          unsigned char* pad=0;
00875          unsigned char offset;
00876          unsigned char count=0;
00877          uint16_t chunk=0;
00878          charT lastChar=0;
00879 
00880          for(;;++read)
00881          {
00882             if(++count>40)
00883             {
00884                err = ios::failbit;
00885                break;
00886             }
00887             else if('0' <= *read && *read <= '9')
00888                offset = '0';
00889             else if('A' <= *read && *read <= 'F')
00890                offset = 'A'-10;
00891             else if('a' <= *read && *read <= 'f')
00892                offset = 'a'-10;
00893             else if(*read == '.')
00894             {
00895                if(write == buffer)
00896                {
00897                   // We must be getting a pure ipv4 formatted address. Not an ::ffff:xxx.xxx.xxx.xxx style ipv4 address.
00898                   *(uint16_t*)write = 0xffff;
00899                   pad = buffer;
00900                   write+=2;
00901                }
00902                else if(write - buffer > 12)
00903                {
00904                   // We don't have enought space for an ipv4 address
00905                   err = ios::failbit;
00906                   break;
00907                }
00908 
00909                // First convert the value stored in chunk to the first part of the ipv4 address
00910                *write = 0;
00911                for(int i=0; i<3; ++i)
00912                {
00913                   *write = *write * 10 + ((chunk&0x0f00)>>8);
00914                   chunk <<= 4;
00915                }
00916                ++write;
00917 
00918                // Now we'll get the remaining pieces
00919                for(int i=0; i<3; ++i)
00920                {
00921                   if(*read != is.widen('.'))
00922                   {
00923                      err = ios::failbit;
00924                      break;
00925                   }
00926                   unsigned int value;
00927                   use_facet<num_get<charT, istreambuf_iterator<charT, Traits> > >(is.getloc()).get(++read, istreambuf_iterator<charT, Traits>(), is, err, value);
00928                   *write++ = value;
00929                }
00930                break;
00931             }
00932             else
00933             {
00934                if(*read == ':' && (!lastChar || lastChar == ':'))
00935                {
00936                   if(pad && pad != write)
00937                   {
00938                      err = ios::failbit;
00939                      break;
00940                   }
00941                   else
00942                      pad = write;
00943                }
00944                else
00945                {
00946                   *write = (chunk&0xff00)>>8;
00947                   *(write+1) = chunk&0x00ff;
00948                   chunk = 0;
00949                   write += 2;
00950                   if(write>=buffer+Address::size)
00951                      break;
00952                   if(*read!=':')
00953                   {
00954                      if(!pad)
00955                         err = ios::failbit;
00956                      break;
00957                   }
00958                }
00959                lastChar=':';
00960                continue;
00961             }
00962             chunk <<= 4;
00963             chunk |= *read-offset;
00964             lastChar=*read;
00965             
00966          }
00967 
00968          if(err == ios::goodbit)
00969          {
00970             if(pad)
00971             {
00972                if(pad==write)
00973                   std::memset(write, 0, Address::size-(write-buffer));
00974                else
00975                {
00976                   const size_t padSize=buffer+Address::size-write;
00977                   std::memmove(pad+padSize, pad, write-pad);
00978                   std::memset(pad, 0, padSize);
00979                }
00980             }
00981             address=buffer;
00982          }
00983          else
00984             is.setstate(err);
00985       }
00986    }
00987    catch(bad_alloc&)
00988    {
00989       ios_base::iostate exception_mask = is.exceptions();
00990       is.exceptions(ios_base::goodbit);
00991       is.setstate(ios_base::badbit);
00992       is.exceptions(exception_mask);
00993       if(exception_mask & ios_base::badbit) throw;
00994    }
00995    catch(...)
00996    {
00997       ios_base::iostate exception_mask = is.exceptions();
00998       is.exceptions(ios_base::goodbit);
00999       is.setstate(ios_base::failbit);
01000       is.exceptions(exception_mask);
01001       if(exception_mask & ios_base::failbit) throw;
01002    }
01003 
01004    return is;
01005 }
01006 
01007 Fastcgipp::Http::Address::operator bool() const
01008 {
01009    static const unsigned char nullString[size] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 
01010    if(std::memcmp(m_data, nullString, size) == 0)
01011       return false;
01012    return true;
01013 }