]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Update to v0.1.5 2108/head
authorAki Tuomi <cmouse@desteem.org>
Wed, 21 Jan 2015 11:18:39 +0000 (13:18 +0200)
committerAki Tuomi <cmouse@desteem.org>
Wed, 21 Jan 2015 11:18:39 +0000 (13:18 +0200)
pdns/ext/yahttp/yahttp/cookie.hpp
pdns/ext/yahttp/yahttp/reqresp.cpp
pdns/ext/yahttp/yahttp/reqresp.hpp
pdns/ext/yahttp/yahttp/router.cpp
pdns/ext/yahttp/yahttp/utility.hpp

index 4b285aa760c4459fe2edbebe5d50d0ef28f274c8..b99b21e0104136644c643ccf9fb4c5b9640e3e92 100644 (file)
@@ -46,13 +46,17 @@ namespace YaHTTP {
   /*! Implements a Cookie jar for storing multiple cookies */
   class CookieJar {
     public:
-    std::map<std::string, Cookie> cookies;  //<! cookie container
+    std::map<std::string, Cookie, ASCIICINullSafeComparator> cookies;  //<! cookie container
   
     CookieJar() {}; //<! constructs empty cookie jar
     CookieJar(const CookieJar & rhs) {
       this->cookies = rhs.cookies;
     } //<! copy cookies from another cookie jar
   
+    void clear() {
+      this->cookies.clear();
+    }
+
     void keyValuePair(const std::string &keyvalue, std::string &key, std::string &value) {
       size_t pos;
       pos = keyvalue.find("=");
index 14196b998dcf30e710590a42e3887e272e105304..07a408a349cf987458c9178ff9298d14d2aa6762 100644 (file)
@@ -6,9 +6,10 @@ namespace YaHTTP {
     buffer.append(somedata);
     while(state < 2) {
       int cr=0;
+      pos = buffer.find_first_of("\n");
       // need to find CRLF in buffer
-      if ((pos = buffer.find_first_of("\n")) == std::string::npos) return false;
-      if (buffer[pos-1]=='\r')
+      if (pos == std::string::npos) return false;
+      if (pos>0 && buffer[pos-1]=='\r')
         cr=1;
       std::string line(buffer.begin(), buffer.begin()+pos-cr); // exclude CRLF
       buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer including CRLF
@@ -19,8 +20,16 @@ namespace YaHTTP {
           std::string tmpurl;
           std::istringstream iss(line);
           iss >> target->method >> tmpurl >> ver;
-          if (ver.find("HTTP/1.") != 0)
-            throw ParseError("Not a HTTP 1.x request");
+          if (ver.size() == 0)
+            target->version = 9;
+          else if (ver.find("HTTP/0.9") == 0)
+            target->version = 9;
+          else if (ver.find("HTTP/1.0") == 0)
+            target->version = 10;
+          else if (ver.find("HTTP/1.1") == 0)
+            target->version = 11;
+          else
+            throw ParseError("HTTP version not supported");
           // uppercase the target method
           std::transform(target->method.begin(), target->method.end(), target->method.begin(), ::toupper);
           target->url.parse(tmpurl);
@@ -29,9 +38,25 @@ namespace YaHTTP {
         } else if(target->kind == YAHTTP_TYPE_RESPONSE) {
           std::string ver;
           std::istringstream iss(line);
-          iss >> ver >> target->status >> target->statusText;
-          if (ver.find("HTTP/1.") != 0)
-            throw ParseError("Not a HTTP 1.x response");
+          std::string::size_type pos1;
+          iss >> ver >> target->status;
+          std::getline(iss, target->statusText);
+          pos1=0;
+          while(pos1 < target->statusText.size() && ::isspace(target->statusText.at(pos1))) pos1++;
+          target->statusText = target->statusText.substr(pos1); 
+          if ((pos1 = target->statusText.find("\r")) != std::string::npos) {
+            target->statusText = target->statusText.substr(0, pos1-1);
+          }
+          if (ver.size() == 0) {
+            target->version = 9;
+          } else if (ver.find("HTTP/0.9") == 0)
+            target->version = 9;
+          else if (ver.find("HTTP/1.0") == 0)
+            target->version = 10;
+          else if (ver.find("HTTP/1.1") == 0)
+            target->version = 11;
+          else
+            throw ParseError("HTTP version not supported");
           state = 1;
         }
       } else if (state == 1) {
@@ -43,11 +68,15 @@ namespace YaHTTP {
           break;
         }
         // split headers
-        if ((pos = line.find_first_of(": ")) == std::string::npos)
+        if ((pos = line.find(": ")) == std::string::npos)
           throw ParseError("Malformed header line");
         key = line.substr(0, pos);
         value = line.substr(pos+2);
-        Utility::trimRight(value);
+        for(std::string::iterator it=key.begin(); it != key.end(); it++)
+          if (std::isspace(*it))
+            throw ParseError("Header key contains whitespace which is not allowed by RFC");
+
+        Utility::trim(value);
         std::transform(key.begin(), key.end(), key.begin(), ::tolower);
         // is it already defined
 
@@ -56,7 +85,7 @@ namespace YaHTTP {
           target->jar.parseCookieHeader(value);
         } else {
           if (key == "host" && target->kind == YAHTTP_TYPE_REQUEST) {
-            // maybe it contains port? 
+            // maybe it contains port?
             if ((pos = value.find(":")) == std::string::npos) {
               target->url.host = value;
             } else {
@@ -76,9 +105,9 @@ namespace YaHTTP {
     minbody = 0;
     // check for expected body size
     if (target->kind == YAHTTP_TYPE_REQUEST) maxbody = target->max_request_size;
-    else if (target->kind == YAHTTP_TYPE_RESPONSE) maxbody = target->max_response_size; 
+    else if (target->kind == YAHTTP_TYPE_RESPONSE) maxbody = target->max_response_size;
     else maxbody = 0;
-   
+
     if (!chunked) {
       if (target->headers.find("content-length") != target->headers.end()) {
         std::istringstream maxbodyS(target->headers["content-length"]);
@@ -114,7 +143,7 @@ namespace YaHTTP {
           if (buffer.at(chunk_size) == '\r') {
             if (buffer.size() < static_cast<size_t>(chunk_size+2) || buffer.at(chunk_size+1) != '\n') return false; // expect newline after carriage return
             crlf=2;
-          } else if (buffer.at(chunk_size) != '\n') return false; 
+          } else if (buffer.at(chunk_size) != '\n') return false;
           std::string tmp = buffer.substr(0, chunk_size);
           buffer.erase(buffer.begin(), buffer.begin()+chunk_size+crlf);
           bodybuf << tmp;
@@ -122,9 +151,9 @@ namespace YaHTTP {
           if (buffer.size() == 0) break; // just in case
         }
       } else {
-        if (bodybuf.str().length() + buffer.length() > maxbody) 
+        if (bodybuf.str().length() + buffer.length() > maxbody)
           bodybuf << buffer.substr(0, maxbody - bodybuf.str().length());
-        else 
+        else
           bodybuf << buffer;
         buffer = "";
       }
@@ -134,37 +163,65 @@ namespace YaHTTP {
 
     return ready();
   };
-  
+
   void HTTPBase::write(std::ostream& os) const {
     if (kind == YAHTTP_TYPE_REQUEST) {
       std::ostringstream getparmbuf;
       std::string getparms;
-      // prepare URL 
+      // prepare URL
       for(strstr_map_t::const_iterator i = getvars.begin(); i != getvars.end(); i++) {
         getparmbuf << Utility::encodeURL(i->first, false) << "=" << Utility::encodeURL(i->second, false) << "&";
       }
-      if (getparmbuf.str().length() > 0)  
+      if (getparmbuf.str().length() > 0)
         getparms = "?" + std::string(getparmbuf.str().begin(), getparmbuf.str().end() - 1);
       else
         getparms = "";
-      os << method << " " << url.path << getparms << " HTTP/1.1";
+      os << method << " " << url.path << getparms << " HTTP/" << versionStr(this->version);
     } else if (kind == YAHTTP_TYPE_RESPONSE) {
-      os << "HTTP/1.1 " << status << " ";
+      os << "HTTP/" << versionStr(this->version) << " " << status << " ";
       if (statusText.empty())
         os << Utility::status2text(status);
       else
         os << statusText;
     }
     os << "\r\n";
-  
+
+    bool cookieSent = false;
+    bool sendChunked = false;
+
+    if (this->version > 10) { // 1.1 or better
+      if (headers.find("content-length") == headers.end()) {
+        // must use chunked on response
+        sendChunked = (kind == YAHTTP_TYPE_RESPONSE);
+        if ((headers.find("transfer-encoding") != headers.end() && headers.find("transfer-encoding")->second != "chunked")) {
+          throw YaHTTP::Error("Transfer-encoding must be chunked, or Content-Length defined");
+        }
+        if ((headers.find("transfer-encoding") == headers.end() && kind == YAHTTP_TYPE_RESPONSE)) {
+          sendChunked = true;
+          // write the header now
+          os << "Transfer-Encoding: chunked" << "\r\n";
+        }
+      } else {
+        if ((headers.find("transfer-encoding") == headers.end() && kind == YAHTTP_TYPE_RESPONSE)) {
+          sendChunked = true;
+          // write the header now
+          os << "Transfer-Encoding: chunked" << "\r\n";
+        } else if (headers.find("transfer-encoding") != headers.end() && headers.find("transfer-encoding")->second == "chunked") {
+          sendChunked = true;
+        }
+      }
+    }
+
     // write headers
     strstr_map_t::const_iterator iter = headers.begin();
     while(iter != headers.end()) {
       if (iter->first == "host" && kind != YAHTTP_TYPE_REQUEST) { iter++; continue; }
+      std::string header = Utility::camelizeHeader(iter->first);
+      if (header == "Cookie" || header == "Set-Cookie") cookieSent = true;
       os << Utility::camelizeHeader(iter->first) << ": " << iter->second << "\r\n";
       iter++;
     }
-    if (jar.cookies.size() > 0) { // write cookies
+    if (!cookieSent && jar.cookies.size() > 0) { // write cookies
       for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) {
         if (kind == YAHTTP_TYPE_REQUEST) {
           os << "Cookie: ";
@@ -176,40 +233,41 @@ namespace YaHTTP {
     }
     os << "\r\n";
 #ifdef HAVE_CPP_FUNC_PTR
-    this->renderer(this, os);
+    this->renderer(this, os, sendChunked);
 #else
-    os << body;
+    SendbodyRenderer r;
+    r(this, os, chunked)
 #endif
   };
-  
+
   std::ostream& operator<<(std::ostream& os, const Response &resp) {
     resp.write(os);
     return os;
   };
-  
+
   std::istream& operator>>(std::istream& is, Response &resp) {
     YaHTTP::AsyncResponseLoader arl;
     arl.initialize(&resp);
     while(is.good()) {
       char buf[1024];
       is.read(buf, 1024);
-      if (is.gcount()) { // did we actually read anything
+      if (is.gcount()>0) { // did we actually read anything
         is.clear();
         if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
       }
     }
     // throw unless ready
-    if (arl.ready() == false) 
+    if (arl.ready() == false)
       throw ParseError("Was not able to extract a valid Response from stream");
     arl.finalize();
     return is;
   };
-  
+
   std::ostream& operator<<(std::ostream& os, const Request &req) {
     req.write(os);
     return os;
   };
-  
+
   std::istream& operator>>(std::istream& is, Request &req) {
     YaHTTP::AsyncRequestLoader arl;
     arl.initialize(&req);
index 29718806b76a46d3e08e78ab3253d8d7eb749fb2..8916c8e71080f38d809b0e88dc3a07d58945aeea 100644 (file)
@@ -18,6 +18,8 @@ namespace funcptr = boost;
 #include <unistd.h>
 #endif
 
+#include <algorithm>
+
 #ifndef YAHTTP_MAX_REQUEST_SIZE
 #define YAHTTP_MAX_REQUEST_SIZE 2097152
 #endif
@@ -30,8 +32,7 @@ namespace funcptr = boost;
 #define YAHTTP_TYPE_RESPONSE 2
 
 namespace YaHTTP {
-  typedef std::map<std::string,std::string> strstr_map_t; //<! String to String map 
-  typedef std::map<std::string,Cookie> strcookie_map_t; //<! String to Cookie map
+  typedef std::map<std::string,Cookie,ASCIICINullSafeComparator> strcookie_map_t; //<! String to Cookie map
 
   typedef enum {
     urlencoded,
@@ -41,14 +42,23 @@ namespace YaHTTP {
   /*! Base class for request and response */
   class HTTPBase {
   public:
-#ifdef HAVE_CPP_FUNC_PTR
     /*! Default renderer for request/response, simply copies body to response */
     class SendBodyRender {
     public:
       SendBodyRender() {};
 
-      size_t operator()(const HTTPBase *doc, std::ostream& os) const {
-        os << doc->body;
+      size_t operator()(const HTTPBase *doc, std::ostream& os, bool chunked) const {
+        if (chunked) {
+          std::string::size_type i,cl;
+          for(i=0;i<doc->body.length();i+=1024) {
+            cl = std::min(static_cast<std::string::size_type>(1024), doc->body.length()-i); // for less than 1k blocks
+            os << std::hex << cl << std::dec << "\r\n";
+            os << doc->body.substr(i, cl) << "\r\n";
+          }
+          os << 0 << "\r\n\r\n"; // last chunk
+        } else {
+          os << doc->body;
+        }
         return doc->body.length();
       }; //<! writes body to ostream and returns length 
     };
@@ -59,7 +69,7 @@ namespace YaHTTP {
         this->path = path;
       };
   
-      size_t operator()(const HTTPBase *doc __attribute__((unused)), std::ostream& os) const {
+      size_t operator()(const HTTPBase *doc __attribute__((unused)), std::ostream& os, bool chunked) const {
         char buf[4096];
         size_t n,k;
 #ifdef HAVE_CXX11
@@ -68,20 +78,28 @@ namespace YaHTTP {
         std::ifstream ifs(path.c_str(), std::ifstream::binary);
 #endif
         n = 0;
+
         while(ifs && ifs.good()) {
           ifs.read(buf, sizeof buf);
           n += (k = ifs.gcount());
-          if (k)
+          if (k) {
+            if (chunked) os << std::hex << k << std::dec << "\r\n";
             os.write(buf, k);
+            if (chunked) os << "\r\n"; 
+          }
         }
-
+        if (chunked) os << 0 << "\r\n\r\n";
         return n;
       }; //<! writes file to ostream and returns length
 
       std::string path; //<! File to send
     };
-#endif
+
     HTTPBase() {
+      initialize();
+    };
+
+    virtual void initialize() {
       kind = 0;
       status = 0;
 #ifdef HAVE_CPP_FUNC_PTR
@@ -89,7 +107,18 @@ namespace YaHTTP {
 #endif
       max_request_size = YAHTTP_MAX_REQUEST_SIZE;
       max_response_size = YAHTTP_MAX_RESPONSE_SIZE;
-    };
+      url = "";
+      method = "";
+      statusText = "";
+      jar.clear();
+      headers.clear();
+      parameters.clear();
+      getvars.clear();
+      postvars.clear();
+      body = "";
+      routeName = "";
+      version = 11; // default to version 1.1
+    }
 protected:
     HTTPBase(const HTTPBase& rhs) {
       this->url = rhs.url; this->kind = rhs.kind;
@@ -98,7 +127,7 @@ protected:
       this->jar = rhs.jar; this->postvars = rhs.postvars;
       this->parameters = rhs.parameters; this->getvars = rhs.getvars;
       this->body = rhs.body; this->max_request_size = rhs.max_request_size;
-      this->max_response_size = rhs.max_response_size;
+      this->max_response_size = rhs.max_response_size; this->version = rhs.version;
 #ifdef HAVE_CPP_FUNC_PTR
       this->renderer = rhs.renderer;
 #endif
@@ -110,7 +139,7 @@ protected:
       this->jar = rhs.jar; this->postvars = rhs.postvars;
       this->parameters = rhs.parameters; this->getvars = rhs.getvars;
       this->body = rhs.body; this->max_request_size = rhs.max_request_size;
-      this->max_response_size = rhs.max_response_size;
+      this->max_response_size = rhs.max_response_size; this->version = rhs.version;
 #ifdef HAVE_CPP_FUNC_PTR
       this->renderer = rhs.renderer;
 #endif
@@ -120,6 +149,7 @@ public:
     URL url; //<! URL of this request/response
     int kind; //<! Type of object (1 = request, 2 = response)
     int status; //<! status code 
+    int version; //<! http version 9 = 0.9, 10 = 1.0, 11 = 1.1
     std::string statusText; //<! textual representation of status code
     std::string method; //<! http verb
     strstr_map_t headers; //<! map of header(s)
@@ -136,7 +166,7 @@ public:
     ssize_t max_response_size;  //<! maximum size of response
  
 #ifdef HAVE_CPP_FUNC_PTR
-    funcptr::function<size_t(const HTTPBase*,std::ostream&)> renderer; //<! rendering function
+    funcptr::function<size_t(const HTTPBase*,std::ostream&,bool)> renderer; //<! rendering function
 #endif
     void write(std::ostream& os) const; //<! writes request to the given output stream
 
@@ -144,6 +174,15 @@ public:
     strstr_map_t& POST() { return postvars; }; //<! accessor for postvars
     strcookie_map_t& COOKIES() { return jar.cookies; }; //<! accessor for cookies
 
+    std::string versionStr(int version) const {
+      switch(version) {
+      case  9: return "0.9";
+      case 10: return "1.0";
+      case 11: return "1.1";
+      default: throw YaHTTP::Error("Unsupported version");
+      }
+    };
+
     std::string str() const {
        std::ostringstream oss;
        write(oss);
@@ -154,7 +193,7 @@ public:
   /*! Response class, represents a HTTP Response document */
   class Response: public HTTPBase { 
   public:
-    Response() { this->kind = YAHTTP_TYPE_RESPONSE; };
+    Response() { initialize(); };
     Response(const HTTPBase& rhs): HTTPBase(rhs) {
       this->kind = YAHTTP_TYPE_RESPONSE;
     };
@@ -163,6 +202,10 @@ public:
       this->kind = YAHTTP_TYPE_RESPONSE;
       return *this;
     };
+    void initialize() {
+      HTTPBase::initialize();
+      this->kind = YAHTTP_TYPE_RESPONSE;
+    }
     friend std::ostream& operator<<(std::ostream& os, const Response &resp);
     friend std::istream& operator>>(std::istream& is, Response &resp);
   };
@@ -170,7 +213,7 @@ public:
   /* Request class, represents a HTTP Request document */
   class Request: public HTTPBase {
   public:
-    Request() { this->kind = YAHTTP_TYPE_REQUEST; };
+    Request() { initialize(); };
     Request(const HTTPBase& rhs): HTTPBase(rhs) {
       this->kind = YAHTTP_TYPE_REQUEST;
     };
@@ -179,7 +222,10 @@ public:
       this->kind = YAHTTP_TYPE_REQUEST;
       return *this;
     };
-
+    void initialize() {
+      HTTPBase::initialize();
+      this->kind = YAHTTP_TYPE_REQUEST;
+    }
     void setup(const std::string& method, const std::string& url) {
       this->url.parse(url);
       this->headers["host"] = this->url.host;
@@ -242,6 +288,8 @@ public:
       bodybuf.str(""); maxbody = 0;
       pos = 0; state = 0; this->target = target; 
       hasBody = false;
+      buffer = "";
+      this->target->initialize();
     }; //<! Initialize the parser for target and clear state
     int feed(const std::string& somedata); //<! Feed data to the parser
     bool ready() {
index 26cd774f5170a34c8a6c581762578ff694a3ac8d..3678000eaea84edd1f103b52c840d18f1f2fcb04 100644 (file)
@@ -155,7 +155,6 @@ namespace YaHTTP {
         continue;
       }
     }
-    std::cout << mask.substr(k3) << std::endl;
     path << mask.substr(k3);
     result = path.str();
     return std::make_pair(method, result);
index c6be7c6333c638e420b0c01088d4323dad80b816..3b0a8d1d9ad19cd51619ee0de05d8bd55e8854d9 100644 (file)
@@ -5,6 +5,22 @@ namespace YaHTTP {
   static const char *MONTHS[] = {0,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",0}; //<! List of months 
   static const char *DAYS[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat",0}; //<! List of days
 
+  /*! Case-Insensitive NULL safe comparator for string maps */
+  struct ASCIICINullSafeComparator {
+    bool operator() (const std::string& lhs, const std::string& rhs) const {
+      char v;
+      std::string::const_iterator lhi = lhs.begin();
+      std::string::const_iterator rhi = rhs.begin();
+      for(;lhi != lhs.end() && rhi != rhs.end(); lhi++, rhi++)
+        if ((v = ::tolower(*lhi) - ::tolower(*rhi)) != 0) return v<0; 
+      if (lhi == lhs.end() && rhi != rhs.end()) return true;
+      if (lhi != lhs.end() && rhi == rhs.end()) return false;
+      return false; // they are equal
+    }
+  };
+
+  typedef std::map<std::string,std::string,ASCIICINullSafeComparator> strstr_map_t; //<! String to String map
+
   /*! Represents a date/time with utc offset */
   class DateTime {
   public:
@@ -158,7 +174,6 @@ namespace YaHTTP {
        const char *ptr;
        if ( (ptr = strptime(cookie_date.c_str(), "%d-%b-%Y %T", &tm)) != NULL) {
           while(*ptr && ( ::isspace(*ptr) || ::isalnum(*ptr) )) ptr++;
-          std::cerr << ptr << std::endl;
           if (*ptr) throw "Unparseable date (non-final)"; // must be final.
           fromTm(&tm);
           this->utc_offset = 0;
@@ -331,9 +346,9 @@ namespace YaHTTP {
        }
     }; //<! static HTTP codes to text mappings
 
-    static std::map<std::string,std::string> parseUrlParameters(std::string parameters) {
+    static strstr_map_t parseUrlParameters(std::string parameters) {
       std::string::size_type pos = 0;
-      std::map<std::string,std::string> parameter_map;
+      strstr_map_t parameter_map;
       while (pos != std::string::npos) {
         // find next parameter start
         std::string::size_type nextpos = parameters.find("&", pos);
@@ -389,6 +404,13 @@ namespace YaHTTP {
       return iequals(a,b,a.size());
     }; //<! case-insensitive comparison
 
+    static void trimLeft(std::string &str) {
+       const std::locale &loc = std::locale::classic();
+       std::string::iterator iter = str.begin();
+       while(iter != str.end() && std::isspace(*iter, loc)) iter++;
+       str.erase(str.begin(), iter);
+    }; //<! removes whitespace from left
+
     static void trimRight(std::string &str) {
        const std::locale &loc = std::locale::classic();
        std::string::reverse_iterator iter = str.rbegin();
@@ -396,6 +418,11 @@ namespace YaHTTP {
        str.erase(iter.base(), str.end());
     }; //<! removes whitespace from right
 
+    static void trim(std::string &str) {
+       trimLeft(str);
+       trimRight(str);
+    }; //<! removes whitespace from left and right
+
     static std::string camelizeHeader(const std::string &str) {
        std::string::const_iterator iter = str.begin();
        std::string result;