]> granicus.if.org Git - pdns/commitdiff
YaHTTP: Sync with upstream changes
authorRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 15 May 2017 11:27:23 +0000 (13:27 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 15 May 2017 11:52:33 +0000 (13:52 +0200)
Backport changes from upstream up to c5b83288a4c2f8ec07cb8cb7bd150f2210db67b6
"Add missing `YaHTTP::isdigit()`, fix locale-enabled versions"

ext/yahttp/yahttp/cookie.hpp
ext/yahttp/yahttp/reqresp.cpp
ext/yahttp/yahttp/reqresp.hpp
ext/yahttp/yahttp/url.hpp
ext/yahttp/yahttp/utility.hpp
ext/yahttp/yahttp/yahttp.hpp

index dd5433580dbb4859e37446028e5abd215515d79f..c597c7c8114f8b7b9ed02741208c2d1e879ada2e 100644 (file)
@@ -6,16 +6,29 @@ namespace YaHTTP {
        secure = false;
        httponly = false;
        name = value = "";
+       expires = DateTime();
      }; //!< Set the cookie to empty value
 
      Cookie(const Cookie &rhs) {
+       name = rhs.name;
+       value = rhs.value;
        domain = rhs.domain;
        path = rhs.path;
        secure = rhs.secure;
        httponly = rhs.httponly;
+       expires = rhs.expires;
+     }; //<! Copy cookie values
+
+     Cookie& operator=(const Cookie &rhs) {
        name = rhs.name;
        value = rhs.value;
-     }; //<! Copy cookie values
+       domain = rhs.domain;
+       path = rhs.path;
+       secure = rhs.secure;
+       httponly = rhs.httponly;
+       expires = rhs.expires;
+       return *this;
+     }
 
      DateTime expires; /*!< Expiration date */
      std::string domain; /*!< Domain where cookie is valid */
@@ -29,6 +42,7 @@ namespace YaHTTP {
      std::string str() const {
        std::ostringstream oss;
        oss << YaHTTP::Utility::encodeURL(name) << "=" << YaHTTP::Utility::encodeURL(value);
+
        if (expires.isSet) 
          oss << "; expires=" << expires.cookie_str();
        if (domain.size()>0)
@@ -66,74 +80,64 @@ namespace YaHTTP {
     } //<! key value pair parser
   
     void parseCookieHeader(const std::string &cookiestr) {
+      size_t pos, npos;
       std::list<Cookie> lcookies;
-      int cstate = 0; //cookiestate
-      size_t pos,npos;
+      Cookie c;
       pos = 0;
-      cstate = 0;
       while(pos < cookiestr.size()) {
-        if (cookiestr.compare(pos, 7, "expires") ==0 ||
-            cookiestr.compare(pos, 6, "domain")  ==0 ||
-            cookiestr.compare(pos, 4, "path")    ==0) {
-          cstate = 1;
-          // get the date
-          std::string key, value, s;
-          npos = cookiestr.find("; ", pos);
-          if (npos == std::string::npos) {
-            // last value
-            s = std::string(cookiestr.begin() + pos + 1, cookiestr.end());
-            pos = cookiestr.size();
-          } else {
-            s = std::string(cookiestr.begin() + pos + 1, cookiestr.begin() + npos - 1);
-            pos = npos+2;
-          }
-          keyValuePair(s, key, value);
-          if (s == "expires") {
-            DateTime dt;
-            dt.parseCookie(value);
-            for(std::list<Cookie>::iterator i = lcookies.begin(); i != lcookies.end(); i++)
-              i->expires = dt;
-          } else if (s == "domain") {
-            for(std::list<Cookie>::iterator i = lcookies.begin(); i != lcookies.end(); i++)
-              i->domain = value;
-          } else if (s == "path") {
-            for(std::list<Cookie>::iterator i = lcookies.begin(); i != lcookies.end(); i++)
-              i->path = value;
-          }
-        } else if (cookiestr.compare(pos, 8, "httpOnly")==0) {
-          cstate = 1;
-          for(std::list<Cookie>::iterator i = lcookies.begin(); i != lcookies.end(); i++)
-            i->httponly = true;
-        } else if (cookiestr.compare(pos, 6, "secure")  ==0) {
-          cstate = 1;
-          for(std::list<Cookie>::iterator i = lcookies.begin(); i != lcookies.end(); i++)
-            i->secure = true;
-        } else if (cstate == 0) { // expect cookie
-          Cookie c;
-          std::string s;
-          npos = cookiestr.find("; ", pos);
-          if (npos == std::string::npos) {
-            // last value
-            s = std::string(cookiestr.begin() + pos, cookiestr.end());
-            pos = cookiestr.size();
-          } else {
-            s = std::string(cookiestr.begin() + pos, cookiestr.begin() + npos);
-            pos = npos+2;
-          }
-          keyValuePair(s, c.name, c.value);
-          c.name = YaHTTP::Utility::decodeURL(c.name);
-          c.value = YaHTTP::Utility::decodeURL(c.value);
-          lcookies.push_back(c);
-        } else if (cstate == 1) {
+        if ((npos = cookiestr.find("; ", pos)) == std::string::npos)
+          npos = cookiestr.size();
+        keyValuePair(cookiestr.substr(pos, npos-pos), c.name, c.value);
+        c.name = YaHTTP::Utility::decodeURL(c.name);
+        c.value = YaHTTP::Utility::decodeURL(c.value);
+        lcookies.push_back(c);
+        pos = npos+2;
+      }
+      for(std::list<Cookie>::iterator i = lcookies.begin(); i != lcookies.end(); i++) {
+        this->cookies[i->name] = *i;
+      }
+    }
+
+    void parseSetCookieHeader(const std::string &cookiestr) {
+      Cookie c;
+      size_t pos,npos;
+      std::string k, v;
+
+      if ((pos = cookiestr.find("; ", 0)) == std::string::npos)
+        pos = cookiestr.size();
+      keyValuePair(cookiestr.substr(0, pos), c.name, c.value);
+      c.name = YaHTTP::Utility::decodeURL(c.name);
+      c.value = YaHTTP::Utility::decodeURL(c.value);
+      if (pos < cookiestr.size()) pos+=2;
+
+      while(pos < cookiestr.size()) {
+        if ((npos = cookiestr.find("; ", pos)) == std::string::npos)
+          npos = cookiestr.size();
+        std::string s = cookiestr.substr(pos, npos-pos);
+        if (s.find("=") != std::string::npos)
+          keyValuePair(s, k, v);
+        else
+          k = s;
+        if (k == "expires") {
+          DateTime dt;
+          dt.parseCookie(v);
+          c.expires = dt;
+        } else if (k == "domain") {
+          c.domain = v;
+        } else if (k == "path") {
+          c.path = v;
+        } else if (k == "httpOnly") {
+          c.httponly = true;
+        } else if (k == "secure") {
+          c.secure = true;
+        } else {
           // ignore crap
           break;
         }
+        pos = npos+2;
       }
   
-      // store cookies
-      for(std::list<Cookie>::iterator i = lcookies.begin(); i != lcookies.end(); i++) {
-        this->cookies[i->name] = *i;
-      }
+      this->cookies[c.name] = c;
     }; //<! Parse multiple cookies from header 
   };
 };
index be0612a90f176101d88710c8e4ebd4d8ccf9d1ab..92fc6480a7e6a88bca2407b60510604467bc2661 100644 (file)
@@ -1,8 +1,41 @@
 #include "yahttp.hpp"
 
 namespace YaHTTP {
+
+  bool isspace(char c) {
+    return std::isspace(c) != 0;
+  }
+
+  bool isspace(char c, const std::locale& loc) {
+    return std::isspace(c, loc);
+  }
+
+  bool isxdigit(char c) {
+    return std::isxdigit(c) != 0;
+  }
+
+  bool isxdigit(char c, const std::locale& loc) {
+    return std::isxdigit(c, loc);
+  }
+
+  bool isdigit(char c) {
+    return std::isdigit(c) != 0;
+  }
+
+  bool isdigit(char c, const std::locale& loc) {
+    return std::isdigit(c, loc);
+  }
+
+  bool isalnum(char c) {
+    return std::isalnum(c) != 0;
+  }
+
+  bool isalnum(char c, const std::locale& loc) {
+    return std::isalnum(c, loc);
+  }
+
   template <class T>
-  int AsyncLoader<T>::feed(const std::string& somedata) {
+  bool AsyncLoader<T>::feed(const std::string& somedata) {
     buffer.append(somedata);
     while(state < 2) {
       int cr=0;
@@ -42,7 +75,7 @@ namespace YaHTTP {
           iss >> ver >> target->status;
           std::getline(iss, target->statusText);
           pos1=0;
-          while(pos1 < target->statusText.size() && ::isspace(target->statusText.at(pos1))) pos1++;
+          while(pos1 < target->statusText.size() && YaHTTP::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);
@@ -73,15 +106,16 @@ namespace YaHTTP {
         key = line.substr(0, pos1);
         value = line.substr(pos1+2);
         for(std::string::iterator it=key.begin(); it != key.end(); it++)
-          if (std::isspace(*it))
+          if (YaHTTP::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
 
-        if ((key == "set-cookie" && target->kind == YAHTTP_TYPE_RESPONSE) ||
-            (key == "cookie" && target->kind == YAHTTP_TYPE_REQUEST)) {
+        if (key == "set-cookie" && target->kind == YAHTTP_TYPE_RESPONSE) {
+          target->jar.parseSetCookieHeader(value);
+        } else if (key == "cookie" && target->kind == YAHTTP_TYPE_REQUEST) {
           target->jar.parseCookieHeader(value);
         } else {
           if (key == "host" && target->kind == YAHTTP_TYPE_REQUEST) {
@@ -136,7 +170,7 @@ namespace YaHTTP {
           buf[pos]=0; // just in case...
           buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer
           sscanf(buf, "%x", &chunk_size);
-          if (!chunk_size) { state = 3; break; } // last chunk
+          if (chunk_size == 0) { state = 3; break; } // last chunk
         } else {
           int crlf=1;
           if (buffer.size() < static_cast<size_t>(chunk_size+1)) return false; // expect newline
@@ -192,7 +226,7 @@ namespace YaHTTP {
     bool sendChunked = false;
 
     if (this->version > 10) { // 1.1 or better
-      if (headers.find("content-length") == headers.end()) {
+      if (headers.find("content-length") == headers.end() && !this->is_multipart) {
         // must use chunked on response
         sendChunked = (kind == YAHTTP_TYPE_RESPONSE);
         if ((headers.find("transfer-encoding") != headers.end() && headers.find("transfer-encoding")->second != "chunked")) {
@@ -210,21 +244,29 @@ namespace YaHTTP {
     // write headers
     strstr_map_t::const_iterator iter = headers.begin();
     while(iter != headers.end()) {
-      if (iter->first == "host" && kind != YAHTTP_TYPE_REQUEST) { iter++; continue; }
+      if (iter->first == "host" && (kind != YAHTTP_TYPE_REQUEST || version < 10)) { iter++; continue; }
       if (iter->first == "transfer-encoding" && sendChunked) { 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 (!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: ";
-        } else {
+    if (version > 9 && !cookieSent && jar.cookies.size() > 0) { // write cookies
+     if (kind == YAHTTP_TYPE_REQUEST) {
+        bool first = true;
+        os << "Cookie: ";
+        for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) {
+          if (first)
+            first = false;
+          else
+            os << "; ";
+          os << Utility::encodeURL(i->second.name) << "=" << Utility::encodeURL(i->second.value);
+        }
+     } else if (kind == YAHTTP_TYPE_REQUEST) {
+        for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) {
           os << "Set-Cookie: ";
+          os << i->second.str() << "\r\n";
         }
-        os << i->second.str() << "\r\n";
       }
     }
     os << "\r\n";
@@ -270,7 +312,7 @@ namespace YaHTTP {
     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
       }
index 5637e444213e1b26a9edb38398db65954e601455..2e2b609cb5da82c809f16629f4ea5a10c441b364 100644 (file)
@@ -79,10 +79,10 @@ namespace YaHTTP {
 #endif
         n = 0;
 
-        while(ifs && ifs.good()) {
+        while(ifs.good()) {
           ifs.read(buf, sizeof buf);
           n += (k = ifs.gcount());
-          if (k) {
+          if (k > 0) {
             if (chunked) os << std::hex << k << std::dec << "\r\n";
             os.write(buf, k);
             if (chunked) os << "\r\n"; 
@@ -118,6 +118,7 @@ namespace YaHTTP {
       body = "";
       routeName = "";
       version = 11; // default to version 1.1
+      is_multipart = false;
     }
 protected:
     HTTPBase(const HTTPBase& rhs) {
@@ -164,7 +165,7 @@ public:
 
     ssize_t max_request_size; //<! maximum size of request
     ssize_t max_response_size;  //<! maximum size of response
+    bool is_multipart; //<! if the request is multipart, prevents Content-Length header
 #ifdef HAVE_CPP_FUNC_PTR
     funcptr::function<size_t(const HTTPBase*,std::ostream&,bool)> renderer; //<! rendering function
 #endif
@@ -266,17 +267,21 @@ public:
         headers["content-type"] = "application/x-www-form-urlencoded; charset=utf-8";
       } else if (format == multipart) {
         headers["content-type"] = "multipart/form-data; boundary=YaHTTP-12ca543";
+        this->is_multipart = true;
         for(strstr_map_t::const_iterator i = POST().begin(); i != POST().end(); i++) {
-          postbuf << "--YaHTTP-12ca543\r\nContent-Disposition: form-data; name=\"" << Utility::encodeURL(i->first, false) << "; charset=UTF-8\r\n\r\n"
+          postbuf << "--YaHTTP-12ca543\r\nContent-Disposition: form-data; name=\"" << Utility::encodeURL(i->first, false) << "\"; charset=UTF-8\r\nContent-Length: " << i->second.size() << "\r\n\r\n"
             << Utility::encodeURL(i->second, false) << "\r\n";
         }
+        postbuf << "--";
+        body = postbuf.str();
       }
 
       postbuf.str("");
       postbuf << body.length();
       // set method and change headers
       method = "POST";
-      headers["content-length"] = postbuf.str();
+      if (!this->is_multipart)
+        headers["content-length"] = postbuf.str();
     }; //<! convert all postvars into string and stuff it into body
 
     friend std::ostream& operator<<(std::ostream& os, const Request &resp);
@@ -309,7 +314,7 @@ public:
       buffer = "";
       this->target->initialize();
     }; //<! Initialize the parser for target and clear state
-    int feed(const std::string& somedata); //<! Feed data to the parser
+    bool feed(const std::string& somedata); //<! Feed data to the parser
     bool ready() {
      return (chunked == true && state == 3) || // if it's chunked we get end of data indication
              (chunked == false && state > 1 &&  
index 8d46276791a1e501fde7baa928371c343d066ba0..e3225ea4f6b1409a5f1e089cdeeb41315d6051ea 100644 (file)
@@ -59,7 +59,7 @@ namespace YaHTTP {
              password = url.substr(pos2+1, pos1 - pos2 - 1);
              password = Utility::decodeURL(password);
           } else {
-             username = url.substr(pos+1, pos1 - pos);
+             username = url.substr(pos, pos1 - pos);
           }
           pos = pos1+1;
           username = Utility::decodeURL(username);
index 647b023fcdd966b917290b1759642605b89f0ff5..7d485359e0b5114a167d820c7b8951510bec520e 100644 (file)
@@ -5,6 +5,15 @@ 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
 
+  bool isspace(char c);
+  bool isspace(char c, const std::locale& loc);
+  bool isxdigit(char c);
+  bool isxdigit(char c, const std::locale& loc);
+  bool isdigit(char c);
+  bool isdigit(char c, const std::locale& loc);
+  bool isalnum(char c);
+  bool isalnum(char c, const std::locale& loc);
+
   /*! Case-Insensitive NULL safe comparator for string maps */
   struct ASCIICINullSafeComparator {
     bool operator() (const std::string& lhs, const std::string& rhs) const {
@@ -109,12 +118,12 @@ namespace YaHTTP {
      }; //<! parses date from struct tm 
 
      void validate() const {
-       if (wday < 0 || wday > 6) throw "Invalid date";
-       if (month < 1 || month > 12) throw "Invalid date";
-       if (year < 0) throw "Invalid date";
+       if (wday < 0 || wday > 6) throw std::range_error("Invalid date");
+       if (month < 1 || month > 12) throw std::range_error("Invalid date");
+       if (year < 0) throw std::range_error("Invalid date");
        if (hours < 0 || hours > 23 ||
            minutes < 0 || minutes > 59 ||
-           seconds < 0 || seconds > 60) throw "Invalid date";
+           seconds < 0 || seconds > 60) throw std::range_error("Invalid date");
      }; //<! make sure we are within ranges (not a *REAL* validation, just range check)
 
      std::string rfc_str() const {
@@ -153,32 +162,38 @@ namespace YaHTTP {
        if ( (ptr = strptime(rfc822_date.c_str(), "%a, %d %b %Y %T", &tm)) != NULL) {
           int sign;
          // parse the timezone parameter
-          while(*ptr && ::isspace(*ptr)) ptr++;
+          while(*ptr && YaHTTP::isspace(*ptr)) ptr++;
           if (*ptr == '+') sign = 0;
           else if (*ptr == '-') sign = -1;
-          else throw "Unparseable date";
+          else throw YaHTTP::ParseError("Unparseable date");
           ptr++;
           utc_offset = ::atoi(ptr) * sign;
-          while(*ptr && ::isdigit(*ptr)) ptr++;
+          while(*ptr != '\0' && YaHTTP::isdigit(*ptr)) ptr++;
 #endif
-          while(*ptr && ::isspace(*ptr)) ptr++;
-          if (*ptr) throw "Unparseable date"; // must be final.
+          while(*ptr != '\0' && YaHTTP::isspace(*ptr)) ptr++;
+          if (*ptr != '\0') throw YaHTTP::ParseError("Unparseable date"); // must be final.
           fromTm(&tm);
        } else {
-          throw "Unparseable date";
+          throw YaHTTP::ParseError("Unparseable date");
        }
      }; //<! parses RFC-822 date
 
      void parseCookie(const std::string &cookie_date) {
        struct tm tm;
        const char *ptr;
-       if ( (ptr = strptime(cookie_date.c_str(), "%d-%b-%Y %T", &tm)) != NULL) {
-          while(*ptr && ( ::isspace(*ptr) || ::isalnum(*ptr) )) ptr++;
-          if (*ptr) throw "Unparseable date (non-final)"; // must be final.
+       if ( (ptr = strptime(cookie_date.c_str(), "%d-%b-%Y %T", &tm)) != NULL
+#ifdef HAVE_TM_GMTOFF
+          || (ptr = strptime(cookie_date.c_str(), "%d-%b-%Y %T %z", &tm)) != NULL
+          || (ptr = strptime(cookie_date.c_str(), "%a, %d-%b-%Y %T %Z", &tm)) != NULL
+#endif
+          ) {
+          while(*ptr != '\0' && ( YaHTTP::isspace(*ptr) || YaHTTP::isalnum(*ptr) )) ptr++;
+          if (*ptr != '\0') throw YaHTTP::ParseError("Unparseable date (non-final)"); // must be final.
           fromTm(&tm);
           this->utc_offset = 0;
        } else {
-          throw "Unparseable date (did not match pattern cookie)";
+          std::cout << cookie_date << std::endl;
+          throw YaHTTP::ParseError("Unparseable date (did not match pattern cookie)");
        }
      }; //<! parses HTTP Cookie date
 
@@ -236,7 +251,7 @@ namespace YaHTTP {
       char repl[3];
       size_t pos;
       for(std::string::iterator iter = result.begin(); iter != result.end(); iter++) {
-        if (!std::isalnum(*iter) && (!asUrl || skip.find(*iter) == std::string::npos)) {
+        if (!YaHTTP::isalnum(*iter) && (!asUrl || skip.find(*iter) == std::string::npos)) {
           // replace with different thing
           pos = std::distance(result.begin(), iter);
           ::snprintf(repl,3,"%02x", static_cast<unsigned char>(*iter));
@@ -255,7 +270,7 @@ namespace YaHTTP {
       std::ostringstream result;
       std::string skip = "+-.,&;_#%[]?/@(){}=";
       for(std::vector<unsigned char>::iterator iter = vec.begin(); iter != vec.end(); iter++) {
-        if (!std::isalnum((char)*iter) && (!asUrl || skip.find((char)*iter) == std::string::npos)) {
+        if (!YaHTTP::isalnum((char)*iter) && (!asUrl || skip.find((char)*iter) == std::string::npos)) {
           // bit more complex replace
           result << "%" << std::hex << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(*iter);
         } else result << (char)*iter;
@@ -409,14 +424,14 @@ namespace YaHTTP {
     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++;
+       while(iter != str.end() && YaHTTP::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();
-       while(iter != str.rend() && std::isspace(*iter, loc)) iter++;
+       while(iter != str.rend() && YaHTTP::isspace(*iter, loc)) iter++;
        str.erase(iter.base(), str.end());
     }; //<! removes whitespace from right
 
index d933986457f916e529ff8c179288acb960c6d682..d60be7a9fa14ee43beff4746ecd0b08527a2e950 100644 (file)
@@ -4,15 +4,16 @@
 #include <algorithm>
 #include <string>
 #include <cstdio>
+#include <stdexcept>
 #include <sys/time.h>
 #include <iomanip>
 #include <list>
 #include <vector>
 
 #include "yahttp-config.h"
+#include "exception.hpp"
 #include "url.hpp"
 #include "utility.hpp"
-#include "exception.hpp"
 #include "url.hpp"
 #include "cookie.hpp"
 #include "reqresp.hpp"