You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2019/10/25 13:27:13 UTC

svn commit: r1868930 [2/5] - in /httpd/httpd/branches/2.4.x: ./ build/ docs/manual/mod/ modules/md/

Modified: httpd/httpd/branches/2.4.x/modules/md/md_crypt.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_crypt.c?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_crypt.c (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_crypt.c Fri Oct 25 13:27:12 2019
@@ -155,7 +155,7 @@ apr_status_t md_crypt_init(apr_pool_t *p
 
 static apr_status_t fwrite_buffer(void *baton, apr_file_t *f, apr_pool_t *p) 
 {
-    md_data *buf = baton;
+    md_data_t *buf = baton;
     apr_size_t wlen;
     
     (void)p;
@@ -253,6 +253,11 @@ static apr_time_t md_asn1_time_get(const
 #endif
 }
 
+apr_time_t md_asn1_generalized_time_get(void *ASN1_GENERALIZEDTIME)
+{
+    return md_asn1_time_get(ASN1_GENERALIZEDTIME);
+}
+
 
 /**************************************************************************************************/
 /* private keys */
@@ -384,7 +389,7 @@ apr_status_t md_pkey_fload(md_pkey_t **p
     return rv;
 }
 
-static apr_status_t pkey_to_buffer(md_data *buf, md_pkey_t *pkey, apr_pool_t *p,
+static apr_status_t pkey_to_buffer(md_data_t *buf, md_pkey_t *pkey, apr_pool_t *p,
                                    const char *pass, apr_size_t pass_len)
 {
     BIO *bio = BIO_new(BIO_s_mem());
@@ -435,7 +440,7 @@ apr_status_t md_pkey_fsave(md_pkey_t *pk
                            const char *pass_phrase, apr_size_t pass_len,
                            const char *fname, apr_fileperms_t perms)
 {
-    md_data buffer;
+    md_data_t buffer;
     apr_status_t rv;
     
     if (APR_SUCCESS == (rv = pkey_to_buffer(&buffer, pkey, p, pass_phrase, pass_len))) {
@@ -507,12 +512,14 @@ static void RSA_get0_key(const RSA *r,
 static const char *bn64(const BIGNUM *b, apr_pool_t *p) 
 {
     if (b) {
-         apr_size_t len = (apr_size_t)BN_num_bytes(b);
-         char *buffer = apr_pcalloc(p, len);
-         if (buffer) {
-            BN_bn2bin(b, (unsigned char *)buffer);
-            return md_util_base64url_encode(buffer, len, p);
-         }
+        md_data_t buffer;
+
+        buffer.len = (apr_size_t)BN_num_bytes(b);
+        buffer.data = apr_pcalloc(p, buffer.len);
+        if (buffer.data) {
+            BN_bn2bin(b, (unsigned char *)buffer.data);
+            return md_util_base64url_encode(&buffer, p);
+        }
     }
     return NULL;
 }
@@ -545,21 +552,23 @@ apr_status_t md_crypt_sign64(const char
                              const char *d, size_t dlen)
 {
     EVP_MD_CTX *ctx = NULL;
-    char *buffer;
+    md_data_t buffer;
     unsigned int blen;
     const char *sign64 = NULL;
     apr_status_t rv = APR_ENOMEM;
     
-    buffer = apr_pcalloc(p, (apr_size_t)EVP_PKEY_size(pkey->pkey));
-    if (buffer) {
+    buffer.len = (apr_size_t)EVP_PKEY_size(pkey->pkey);
+    buffer.data = apr_pcalloc(p, buffer.len);
+    if (buffer.data) {
         ctx = EVP_MD_CTX_create();
         if (ctx) {
             rv = APR_ENOTIMPL;
             if (EVP_SignInit_ex(ctx, EVP_sha256(), NULL)) {
                 rv = APR_EGENERAL;
                 if (EVP_SignUpdate(ctx, d, dlen)) {
-                    if (EVP_SignFinal(ctx, (unsigned char*)buffer, &blen, pkey->pkey)) {
-                        sign64 = md_util_base64url_encode(buffer, blen, p);
+                    if (EVP_SignFinal(ctx, (unsigned char*)buffer.data, &blen, pkey->pkey)) {
+                        buffer.len = blen;
+                        sign64 = md_util_base64url_encode(&buffer, p);
                         if (sign64) {
                             rv = APR_SUCCESS;
                         }
@@ -581,10 +590,10 @@ apr_status_t md_crypt_sign64(const char
     return rv;
 }
 
-static apr_status_t sha256_digest(md_data **pdigest, apr_pool_t *p, const md_data *buf)
+static apr_status_t sha256_digest(md_data_t **pdigest, apr_pool_t *p, const md_data_t *buf)
 {
     EVP_MD_CTX *ctx = NULL;
-    md_data *digest;
+    md_data_t *digest;
     apr_status_t rv = APR_ENOMEM;
     unsigned int dlen;
 
@@ -614,14 +623,14 @@ leave:
     return rv;
 }
 
-apr_status_t md_crypt_sha256_digest64(const char **pdigest64, apr_pool_t *p, const md_data *d)
+apr_status_t md_crypt_sha256_digest64(const char **pdigest64, apr_pool_t *p, const md_data_t *d)
 {
     const char *digest64 = NULL;
-    md_data *digest;
+    md_data_t *digest;
     apr_status_t rv;
     
     if (APR_SUCCESS == (rv = sha256_digest(&digest, p, d))) {
-        if (NULL == (digest64 = md_util_base64url_encode(digest->data, digest->len, p))) {
+        if (NULL == (digest64 = md_util_base64url_encode(digest, p))) {
             rv = APR_EGENERAL;
         }
     }
@@ -630,9 +639,9 @@ apr_status_t md_crypt_sha256_digest64(co
 }
 
 apr_status_t md_crypt_sha256_digest_hex(const char **pdigesthex, apr_pool_t *p, 
-                                        const md_data *data)
+                                        const md_data_t *data)
 {
-    md_data *digest;
+    md_data_t *digest;
     apr_status_t rv;
     
     if (APR_SUCCESS == (rv = sha256_digest(&digest, p, data))) {
@@ -661,19 +670,19 @@ static apr_status_t cert_cleanup(void *d
     return APR_SUCCESS;
 }
 
-static md_cert_t *make_cert(apr_pool_t *p, X509 *x509) 
+md_cert_t *md_cert_wrap(apr_pool_t *p, void *x509) 
 {
     md_cert_t *cert = apr_pcalloc(p, sizeof(*cert));
     cert->pool = p;
     cert->x509 = x509;
-    apr_pool_cleanup_register(p, cert, cert_cleanup, apr_pool_cleanup_null);
-    
     return cert;
 }
 
-void md_cert_free(md_cert_t *cert)
+md_cert_t *md_cert_make(apr_pool_t *p, void *x509) 
 {
-    cert_cleanup(cert);
+    md_cert_t *cert = md_cert_wrap(p, x509);
+    apr_pool_cleanup_register(p, cert, cert_cleanup, apr_pool_cleanup_null);
+    return cert;
 }
 
 void *md_cert_get_X509(const md_cert_t *cert)
@@ -684,16 +693,15 @@ void *md_cert_get_X509(const md_cert_t *
 const char *md_cert_get_serial_number(const md_cert_t *cert, apr_pool_t *p)
 {
     const char *s = "";
+    BIGNUM *bn; 
+    const char *serial;
     const ASN1_INTEGER *ai = X509_get_serialNumber(cert->x509);
     if (ai) {
-        BIGNUM *bn; 
-        const char *hex;
-        
         bn = ASN1_INTEGER_to_BN(ai, NULL);
-        hex = BN_bn2hex(bn);
-        s = apr_pstrdup(p, hex);
+        serial = BN_bn2hex(bn);
+        s = apr_pstrdup(p, serial);
+        OPENSSL_free((void*)serial);
         OPENSSL_free((void*)bn);
-        OPENSSL_free((void*)hex);
     }
     return s;
 }
@@ -831,7 +839,7 @@ apr_status_t md_cert_fload(md_cert_t **p
         x509 = PEM_read_X509(f, NULL, NULL, NULL);
         rv = fclose(f);
         if (x509 != NULL) {
-            cert =  make_cert(p, x509);
+            cert =  md_cert_make(p, x509);
         }
         else {
             rv = APR_EINVAL;
@@ -842,7 +850,7 @@ apr_status_t md_cert_fload(md_cert_t **p
     return rv;
 }
 
-static apr_status_t cert_to_buffer(md_data *buffer, const md_cert_t *cert, apr_pool_t *p)
+static apr_status_t cert_to_buffer(md_data_t *buffer, const md_cert_t *cert, apr_pool_t *p)
 {
     BIO *bio = BIO_new(BIO_s_mem());
     int i;
@@ -871,7 +879,7 @@ static apr_status_t cert_to_buffer(md_da
 apr_status_t md_cert_fsave(md_cert_t *cert, apr_pool_t *p, 
                            const char *fname, apr_fileperms_t perms)
 {
-    md_data buffer;
+    md_data_t buffer;
     apr_status_t rv;
     
     if (APR_SUCCESS == (rv = cert_to_buffer(&buffer, cert, p))) {
@@ -882,20 +890,20 @@ apr_status_t md_cert_fsave(md_cert_t *ce
 
 apr_status_t md_cert_to_base64url(const char **ps64, const md_cert_t *cert, apr_pool_t *p)
 {
-    md_data buffer;
+    md_data_t buffer;
     apr_status_t rv;
     
     if (APR_SUCCESS == (rv = cert_to_buffer(&buffer, cert, p))) {
-        *ps64 = md_util_base64url_encode(buffer.data, buffer.len, p);
+        *ps64 = md_util_base64url_encode(&buffer, p);
         return APR_SUCCESS;
     }
     *ps64 = NULL;
     return rv;
 }
 
-apr_status_t md_cert_to_sha256_digest(md_data **pdigest, const md_cert_t *cert, apr_pool_t *p)
+apr_status_t md_cert_to_sha256_digest(md_data_t **pdigest, const md_cert_t *cert, apr_pool_t *p)
 {
-    md_data *digest;
+    md_data_t *digest;
     unsigned int dlen;
     apr_status_t rv = APR_ENOMEM;
     
@@ -914,7 +922,7 @@ leave:
 
 apr_status_t md_cert_to_sha256_fingerprint(const char **pfinger, const md_cert_t *cert, apr_pool_t *p)
 {
-    md_data *digest;
+    md_data_t *digest;
     apr_status_t rv;
     
     rv = md_cert_to_sha256_digest(&digest, cert, p);
@@ -937,7 +945,7 @@ static int md_cert_read_pem(BIO *bf, apr
         rv = APR_ENOENT;
         goto out;
     }
-    cert = make_cert(p, x509);
+    cert = md_cert_make(p, x509);
     rv = APR_SUCCESS;
     
 out:
@@ -974,7 +982,7 @@ apr_status_t md_cert_read_http(md_cert_t
                 goto out;
             }
             else {
-                cert = make_cert(p, x509);
+                cert = md_cert_make(p, x509);
                 rv = APR_SUCCESS;
                 md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p, "cert parsed");
             }
@@ -1065,7 +1073,7 @@ apr_status_t md_chain_fappend(struct apr
     if (rv == APR_SUCCESS) {
         ERR_clear_error();
         while (NULL != (x509 = PEM_read_X509(f, NULL, NULL, NULL))) {
-            cert = make_cert(p, x509);
+            cert = md_cert_make(p, x509);
             APR_ARRAY_PUSH(certs, md_cert_t *) = cert;
         }
         fclose(f);
@@ -1248,12 +1256,13 @@ apr_status_t md_cert_req_create(const ch
                                 apr_array_header_t *domains, int must_staple, 
                                 md_pkey_t *pkey, apr_pool_t *p)
 {
-    const char *s, *csr_der, *csr_der_64 = NULL;
+    const char *s, *csr_der_64 = NULL;
     const unsigned char *domain;
     X509_REQ *csr;
     X509_NAME *n = NULL;
     STACK_OF(X509_EXTENSION) *exts = NULL;
     apr_status_t rv;
+    md_data_t csr_der;
     int csr_der_len;
     
     assert(domains->nelts > 0);
@@ -1305,12 +1314,13 @@ apr_status_t md_cert_req_create(const ch
         md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: der length", name);
         rv = APR_EGENERAL; goto out;
     }
-    s = csr_der = apr_pcalloc(p, (apr_size_t)csr_der_len + 1);
+    csr_der.len = (apr_size_t)csr_der_len;
+    s = csr_der.data = apr_pcalloc(p, csr_der.len + 1);
     if (i2d_X509_REQ(csr, (unsigned char**)&s) != csr_der_len) {
         md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: csr der enc", name);
         rv = APR_EGENERAL; goto out;
     }
-    csr_der_64 = md_util_base64url_encode(csr_der, (apr_size_t)csr_der_len, p);
+    csr_der_64 = md_util_base64url_encode(&csr_der, p);
     rv = APR_SUCCESS;
     
 out:
@@ -1418,7 +1428,7 @@ apr_status_t md_cert_self_sign(md_cert_t
         rv = APR_EGENERAL; goto out;
     }
 
-    cert = make_cert(p, x);
+    cert = md_cert_make(p, x);
     rv = APR_SUCCESS;
     
 out:
@@ -1470,7 +1480,7 @@ apr_status_t md_cert_make_tls_alpn_01(md
         rv = APR_EGENERAL; goto out;
     }
 
-    cert = make_cert(p, x);
+    cert = md_cert_make(p, x);
     rv = APR_SUCCESS;
     
 out:

Modified: httpd/httpd/branches/2.4.x/modules/md/md_crypt.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_crypt.h?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_crypt.h (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_crypt.h Fri Oct 25 13:27:12 2019
@@ -24,7 +24,7 @@ struct md_t;
 struct md_http_response_t;
 struct md_cert_t;
 struct md_pkey_t;
-struct md_data;
+struct md_data_t;
 
 
 /**************************************************************************************************/
@@ -32,12 +32,14 @@ struct md_data;
 
 apr_status_t md_rand_bytes(unsigned char *buf, apr_size_t len, apr_pool_t *p);
 
+apr_time_t md_asn1_generalized_time_get(void *ASN1_GENERALIZEDTIME);
+
 /**************************************************************************************************/
 /* digests */
 apr_status_t md_crypt_sha256_digest64(const char **pdigest64, apr_pool_t *p, 
-                                      const struct md_data *data);
+                                      const struct md_data_t *data);
 apr_status_t md_crypt_sha256_digest_hex(const char **pdigesthex, apr_pool_t *p, 
-                                        const struct md_data *data);
+                                        const struct md_data_t *data);
 
 #define MD_DATA_SET_STR(d, s)       do { (d)->data = (s); (d)->len = strlen(s); } while(0)
 
@@ -97,7 +99,18 @@ typedef enum {
     MD_CERT_EXPIRED
 } md_cert_state_t;
 
-void md_cert_free(md_cert_t *cert);
+/**
+ * Create a holder of the certificate that will free its memmory when the
+ * pool is destroyed.
+ */
+md_cert_t *md_cert_make(apr_pool_t *p, void *x509);
+
+/**
+ * Wrap a x509 certificate into our own structure, without taking ownership
+ * of its memory. The caller remains responsible.
+ */
+md_cert_t *md_cert_wrap(apr_pool_t *p, void *x509);
+
 void *md_cert_get_X509(const md_cert_t *cert);
 
 apr_status_t md_cert_fload(md_cert_t **pcert, apr_pool_t *p, const char *fname);
@@ -136,7 +149,7 @@ apr_status_t md_cert_get_alt_names(apr_a
 apr_status_t md_cert_to_base64url(const char **ps64, const md_cert_t *cert, apr_pool_t *p);
 apr_status_t md_cert_from_base64url(md_cert_t **pcert, const char *s64, apr_pool_t *p);
 
-apr_status_t md_cert_to_sha256_digest(struct md_data **pdigest, const md_cert_t *cert, apr_pool_t *p);
+apr_status_t md_cert_to_sha256_digest(struct md_data_t **pdigest, const md_cert_t *cert, apr_pool_t *p);
 apr_status_t md_cert_to_sha256_fingerprint(const char **pfinger, const md_cert_t *cert, apr_pool_t *p);
 
 const char *md_cert_get_serial_number(const md_cert_t *cert, apr_pool_t *p);
@@ -181,9 +194,9 @@ typedef struct md_sct md_sct;
 struct md_sct {
     int version;
     apr_time_t timestamp;
-    struct md_data *logid;
+    struct md_data_t *logid;
     int signature_type_nid;
-    struct md_data *signature;
+    struct md_data_t *signature;
 };
 
 #endif /* md_crypt_h */

Modified: httpd/httpd/branches/2.4.x/modules/md/md_curl.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_curl.c?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_curl.c (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_curl.c Fri Oct 25 13:27:12 2019
@@ -24,13 +24,14 @@
 
 #include "md_http.h"
 #include "md_log.h"
+#include "md_util.h"
 #include "md_curl.h"
 
 /**************************************************************************************************/
 /* md_http curl implementation */
 
 
-static apr_status_t curl_status(int curl_code)
+static apr_status_t curl_status(unsigned int curl_code)
 {
     switch (curl_code) {
         case CURLE_OK:                   return APR_SUCCESS;
@@ -49,6 +50,15 @@ static apr_status_t curl_status(int curl
     }
 }
 
+typedef struct {
+    CURL *curl;
+    CURLM *curlm;
+    struct curl_slist *req_hdrs;
+    md_http_response_t *response;
+    apr_status_t rv;
+    int status_fired;
+} md_curl_internals_t;
+
 static size_t req_data_cb(void *data, size_t len, size_t nmemb, void *baton)
 {
     apr_bucket_brigade *body = baton;
@@ -92,7 +102,8 @@ static size_t req_data_cb(void *data, si
 
 static size_t resp_data_cb(void *data, size_t len, size_t nmemb, void *baton)
 {
-    md_http_response_t *res = baton;
+    md_curl_internals_t *internals = baton;
+    md_http_response_t *res = internals->response;
     size_t blen = len * nmemb;
     apr_status_t rv;
     
@@ -100,7 +111,7 @@ static size_t resp_data_cb(void *data, s
         if (res->req->resp_limit) {
             apr_off_t body_len = 0;
             apr_brigade_length(res->body, 0, &body_len);
-            if (body_len + (apr_off_t)len > res->req->resp_limit) {
+            if (body_len + (apr_off_t)blen > res->req->resp_limit) {
                 return 0; /* signal curl failure */
             }
         }
@@ -115,7 +126,8 @@ static size_t resp_data_cb(void *data, s
 
 static size_t header_cb(void *buffer, size_t elen, size_t nmemb, void *baton)
 {
-    md_http_response_t *res = baton;
+    md_curl_internals_t *internals = baton;
+    md_http_response_t *res = internals->response;
     size_t len, clen = elen * nmemb;
     const char *name = NULL, *value = "", *b = buffer;
     apr_size_t i;
@@ -142,24 +154,6 @@ static size_t header_cb(void *buffer, si
     return clen;
 }
 
-static apr_status_t curl_init(md_http_request_t *req)
-{
-    CURL *curl = curl_easy_init();
-    if (!curl) {
-        return APR_EGENERAL;
-    }
-    
-    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_cb);
-    curl_easy_setopt(curl, CURLOPT_HEADERDATA, NULL);
-    curl_easy_setopt(curl, CURLOPT_READFUNCTION, req_data_cb);
-    curl_easy_setopt(curl, CURLOPT_READDATA, NULL);
-    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, resp_data_cb);
-    curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
-    
-    req->internals = curl;
-    return APR_SUCCESS;
-}
-
 typedef struct {
     md_http_request_t *req;
     struct curl_slist *hdrs;
@@ -181,24 +175,96 @@ static int curlify_headers(void *baton,
     return 1;
 }
 
-static apr_status_t curl_perform(md_http_request_t *req)
+/* Convert timeout values for curl. Since curl uses 0 to disable
+ * timeout, return at least 1 if the apr_time_t value is non-zero. */
+static long timeout_msec(apr_time_t timeout)
 {
-    apr_status_t rv = APR_SUCCESS;
-    CURLcode curle;
-    md_http_response_t *res;
-    CURL *curl;
-    struct curl_slist *req_hdrs = NULL;
+    long ms = (long)apr_time_as_msec(timeout);
+    return ms? ms : (timeout? 1 : 0);
+}
 
-    rv = curl_init(req);
-    curl = req->internals;
+static long timeout_sec(apr_time_t timeout)
+{
+    long s = (long)apr_time_sec(timeout);
+    return s? s : (timeout? 1 : 0);
+}
+
+static int curl_debug_log(CURL *curl, curl_infotype type, char *data, size_t size, void *baton)
+{
+    md_http_request_t *req = baton;
     
-    res = apr_pcalloc(req->pool, sizeof(*res));
+    (void)curl;
+    switch (type) {
+        case CURLINFO_TEXT:
+            md_log_perror(MD_LOG_MARK, MD_LOG_TRACE4, 0, req->pool, 
+                          "req[%d]: info %s", req->id, apr_pstrndup(req->pool, data, size));
+            break;
+        case CURLINFO_HEADER_OUT:
+            md_log_perror(MD_LOG_MARK, MD_LOG_TRACE4, 0, req->pool, 
+                          "req[%d]: header --> %s", req->id, apr_pstrndup(req->pool, data, size));
+            break;
+        case CURLINFO_HEADER_IN:
+            md_log_perror(MD_LOG_MARK, MD_LOG_TRACE4, 0, req->pool, 
+                          "req[%d]: header <-- %s", req->id, apr_pstrndup(req->pool, data, size));
+            break;
+        case CURLINFO_DATA_OUT:
+            md_log_perror(MD_LOG_MARK, MD_LOG_TRACE4, 0, req->pool, 
+                          "req[%d]: data --> %ld bytes", req->id, (long)size);
+            if (md_log_is_level(req->pool, MD_LOG_TRACE5)) {
+                md_data_t d;
+                const char *s;
+                d.data = data;
+                d.len = size;
+                md_data_to_hex(&s, 0, req->pool, &d);
+                md_log_perror(MD_LOG_MARK, MD_LOG_TRACE5, 0, req->pool, 
+                              "req[%d]: data(hex) -->  %s", req->id, s);
+            }
+            break;
+        case CURLINFO_DATA_IN:
+            md_log_perror(MD_LOG_MARK, MD_LOG_TRACE4, 0, req->pool, 
+                          "req[%d]: data <-- %ld bytes", req->id, (long)size);
+            if (md_log_is_level(req->pool, MD_LOG_TRACE5)) {
+                md_data_t d;
+                const char *s;
+                d.data = data;
+                d.len = size;
+                md_data_to_hex(&s, 0, req->pool, &d);
+                md_log_perror(MD_LOG_MARK, MD_LOG_TRACE5, 0, req->pool, 
+                              "req[%d]: data(hex) <-- %s", req->id, s);
+            }
+            break;
+        default:
+            break;
+    }
+    return 0;
+}
+
+static apr_status_t internals_setup(md_http_request_t *req)
+{
+    md_curl_internals_t *internals;
+    CURL *curl;
+    apr_status_t rv = APR_SUCCESS;
     
-    res->req = req;
-    res->rv = APR_SUCCESS;
-    res->status = 400;
-    res->headers = apr_table_make(req->pool, 5);
-    res->body = apr_brigade_create(req->pool, req->bucket_alloc);
+    curl = curl_easy_init();
+    if (!curl) {
+        rv = APR_EGENERAL;
+        goto leave;
+    }
+    internals = apr_pcalloc(req->pool, sizeof(*internals));
+    internals->curl = curl;
+        
+    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_cb);
+    curl_easy_setopt(curl, CURLOPT_HEADERDATA, NULL);
+    curl_easy_setopt(curl, CURLOPT_READFUNCTION, req_data_cb);
+    curl_easy_setopt(curl, CURLOPT_READDATA, NULL);
+    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, resp_data_cb);
+    curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
+
+    internals->response = apr_pcalloc(req->pool, sizeof(md_http_response_t));
+    internals->response->req = req;
+    internals->response->status = 400;
+    internals->response->headers = apr_table_make(req->pool, 5);
+    internals->response->body = apr_brigade_create(req->pool, req->bucket_alloc);
     
     curl_easy_setopt(curl, CURLOPT_URL, req->url);
     if (!apr_strnatcasecmp("GET", req->method)) {
@@ -213,9 +279,20 @@ static apr_status_t curl_perform(md_http
     else {
         curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, req->method);
     }
-    curl_easy_setopt(curl, CURLOPT_HEADERDATA, res);
+    curl_easy_setopt(curl, CURLOPT_HEADERDATA, internals);
     curl_easy_setopt(curl, CURLOPT_READDATA, req->body);
-    curl_easy_setopt(curl, CURLOPT_WRITEDATA, res);
+    curl_easy_setopt(curl, CURLOPT_WRITEDATA, internals);
+    
+    if (req->timeout.overall > 0) {
+        curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, timeout_msec(req->timeout.overall));
+    }
+    if (req->timeout.connect > 0) {
+        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, timeout_msec(req->timeout.connect));
+    }
+    if (req->timeout.stalled > 0) {
+        curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, req->timeout.stall_bytes_per_sec);
+        curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, timeout_sec(req->timeout.stalled));
+    }
     
     if (req->user_agent) {
         curl_easy_setopt(curl, CURLOPT_USERAGENT, req->user_agent);
@@ -230,47 +307,244 @@ static apr_status_t curl_perform(md_http
         ctx.hdrs = NULL;
         ctx.rv = APR_SUCCESS;
         apr_table_do(curlify_headers, &ctx, req->headers, NULL);
-        req_hdrs = ctx.hdrs;
+        internals->req_hdrs = ctx.hdrs;
         if (ctx.rv == APR_SUCCESS) {
-            curl_easy_setopt(curl, CURLOPT_HTTPHEADER, req_hdrs);
+            curl_easy_setopt(curl, CURLOPT_HTTPHEADER, internals->req_hdrs);
         }
     }
     
-    md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, req->pool, 
-                  "request --> %s %s", req->method, req->url);
+    md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, 0, req->pool, 
+                  "req[%d]: %s %s", req->id, req->method, req->url);
     
-    if (md_log_is_level(req->pool, MD_LOG_TRACE3)) {
+    if (md_log_is_level(req->pool, MD_LOG_TRACE4)) {
         curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+        curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_debug_log);
+        curl_easy_setopt(curl, CURLOPT_DEBUGDATA, req);
     }
     
-    curle = curl_easy_perform(curl);
-    res->rv = curl_status(curle);
+leave:
+    req->internals = (APR_SUCCESS == rv)? internals : NULL;
+    return rv;
+}
+
+static apr_status_t update_status(md_http_request_t *req)
+{
+    md_curl_internals_t *internals = req->internals;
+    long l;
+    apr_status_t rv = APR_SUCCESS;
+
+    if (internals) {
+        rv = curl_status(curl_easy_getinfo(internals->curl, CURLINFO_RESPONSE_CODE, &l));
+        if (APR_SUCCESS == rv) {
+            internals->response->status = (int)l;
+        }
+    }
+    return rv;
+}
+
+static void fire_status(md_http_request_t *req, apr_status_t rv)
+{
+    md_curl_internals_t *internals = req->internals;
+        
+    if (internals && !internals->status_fired) {
+        internals->status_fired = 1;
+        
+        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, req->pool, 
+                      "req[%d] fire callbacks", req->id);
+        if ((APR_SUCCESS == rv) && req->cb.on_response) {
+            rv = req->cb.on_response(internals->response, req->cb.on_response_data);
+        }
     
-    if (APR_SUCCESS == res->rv) {
-        long l;
-        res->rv = curl_status(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &l));
-        if (APR_SUCCESS == res->rv) {
-            res->status = (int)l;
+        internals->rv = rv;
+        if (req->cb.on_status) {
+            req->cb.on_status(req, rv, req->cb.on_status_data);
         }
-        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, res->rv, req->pool, 
-                      "request <-- %d", res->status);
     }
-    else {
-        md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, res->rv, req->pool, 
-                      "request failed(%d): %s", curle, 
-                      curl_easy_strerror(curle));
+}
+
+static apr_status_t md_curl_perform(md_http_request_t *req)
+{
+    apr_status_t rv = APR_SUCCESS;
+    CURLcode curle;
+    md_curl_internals_t *internals;
+    long l;
+
+    if (APR_SUCCESS != (rv = internals_setup(req))) goto leave;
+    internals = req->internals;
+    
+    curle = curl_easy_perform(internals->curl);
+    
+    rv = curl_status(curle);
+    if (APR_SUCCESS != rv) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, req->pool, 
+                      "request failed(%d): %s", curle, curl_easy_strerror(curle));
+        goto leave;
     }
     
-    if (req->cb) {
-        res->rv = req->cb(res);
+    rv = curl_status(curl_easy_getinfo(internals->curl, CURLINFO_RESPONSE_CODE, &l));
+    if (APR_SUCCESS == rv) {
+        internals->response->status = (int)l;
     }
+    md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, rv, req->pool, "request <-- %d", 
+                  internals->response->status);
     
-    rv = res->rv;
+    if (req->cb.on_response) {
+        rv = req->cb.on_response(internals->response, req->cb.on_response_data);
+        req->cb.on_response = NULL;
+    }
+    
+leave:
+    fire_status(req, rv);
     md_http_req_destroy(req);
-    if (req_hdrs) {
-        curl_slist_free_all(req_hdrs);
+    return rv;
+}
+
+static md_http_request_t *find_curl_request(apr_array_header_t *requests, CURL *curl)
+{
+    md_http_request_t *req;
+    md_curl_internals_t *internals;
+    int i;
+    
+    for (i = 0; i < requests->nelts; ++i) {
+        req = APR_ARRAY_IDX(requests, i, md_http_request_t*);
+        internals = req->internals;
+        if (internals && internals->curl == curl) {
+            return req;
+        }
+    }
+    return NULL;
+}
+
+static void add_to_curlm(md_http_request_t *req, CURLM *curlm)
+{
+    md_curl_internals_t *internals = req->internals;
+    
+    if (curlm && internals && internals->curlm == NULL) {
+        curl_multi_add_handle(curlm, internals->curl);
+        internals->curlm = curlm;
+    }
+}
+
+static void remove_from_curlm(md_http_request_t *req, CURLM *curlm)
+{
+    md_curl_internals_t *internals = req->internals;
+
+    if (curlm && internals && internals->curlm == curlm) {
+        curl_multi_remove_handle(curlm, internals->curl);
+        internals->curlm = NULL;
+    }
+}
+    
+static apr_status_t md_curl_multi_perform(md_http_t *http, apr_pool_t *p,
+                                          md_http_next_req *nextreq, void *baton)
+{
+    md_http_request_t *req;
+    CURLM *curlm = NULL;
+    CURLMcode mc;
+    struct CURLMsg *curlmsg;
+    apr_array_header_t *requests;
+    int i, running, numfds, slowdown, msgcount;
+    apr_status_t rv;
+    
+    requests = apr_array_make(p, 10, sizeof(md_http_request_t*));
+    curlm = curl_multi_init();
+    if (!curlm) {
+        rv = APR_ENOMEM;
+        goto leave;
     }
     
+    running = 1;
+    slowdown = 0;
+    while(1) {
+        while (1) {
+            /* fetch as many requests as nextreq gives us */
+            rv = nextreq(&req, baton, http, requests->nelts);
+            
+            if (APR_SUCCESS == rv) {
+                if (APR_SUCCESS != (rv = internals_setup(req))) {
+                    if (req->cb.on_status) req->cb.on_status(req, rv, req->cb.on_status_data);
+                    md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p, 
+                                  "multi_perform[%d reqs]: setup failed", requests->nelts);
+                }
+                else {
+                    APR_ARRAY_PUSH(requests, md_http_request_t*) = req;
+                    add_to_curlm(req, curlm);
+                    md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p, 
+                                  "multi_perform[%d reqs]: added request", requests->nelts);
+                }
+                continue;
+            }
+            else if (APR_STATUS_IS_ENOENT(rv)) {
+                md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, 0, p, 
+                              "multi_perform[%d reqs]: no more requests", requests->nelts);
+                if (!running) {
+                    goto leave;
+                }
+                break;
+            }
+            else {
+                md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p, 
+                              "multi_perform[%d reqs]: nextreq() failed", requests->nelts);
+                goto leave;
+            }
+        }
+    
+        mc = curl_multi_perform(curlm, &running);
+        if (CURLM_OK == mc) {
+            mc = curl_multi_wait(curlm, NULL, 0, 1000, &numfds);
+            if (numfds) slowdown = 0;
+        }
+        if (CURLM_OK != mc) {
+            rv = APR_ECONNABORTED;
+            md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, 
+                          "multi_perform[%d reqs] failed(%d): %s", 
+                          requests->nelts, mc, curl_multi_strerror(mc));
+            goto leave;
+        }
+        if (!numfds) {
+            /* no activity on any connection, timeout */
+            md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, 0, p, 
+                          "multi_perform[%d reqs]: slowdown %d", requests->nelts, slowdown);
+            if (slowdown) apr_sleep(apr_time_from_msec(100));
+            ++slowdown;
+        }
+
+        /* process status messages, e.g. that a request is done */
+        while (1) {
+            curlmsg = curl_multi_info_read(curlm, &msgcount);
+            if (!curlmsg) break;
+            if (curlmsg->msg == CURLMSG_DONE) {
+                req = find_curl_request(requests, curlmsg->easy_handle);
+                if (req) {
+                    md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, 0, p, 
+                                  "multi_perform[%d reqs]: req[%d] done", 
+                                  requests->nelts, req->id);
+                    update_status(req);
+                    fire_status(req, curl_status(curlmsg->data.result));
+                    remove_from_curlm(req, curlm);
+                    md_array_remove(requests, req);
+                    md_http_req_destroy(req);
+                }
+                else {
+                    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, 
+                                  "multi_perform[%d reqs]: req done, but not found by handle", 
+                                  requests->nelts);
+                }
+            }
+        }
+        assert(running == requests->nelts);
+    };
+
+leave:
+    md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p, 
+                  "multi_perform[%d reqs]: leaving", requests->nelts);
+    for (i = 0; i < requests->nelts; ++i) {
+        req = APR_ARRAY_IDX(requests, i, md_http_request_t*);
+        fire_status(req, APR_SUCCESS);
+        remove_from_curlm(req, curlm);
+        md_http_req_destroy(req);
+    }
+    if (curlm) curl_multi_cleanup(curlm);
     return rv;
 }
 
@@ -284,18 +558,21 @@ static apr_status_t md_curl_init(void) {
     return APR_SUCCESS;
 }
 
-static void curl_req_cleanup(md_http_request_t *req) 
+static void md_curl_req_cleanup(md_http_request_t *req) 
 {
-    if (req->internals) {
-        curl_easy_cleanup(req->internals);
+    md_curl_internals_t *internals = req->internals;
+    if (internals) {
+        if (internals->curl) curl_easy_cleanup(internals->curl);
+        if (internals->req_hdrs) curl_slist_free_all(internals->req_hdrs);
         req->internals = NULL;
     }
 }
 
 static md_http_impl_t impl = {
     md_curl_init,
-    curl_req_cleanup,
-    curl_perform
+    md_curl_req_cleanup,
+    md_curl_perform,
+    md_curl_multi_perform,
 };
 
 md_http_impl_t * md_curl_get_impl(apr_pool_t *p)

Modified: httpd/httpd/branches/2.4.x/modules/md/md_http.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_http.c?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_http.c (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_http.c Fri Oct 25 13:27:12 2019
@@ -22,14 +22,17 @@
 
 #include "md_http.h"
 #include "md_log.h"
+#include "md_util.h"
 
 struct md_http_t {
     apr_pool_t *pool;
     apr_bucket_alloc_t *bucket_alloc;
+    int next_id;
     apr_off_t resp_limit;
     md_http_impl_t *impl;
     const char *user_agent;
     const char *proxy_url;
+    md_http_timeouts_t timeout;
 };
 
 static md_http_impl_t *cur_impl;
@@ -81,9 +84,82 @@ void md_http_set_response_limit(md_http_
     http->resp_limit = resp_limit;
 }
 
+void md_http_set_timeout_default(md_http_t *http, apr_time_t timeout)
+{
+    http->timeout.overall = timeout;
+}
+
+void md_http_set_timeout(md_http_request_t *req, apr_time_t timeout)
+{
+    req->timeout.overall = timeout;
+}
+
+void md_http_set_connect_timeout_default(md_http_t *http, apr_time_t timeout)
+{
+    http->timeout.connect = timeout;
+}
+
+void md_http_set_connect_timeout(md_http_request_t *req, apr_time_t timeout)
+{
+    req->timeout.connect = timeout;
+}
+
+void md_http_set_stalling_default(md_http_t *http, long bytes_per_sec, apr_time_t timeout)
+{
+    http->timeout.stall_bytes_per_sec = bytes_per_sec;
+    http->timeout.stalled = timeout;
+}
+
+void md_http_set_stalling(md_http_request_t *req, long bytes_per_sec, apr_time_t timeout)
+{
+    req->timeout.stall_bytes_per_sec = bytes_per_sec;
+    req->timeout.stalled = timeout;
+}
+
+static apr_status_t req_set_body(md_http_request_t *req, const char *content_type,
+                                 apr_bucket_brigade *body, apr_off_t body_len,
+                                 int detect_len)
+{
+    apr_status_t rv = APR_SUCCESS;
+    
+    if (body && detect_len) {
+        rv = apr_brigade_length(body, 1, &body_len);
+        if (rv != APR_SUCCESS) {
+            return rv;
+        }
+    }
+
+    req->body = body;
+    req->body_len = body? body_len : 0;
+    if (content_type) {
+        apr_table_set(req->headers, "Content-Type", content_type); 
+    }
+    else {
+        apr_table_unset(req->headers, "Content-Type"); 
+    }
+    return rv;
+}
+
+static apr_status_t req_set_body_data(md_http_request_t *req, const char *content_type,
+                                      const md_data_t *body)
+{
+    apr_bucket_brigade *bbody = NULL;
+    apr_status_t rv;
+    
+    if (body && body->len > 0) {
+        bbody = apr_brigade_create(req->pool, req->http->bucket_alloc);
+        rv = apr_brigade_write(bbody, NULL, NULL, body->data, body->len);
+        if (rv != APR_SUCCESS) {
+            md_http_req_destroy(req);
+            return rv;
+        }
+    }
+    return req_set_body(req, content_type, bbody, body? (apr_off_t)body->len : 0, 0);
+}
+
 static apr_status_t req_create(md_http_request_t **preq, md_http_t *http, 
-                               const char *method, const char *url, struct apr_table_t *headers,
-                               md_http_cb *cb, void *baton)
+                               const char *method, const char *url, 
+                               struct apr_table_t *headers)
 {
     md_http_request_t *req;
     apr_pool_t *pool;
@@ -96,17 +172,16 @@ static apr_status_t req_create(md_http_r
     
     req = apr_pcalloc(pool, sizeof(*req));
     req->pool = pool;
+    req->id = http->next_id++;
     req->bucket_alloc = http->bucket_alloc;
     req->http = http;
     req->method = method;
     req->url = url;
     req->headers = headers? apr_table_copy(req->pool, headers) : apr_table_make(req->pool, 5);
     req->resp_limit = http->resp_limit;
-    req->cb = cb;
-    req->baton = baton;
     req->user_agent = http->user_agent;
     req->proxy_url = http->proxy_url;
-
+    req->timeout = http->timeout;
     *preq = req;
     return rv;
 }
@@ -120,107 +195,157 @@ void md_http_req_destroy(md_http_request
     apr_pool_destroy(req->pool);
 }
 
-static apr_status_t schedule(md_http_request_t *req, 
-                             apr_bucket_brigade *body, int detect_clen) 
+void md_http_set_on_status_cb(md_http_request_t *req, md_http_status_cb *cb, void *baton)
 {
-    apr_status_t rv;
-    
-    req->body = body;
-    req->body_len = body? -1 : 0;
+    req->cb.on_status = cb;
+    req->cb.on_status_data = baton;
+}
 
-    if (req->body && detect_clen) {
-        rv = apr_brigade_length(req->body, 1, &req->body_len);
-        if (rv != APR_SUCCESS) {
-            md_http_req_destroy(req);
-            return rv;
-        }
-    }
-    
+void md_http_set_on_response_cb(md_http_request_t *req, md_http_response_cb *cb, void *baton)
+{
+    req->cb.on_response = cb;
+    req->cb.on_response_data = baton;
+}
+
+static void req_init_cl(md_http_request_t *req)
+{
     if (req->body_len == 0 && apr_strnatcasecmp("GET", req->method)) {
         apr_table_setn(req->headers, "Content-Length", "0");
     }
     else if (req->body_len > 0) {
         apr_table_setn(req->headers, "Content-Length", apr_off_t_toa(req->pool, req->body_len));
     }
-    
+}
+
+apr_status_t md_http_perform(md_http_request_t *req)
+{
+    req_init_cl(req);
     return req->http->impl->perform(req);
 }
 
-apr_status_t md_http_GET(struct md_http_t *http, 
-                         const char *url, struct apr_table_t *headers,
-                         md_http_cb *cb, void *baton)
+typedef struct {
+    md_http_next_req *nextreq;
+    void *baton;
+} nextreq_proxy_t;
+
+static apr_status_t proxy_nextreq(md_http_request_t **preq, void *baton, 
+                                      md_http_t *http, int in_flight)
 {
-    md_http_request_t *req;
+    nextreq_proxy_t *proxy = baton;
     apr_status_t rv;
     
-    rv = req_create(&req, http, "GET", url, headers, cb, baton);
-    if (rv != APR_SUCCESS) {
-        return rv;
-    }
+    rv = proxy->nextreq(preq, proxy->baton, http, in_flight);
+    if (APR_SUCCESS == rv) req_init_cl(*preq);
+    return rv;
+}
+
+apr_status_t md_http_multi_perform(md_http_t *http, md_http_next_req *nextreq, void *baton)
+{
+    nextreq_proxy_t proxy;
     
-    return schedule(req, NULL, 0);
+    proxy.nextreq = nextreq;
+    proxy.baton = baton;
+    return http->impl->multi_perform(http, http->pool, proxy_nextreq, &proxy);
 }
 
-apr_status_t md_http_HEAD(struct md_http_t *http, 
-                          const char *url, struct apr_table_t *headers,
-                          md_http_cb *cb, void *baton)
+apr_status_t md_http_GET_create(md_http_request_t **preq, md_http_t *http, const char *url, 
+                                struct apr_table_t *headers)
 {
     md_http_request_t *req;
     apr_status_t rv;
     
-    rv = req_create(&req, http, "HEAD", url, headers, cb, baton);
-    if (rv != APR_SUCCESS) {
-        return rv;
-    }
-    
-    return schedule(req, NULL, 0);
+    rv = req_create(&req, http, "GET", url, headers);
+    *preq = (APR_SUCCESS == rv)? req : NULL;
+    return rv;
 }
 
-apr_status_t md_http_POST(struct md_http_t *http, const char *url, 
-                          struct apr_table_t *headers, const char *content_type, 
-                          apr_bucket_brigade *body,
-                          md_http_cb *cb, void *baton)
+apr_status_t md_http_HEAD_create(md_http_request_t **preq, md_http_t *http, const char *url, 
+                                 struct apr_table_t *headers)
 {
     md_http_request_t *req;
     apr_status_t rv;
     
-    rv = req_create(&req, http, "POST", url, headers, cb, baton);
-    if (rv != APR_SUCCESS) {
-        return rv;
-    }
-    
-    if (content_type) {
-        apr_table_set(req->headers, "Content-Type", content_type); 
-    }
-    return schedule(req, body, 1);
+    rv = req_create(&req, http, "HEAD", url, headers);
+    *preq = (APR_SUCCESS == rv)? req : NULL;
+    return rv;
 }
 
-apr_status_t md_http_POSTd(md_http_t *http, const char *url, 
-                           struct apr_table_t *headers, const char *content_type, 
-                           const char *data, size_t data_len, 
-                           md_http_cb *cb, void *baton)
+apr_status_t md_http_POST_create(md_http_request_t **preq, md_http_t *http, const char *url, 
+                                 struct apr_table_t *headers, const char *content_type, 
+                                 struct apr_bucket_brigade *body, int detect_len)
 {
     md_http_request_t *req;
     apr_status_t rv;
-    apr_bucket_brigade *body = NULL;
     
-    rv = req_create(&req, http, "POST", url, headers, cb, baton);
-    if (rv != APR_SUCCESS) {
-        return rv;
+    rv = req_create(&req, http, "POST", url, headers);
+    if (APR_SUCCESS == rv) {
+        rv = req_set_body(req, content_type, body, -1, detect_len);
     }
+    *preq = (APR_SUCCESS == rv)? req : NULL;
+    return rv;
+}
 
-    if (data && data_len > 0) {
-        body = apr_brigade_create(req->pool, req->http->bucket_alloc);
-        rv = apr_brigade_write(body, NULL, NULL, data, data_len);
-        if (rv != APR_SUCCESS) {
-            md_http_req_destroy(req);
-            return rv;
-        }
-    }
+apr_status_t md_http_POSTd_create(md_http_request_t **preq, md_http_t *http, const char *url, 
+                                  struct apr_table_t *headers, const char *content_type, 
+                                  const struct md_data_t *body)
+{
+    md_http_request_t *req;
+    apr_status_t rv;
     
-    if (content_type) {
-        apr_table_set(req->headers, "Content-Type", content_type); 
+    rv = req_create(&req, http, "POST", url, headers);
+    if (APR_SUCCESS == rv) {
+        rv = req_set_body_data(req, content_type, body);
     }
-     
-    return schedule(req, body, 1);
+    *preq = (APR_SUCCESS == rv)? req : NULL;
+    return rv;
+}
+
+apr_status_t md_http_GET_perform(struct md_http_t *http, 
+                                 const char *url, struct apr_table_t *headers,
+                                 md_http_response_cb *cb, void *baton)
+{
+    md_http_request_t *req;
+    apr_status_t rv;
+
+    rv = md_http_GET_create(&req, http, url, headers);
+    if (APR_SUCCESS == rv) md_http_set_on_response_cb(req, cb, baton);
+    return (APR_SUCCESS == rv)? md_http_perform(req) : rv;
+}
+
+apr_status_t md_http_HEAD_perform(struct md_http_t *http, 
+                                  const char *url, struct apr_table_t *headers,
+                                  md_http_response_cb *cb, void *baton)
+{
+    md_http_request_t *req;
+    apr_status_t rv;
+
+    rv = md_http_HEAD_create(&req, http, url, headers);
+    if (APR_SUCCESS == rv) md_http_set_on_response_cb(req, cb, baton);
+    return (APR_SUCCESS == rv)? md_http_perform(req) : rv;
+}
+
+apr_status_t md_http_POST_perform(struct md_http_t *http, const char *url, 
+                                  struct apr_table_t *headers, const char *content_type, 
+                                  apr_bucket_brigade *body, int detect_len, 
+                                  md_http_response_cb *cb, void *baton)
+{
+    md_http_request_t *req;
+    apr_status_t rv;
+
+    rv = md_http_POST_create(&req, http, url, headers, content_type, body, detect_len);
+    if (APR_SUCCESS == rv) md_http_set_on_response_cb(req, cb, baton);
+    return (APR_SUCCESS == rv)? md_http_perform(req) : rv;
+}
+
+apr_status_t md_http_POSTd_perform(md_http_t *http, const char *url, 
+                                   struct apr_table_t *headers, const char *content_type, 
+                                   const md_data_t *body, 
+                                   md_http_response_cb *cb, void *baton)
+{
+    md_http_request_t *req;
+    apr_status_t rv;
+
+    rv = md_http_POSTd_create(&req, http, url, headers, content_type, body);
+    if (APR_SUCCESS == rv) md_http_set_on_response_cb(req, cb, baton);
+    return (APR_SUCCESS == rv)? md_http_perform(req) : rv;
 }

Modified: httpd/httpd/branches/2.4.x/modules/md/md_http.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_http.h?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_http.h (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_http.h Fri Oct 25 13:27:12 2019
@@ -20,17 +20,45 @@
 struct apr_table_t;
 struct apr_bucket_brigade;
 struct apr_bucket_alloc_t;
+struct md_data_t;
 
 typedef struct md_http_t md_http_t;
 
 typedef struct md_http_request_t md_http_request_t;
 typedef struct md_http_response_t md_http_response_t;
 
-typedef apr_status_t md_http_cb(const md_http_response_t *res);
+/**
+ * Callback invoked once per request, either when an error was encountered
+ * or when everything succeeded and the request is about to be released. Only
+ * in the last case will the status be APR_SUCCESS.
+ */
+typedef apr_status_t md_http_status_cb(const md_http_request_t *req, apr_status_t status, void *data);
+
+/**
+ * Callback invoked when the complete response has been received.
+ */
+typedef apr_status_t md_http_response_cb(const md_http_response_t *res, void *data);
+
+typedef struct md_http_callbacks_t md_http_callbacks_t;
+struct md_http_callbacks_t {
+    md_http_status_cb *on_status;
+    void *on_status_data;
+    md_http_response_cb *on_response;
+    void *on_response_data;
+};
+
+typedef struct md_http_timeouts_t md_http_timeouts_t;
+struct md_http_timeouts_t {
+    apr_time_t overall;
+    apr_time_t connect;
+    long stall_bytes_per_sec;
+    apr_time_t stalled;
+};
 
 struct md_http_request_t {
     md_http_t *http;
     apr_pool_t *pool;
+    int id;
     struct apr_bucket_alloc_t *bucket_alloc;
     const char *method;
     const char *url;
@@ -40,14 +68,13 @@ struct md_http_request_t {
     struct apr_bucket_brigade *body;
     apr_off_t body_len;
     apr_off_t resp_limit;
-    md_http_cb *cb;
-    void *baton;
+    md_http_timeouts_t timeout;
+    md_http_callbacks_t cb;
     void *internals;
 };
 
 struct md_http_response_t {
     md_http_request_t *req;
-    apr_status_t rv;
     int status;
     apr_table_t *headers;
     struct apr_bucket_brigade *body;
@@ -58,38 +85,154 @@ apr_status_t md_http_create(md_http_t **
 
 void md_http_set_response_limit(md_http_t *http, apr_off_t resp_limit);
 
-apr_status_t md_http_GET(md_http_t *http, 
-                         const char *url, struct apr_table_t *headers,
-                         md_http_cb *cb, void *baton);
-
-apr_status_t md_http_HEAD(md_http_t *http, 
-                          const char *url, struct apr_table_t *headers,
-                          md_http_cb *cb, void *baton);
-
-apr_status_t md_http_POST(md_http_t *http, const char *url, 
-                          struct apr_table_t *headers, const char *content_type, 
-                          struct apr_bucket_brigade *body,
-                          md_http_cb *cb, void *baton);
-
-apr_status_t md_http_POSTd(md_http_t *http, const char *url, 
-                           struct apr_table_t *headers, const char *content_type, 
-                           const char *data, size_t data_len, 
-                           md_http_cb *cb, void *baton);
+/**
+ * Set the timeout for the complete reqest. This needs to take everything from
+ * DNS looksups, to conntects, to transfer of all data into account and should
+ * be sufficiently large.
+ * Set to 0 the have no timeout for this.
+ */
+void md_http_set_timeout_default(md_http_t *http, apr_time_t timeout);
+void md_http_set_timeout(md_http_request_t *req, apr_time_t timeout);
+
+/**
+ * Set the timeout for establishing a connection. 
+ * Set to 0 the have no special timeout for this.
+ */
+void md_http_set_connect_timeout_default(md_http_t *http, apr_time_t timeout);
+void md_http_set_connect_timeout(md_http_request_t *req, apr_time_t timeout);
+
+/**
+ * Set the condition for when a transfer is considered "stalled", e.g. does not
+ * progress at a sufficient rate and will be aborted.
+ * Set to 0 the have no stall detection in place.
+ */
+void md_http_set_stalling_default(md_http_t *http, long bytes_per_sec, apr_time_t timeout);
+void md_http_set_stalling(md_http_request_t *req, long bytes_per_sec, apr_time_t timeout);
+
+/**
+ * Perform the request. Then this function returns, the request and
+ * all its memory has been freed and must no longer be used.
+ */
+apr_status_t md_http_perform(md_http_request_t *request);
+
+/**
+ * Set the callback to be invoked once the status of a request is known.
+ * @param req       the request
+ * @param cb        the callback to invoke on the response
+ * @param baton     data passed to the callback    
+ */
+void md_http_set_on_status_cb(md_http_request_t *req, md_http_status_cb *cb, void *baton);
+
+/**
+ * Set the callback to be invoked when the complete 
+ * response has been successfully received. The HTTP status may
+ * be 500, however.
+ * @param req       the request
+ * @param cb        the callback to invoke on the response
+ * @param baton     data passed to the callback    
+ */
+void md_http_set_on_response_cb(md_http_request_t *req, md_http_response_cb *cb, void *baton);
+
+/**
+ * Create a GET reqest.
+ * @param preq      the created request after success
+ * @param http      the md_http instance 
+ * @param url       the url to GET
+ * @param headers   request headers
+ */
+apr_status_t md_http_GET_create(md_http_request_t **preq, md_http_t *http, const char *url, 
+                                struct apr_table_t *headers);
+
+/**
+ * Create a HEAD reqest.
+ * @param preq      the created request after success
+ * @param http      the md_http instance 
+ * @param url       the url to GET
+ * @param headers   request headers
+ */
+apr_status_t md_http_HEAD_create(md_http_request_t **preq, md_http_t *http, const char *url, 
+                                 struct apr_table_t *headers);
+
+/**
+ * Create a POST reqest with a bucket brigade as request body.
+ * @param preq      the created request after success
+ * @param http      the md_http instance 
+ * @param url       the url to GET
+ * @param headers   request headers
+ * @param content_type the content_type of the body or NULL
+ * @param body      the body of the request or NULL
+ * @param detect_len scan the body to detect its length
+ */
+apr_status_t md_http_POST_create(md_http_request_t **preq, md_http_t *http, const char *url, 
+                                 struct apr_table_t *headers, const char *content_type, 
+                                 struct apr_bucket_brigade *body, int detect_len);
+
+/**
+ * Create a POST reqest with known request body data.
+ * @param preq      the created request after success
+ * @param http      the md_http instance 
+ * @param url       the url to GET
+ * @param headers   request headers
+ * @param content_type the content_type of the body or NULL
+ * @param body      the body of the request or NULL
+ */
+apr_status_t md_http_POSTd_create(md_http_request_t **preq, md_http_t *http, const char *url, 
+                                  struct apr_table_t *headers, const char *content_type, 
+                                  const struct md_data_t *body);
+
+/*
+ * Convenience functions for create+perform.
+ */
+apr_status_t md_http_GET_perform(md_http_t *http, const char *url, 
+                                 struct apr_table_t *headers,
+                                 md_http_response_cb *cb, void *baton);
+apr_status_t md_http_HEAD_perform(md_http_t *http, const char *url, 
+                                  struct apr_table_t *headers,
+                                  md_http_response_cb *cb, void *baton);
+apr_status_t md_http_POST_perform(md_http_t *http, const char *url, 
+                                  struct apr_table_t *headers, const char *content_type, 
+                                  struct apr_bucket_brigade *body, int detect_len, 
+                                  md_http_response_cb *cb, void *baton);
+apr_status_t md_http_POSTd_perform(md_http_t *http, const char *url, 
+                                   struct apr_table_t *headers, const char *content_type, 
+                                   const struct md_data_t *body, 
+                                   md_http_response_cb *cb, void *baton);
 
 void md_http_req_destroy(md_http_request_t *req);
 
+/** Return the next request for processing on APR_SUCCESS. Return ARP_ENOENT
+ * when no request is available. Anything else is an error.
+ */
+typedef apr_status_t md_http_next_req(md_http_request_t **preq, void *baton, 
+                                      md_http_t *http, int in_flight);
+
+/**
+ * Perform requests in parallel as retrieved from the nextreq function.
+ * There are as many requests in flight as the nextreq functions provides. 
+ *
+ * To limit the number of parallel requests, nextreq should return APR_ENOENT when the limit
+ * is reached. It will be called again when the number of in_flight requests changes.
+ * 
+ * When all reqests are done, nextreq will be called one more time. Should it not
+ * return anything, this function returns.
+ */
+apr_status_t md_http_multi_perform(md_http_t *http, md_http_next_req *nextreq, void *baton);
+
 /**************************************************************************************************/
 /* interface to implementation */
 
 typedef apr_status_t md_http_init_cb(void);
 typedef void md_http_req_cleanup_cb(md_http_request_t *req);
 typedef apr_status_t md_http_perform_cb(md_http_request_t *req);
+typedef apr_status_t md_http_multi_perform_cb(md_http_t *http, apr_pool_t *p, 
+                                              md_http_next_req *nextreq, void *baton);
 
 typedef struct md_http_impl_t md_http_impl_t;
 struct md_http_impl_t {
     md_http_init_cb *init;
     md_http_req_cleanup_cb *req_cleanup;
     md_http_perform_cb *perform;
+    md_http_multi_perform_cb *multi_perform;
 };
 
 void md_http_use_implementation(md_http_impl_t *impl);

Modified: httpd/httpd/branches/2.4.x/modules/md/md_json.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_json.c?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_json.c (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_json.c Fri Oct 25 13:27:12 2019
@@ -18,10 +18,12 @@
 #include <apr_lib.h>
 #include <apr_strings.h>
 #include <apr_buckets.h>
+#include <apr_date.h>
 
 #include "md_json.h"
 #include "md_log.h"
 #include "md_http.h"
+#include "md_time.h"
 #include "md_util.h"
 
 /* jansson thinks everyone compiles with the platform's cc in its fullest capabilities
@@ -106,12 +108,12 @@ void md_json_destroy(md_json_t *json)
     }
 }
 
-md_json_t *md_json_copy(apr_pool_t *pool, md_json_t *json)
+md_json_t *md_json_copy(apr_pool_t *pool, const md_json_t *json)
 {
     return json_create(pool, json_copy(json->j));
 }
 
-md_json_t *md_json_clone(apr_pool_t *pool, md_json_t *json)
+md_json_t *md_json_clone(apr_pool_t *pool, const md_json_t *json)
 {
     return json_create(pool, json_deep_copy(json->j));
 }
@@ -168,18 +170,16 @@ static apr_status_t jselect_add(json_t *
     j = jselect_parent(&key, 1, json, ap);
     
     if (!j || !json_is_object(j)) {
-        json_decref(val);
         return APR_EINVAL;
     }
     
     aj = json_object_get(j, key);
     if (!aj) {
         aj = json_array();
-        json_object_set_new(j, key, aj);
+        json_object_set(j, key, aj);
     }
     
     if (!json_is_array(aj)) {
-        json_decref(val);
         return APR_EINVAL;
     }
 
@@ -202,7 +202,7 @@ static apr_status_t jselect_insert(json_
     aj = json_object_get(j, key);
     if (!aj) {
         aj = json_array();
-        json_object_set_new(j, key, aj);
+        json_object_set(j, key, aj);
     }
     
     if (!json_is_array(aj)) {
@@ -227,13 +227,11 @@ static apr_status_t jselect_set(json_t *
     j = jselect_parent(&key, 1, json, ap);
     
     if (!j) {
-        json_decref(val);
         return APR_EINVAL;
     }
     
     if (key) {
         if (!json_is_object(j)) {
-            json_decref(val);
             return APR_EINVAL;
         }
         json_object_set(j, key, val);
@@ -313,6 +311,19 @@ int md_json_is(const md_json_type_t jtyp
     return 0;
 }
 
+static const char *md_json_type_name(const md_json_t *json)
+{
+    json_t *j = json->j;
+    if (json_is_object(j)) return "object";
+    if (json_is_array(j)) return "array";
+    if (json_is_string(j)) return "string";
+    if (json_is_real(j)) return "real";
+    if (json_is_integer(j)) return "integer";
+    if (json_is_true(j)) return "true";
+    if (json_is_false(j)) return "false";
+    return "unknown";
+}
+
 /**************************************************************************************************/
 /* booleans */
 
@@ -428,6 +439,35 @@ apr_status_t md_json_sets(const char *va
 }
 
 /**************************************************************************************************/
+/* time */
+
+apr_time_t md_json_get_time(const md_json_t *json, ...)
+{
+    json_t *j;
+    va_list ap;
+    
+    va_start(ap, json);
+    j = jselect(json, ap);
+    va_end(ap);
+
+    if (!j || !json_is_string(j)) return 0;
+    return apr_date_parse_rfc(json_string_value(j));
+}
+
+apr_status_t md_json_set_time(apr_time_t value, md_json_t *json, ...)
+{
+    char ts[APR_RFC822_DATE_LEN];
+    va_list ap;
+    apr_status_t rv;
+    
+    apr_rfc822_date(ts, value);
+    va_start(ap, json);
+    rv = jselect_set_new(json_string(ts), json, ap);
+    va_end(ap);
+    return rv;
+}
+
+/**************************************************************************************************/
 /* json itself */
 
 md_json_t *md_json_getj(md_json_t *json, ...)
@@ -449,6 +489,22 @@ md_json_t *md_json_getj(md_json_t *json,
     return NULL;
 }
 
+md_json_t *md_json_dupj(apr_pool_t *p, const md_json_t *json, ...)
+{
+    json_t *j;
+    va_list ap;
+    
+    va_start(ap, json);
+    j = jselect(json, ap);
+    va_end(ap);
+    
+    if (j) {
+        json_incref(j);
+        return json_create(p, j);
+    }
+    return NULL;
+}
+
 const md_json_t *md_json_getcj(const md_json_t *json, ...)
 {
     json_t *j;
@@ -468,7 +524,7 @@ const md_json_t *md_json_getcj(const md_
     return NULL;
 }
 
-apr_status_t md_json_setj(md_json_t *value, md_json_t *json, ...)
+apr_status_t md_json_setj(const md_json_t *value, md_json_t *json, ...)
 {
     va_list ap;
     apr_status_t rv;
@@ -496,7 +552,7 @@ apr_status_t md_json_setj(md_json_t *val
     return rv;
 }
 
-apr_status_t md_json_addj(md_json_t *value, md_json_t *json, ...)
+apr_status_t md_json_addj(const md_json_t *value, md_json_t *json, ...)
 {
     va_list ap;
     apr_status_t rv;
@@ -518,6 +574,25 @@ apr_status_t md_json_insertj(md_json_t *
     return rv;
 }
 
+apr_size_t md_json_limita(size_t max_elements, md_json_t *json, ...)
+{
+    json_t *j;
+    va_list ap;
+    apr_size_t n = 0;
+    
+    va_start(ap, json);
+    j = jselect(json, ap);
+    va_end(ap);
+
+    if (j && json_is_array(j)) {
+        n = json_array_size(j);
+        while (n > max_elements) {
+            json_array_remove(j, n-1);
+            n = json_array_size(j);
+        }
+    }
+    return n;
+}
 
 /**************************************************************************************************/
 /* arrays / objects */
@@ -642,7 +717,7 @@ apr_status_t md_json_clone_to(void *valu
     return md_json_setj(md_json_clone(p, value), json, NULL);
 }
 
-apr_status_t md_json_clone_from(void **pvalue, md_json_t *json, apr_pool_t *p, void *baton)
+apr_status_t md_json_clone_from(void **pvalue, const md_json_t *json, apr_pool_t *p, void *baton)
 {
     (void)baton;
     *pvalue = md_json_clone(p, json);
@@ -843,7 +918,7 @@ apr_status_t md_json_setsa(apr_array_hea
 /* formatting, parsing */
 
 typedef struct {
-    md_json_t *json;
+    const md_json_t *json;
     md_json_fmt_t fmt;
     const char *fname;
     apr_file_t *f;
@@ -868,7 +943,7 @@ static int dump_cb(const char *buffer, s
     return (rv == APR_SUCCESS)? 0 : -1;
 }
 
-apr_status_t md_json_writeb(md_json_t *json, md_json_fmt_t fmt, apr_bucket_brigade *bb)
+apr_status_t md_json_writeb(const md_json_t *json, md_json_fmt_t fmt, apr_bucket_brigade *bb)
 {
     int rv = json_dump_callback(json->j, dump_cb, bb, fmt_to_flags(fmt));
     return rv? APR_EGENERAL : APR_SUCCESS;
@@ -877,14 +952,18 @@ apr_status_t md_json_writeb(md_json_t *j
 static int chunk_cb(const char *buffer, size_t len, void *baton)
 {
     apr_array_header_t *chunks = baton;
-    char *chunk = apr_pcalloc(chunks->pool, len+1);
+    char *chunk;
     
-    memcpy(chunk, buffer, len);
-    APR_ARRAY_PUSH(chunks, const char *) = chunk;
+    if (len > 0) {
+        chunk = apr_palloc(chunks->pool, len+1);
+        memcpy(chunk, buffer, len);
+        chunk[len] = '\0';
+        APR_ARRAY_PUSH(chunks, const char*) = chunk;
+    }
     return 0;
 }
 
-const char *md_json_writep(md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt)
+const char *md_json_writep(const md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt)
 {
     apr_array_header_t *chunks;
     int rv;
@@ -901,13 +980,13 @@ const char *md_json_writep(md_json_t *js
         case 0:
             return "";
         case 1:
-            return APR_ARRAY_IDX(chunks, 0, const char *);
+            return APR_ARRAY_IDX(chunks, 0, const char*);
         default:
             return apr_array_pstrcat(p, chunks, 0);
     }
 }
 
-apr_status_t md_json_writef(md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt, apr_file_t *f)
+apr_status_t md_json_writef(const md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt, apr_file_t *f)
 {
     apr_status_t rv;
     const char *s;
@@ -920,12 +999,13 @@ apr_status_t md_json_writef(md_json_t *j
     }
     else {
         rv = APR_EINVAL;
-        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, json->p, "md_json_writef: error dumping json");
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, json->p, 
+                      "md_json_writef: error dumping json (%s)", md_json_dump_state(json, p));
     }
     return rv;
 }
 
-apr_status_t md_json_fcreatex(md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt, 
+apr_status_t md_json_fcreatex(const md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt, 
                               const char *fpath, apr_fileperms_t perms)
 {
     apr_status_t rv;
@@ -949,7 +1029,7 @@ static apr_status_t write_json(void *bat
     return rv;
 }
 
-apr_status_t md_json_freplace(md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt, 
+apr_status_t md_json_freplace(const md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt, 
                               const char *fpath, apr_fileperms_t perms)
 {
     j_write_ctx ctx;
@@ -1076,11 +1156,9 @@ apr_status_t md_json_readf(md_json_t **p
 apr_status_t md_json_read_http(md_json_t **pjson, apr_pool_t *pool, const md_http_response_t *res)
 {
     apr_status_t rv = APR_ENOENT;
-    if (res->rv == APR_SUCCESS) {
-        const char *ctype = apr_table_get(res->headers, "content-type");
-        if (ctype && res->body && (strstr(ctype, "/json") || strstr(ctype, "+json"))) {
-            rv = md_json_readb(pjson, pool, res->body);
-        }
+    const char *ctype = apr_table_get(res->headers, "content-type");
+    if (ctype && res->body && (strstr(ctype, "/json") || strstr(ctype, "+json"))) {
+        rv = md_json_readb(pjson, pool, res->body);
     }
     return rv;
 }
@@ -1091,9 +1169,9 @@ typedef struct {
     md_json_t *json;
 } resp_data;
 
-static apr_status_t json_resp_cb(const md_http_response_t *res)
+static apr_status_t json_resp_cb(const md_http_response_t *res, void *data)
 {
-    resp_data *resp = res->req->baton;
+    resp_data *resp = data;
     return md_json_read_http(&resp->json, resp->pool, res);
 }
 
@@ -1106,7 +1184,7 @@ apr_status_t md_json_http_get(md_json_t
     memset(&resp, 0, sizeof(resp));
     resp.pool = pool;
     
-    rv = md_http_GET(http, url, NULL, json_resp_cb, &resp);
+    rv = md_http_GET_perform(http, url, NULL, json_resp_cb, &resp);
     
     if (rv == APR_SUCCESS) {
         *pjson = resp.json;
@@ -1134,3 +1212,63 @@ apr_status_t md_json_copy_to(md_json_t *
     }
     return rv;
 }
+
+const char *md_json_dump_state(const md_json_t *json, apr_pool_t *p)
+{
+    if (!json) return "NULL";
+    return apr_psprintf(p, "%s, refc=%ld", md_json_type_name(json), (long)json->j->refcount);
+}
+
+apr_status_t md_json_set_timeperiod(const md_timeperiod_t *tp, md_json_t *json, ...)
+{
+    char ts[APR_RFC822_DATE_LEN];
+    json_t *jn, *j;
+    va_list ap;
+    const char *key;
+    apr_status_t rv;
+    
+    if (!tp || tp->start || tp->end) {
+        jn = json_object();
+        apr_rfc822_date(ts, tp->start);
+        json_object_set_new(jn, "from", json_string(ts));
+        apr_rfc822_date(ts, tp->end);
+        json_object_set_new(jn, "until", json_string(ts));
+        
+        va_start(ap, json);
+        rv = jselect_set_new(jn, json, ap);
+        va_end(ap);
+        return rv;
+    }
+    else {
+        va_start(ap, json);
+        j = jselect_parent(&key, 0, json, ap);
+        va_end(ap);
+        
+        if (key && j && json_is_object(j)) {
+            json_object_del(j, key);
+        }
+        return APR_SUCCESS;
+    }
+}
+
+apr_status_t md_json_get_timeperiod(md_timeperiod_t *tp, md_json_t *json, ...)
+{
+    json_t *j, *jts;
+    va_list ap;
+    
+    va_start(ap, json);
+    j = jselect(json, ap);
+    va_end(ap);
+    
+    memset(tp, 0, sizeof(*tp));
+    if (!j) goto not_found;
+    jts = json_object_get(j, "from");
+    if (!jts || !json_is_string(jts)) goto not_found;
+    tp->start = apr_date_parse_rfc(json_string_value(jts)); 
+    jts = json_object_get(j, "until");
+    if (!jts || !json_is_string(jts)) goto not_found;
+    tp->end = apr_date_parse_rfc(json_string_value(jts)); 
+    return APR_SUCCESS;
+not_found:
+    return APR_ENOENT;
+}

Modified: httpd/httpd/branches/2.4.x/modules/md/md_json.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_json.h?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_json.h (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_json.h Fri Oct 25 13:27:12 2019
@@ -24,7 +24,7 @@ struct apr_file_t;
 
 struct md_http_t;
 struct md_http_response_t;
-
+struct md_timeperiod_t;
 
 typedef struct md_json_t md_json_t;
 
@@ -47,8 +47,8 @@ typedef enum {
 md_json_t *md_json_create(apr_pool_t *pool);
 void md_json_destroy(md_json_t *json);
 
-md_json_t *md_json_copy(apr_pool_t *pool, md_json_t *json);
-md_json_t *md_json_clone(apr_pool_t *pool, md_json_t *json);
+md_json_t *md_json_copy(apr_pool_t *pool, const md_json_t *json);
+md_json_t *md_json_clone(apr_pool_t *pool, const md_json_t *json);
 
 
 int md_json_has_key(const md_json_t *json, ...);
@@ -72,17 +72,25 @@ const char *md_json_gets(const md_json_t
 const char *md_json_dups(apr_pool_t *p, const md_json_t *json, ...);
 apr_status_t md_json_sets(const char *s, md_json_t *json, ...);
 
+/* timestamp manipulation */
+apr_time_t md_json_get_time(const md_json_t *json, ...);
+apr_status_t md_json_set_time(apr_time_t value, md_json_t *json, ...);
+
 /* json manipulation */
 md_json_t *md_json_getj(md_json_t *json, ...);
+md_json_t *md_json_dupj(apr_pool_t *p, const md_json_t *json, ...);
 const md_json_t *md_json_getcj(const md_json_t *json, ...);
-apr_status_t md_json_setj(md_json_t *value, md_json_t *json, ...);
-apr_status_t md_json_addj(md_json_t *value, md_json_t *json, ...);
+apr_status_t md_json_setj(const md_json_t *value, md_json_t *json, ...);
+apr_status_t md_json_addj(const md_json_t *value, md_json_t *json, ...);
 apr_status_t md_json_insertj(md_json_t *value, size_t index, md_json_t *json, ...);
 
 /* Array/Object manipulation */
 apr_status_t md_json_clr(md_json_t *json, ...);
 apr_status_t md_json_del(md_json_t *json, ...);
 
+/* Remove all array elements beyond max_elements */ 
+apr_size_t md_json_limita(size_t max_elements, md_json_t *json, ...);
+
 /* conversion function from and to json */
 typedef apr_status_t md_json_to_cb(void *value, md_json_t *json, apr_pool_t *p, void *baton);
 typedef apr_status_t md_json_from_cb(void **pvalue, md_json_t *json, apr_pool_t *p, void *baton);
@@ -93,7 +101,7 @@ apr_status_t md_json_pass_from(void **pv
 
 /* conversions from json to json in specified pool */
 apr_status_t md_json_clone_to(void *value, md_json_t *json, apr_pool_t *p, void *baton);
-apr_status_t md_json_clone_from(void **pvalue, md_json_t *json, apr_pool_t *p, void *baton);
+apr_status_t md_json_clone_from(void **pvalue, const md_json_t *json, apr_pool_t *p, void *baton);
 
 /* Manipulating/Iteration on generic Arrays */
 apr_status_t md_json_geta(apr_array_header_t *a, md_json_from_cb *cb, 
@@ -115,13 +123,13 @@ apr_status_t md_json_dupsa(apr_array_hea
 apr_status_t md_json_setsa(apr_array_header_t *a, md_json_t *json, ...);
 
 /* serialization & parsing */
-apr_status_t md_json_writeb(md_json_t *json, md_json_fmt_t fmt, struct apr_bucket_brigade *bb);
-const char *md_json_writep(md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt);
-apr_status_t md_json_writef(md_json_t *json, apr_pool_t *p, 
+apr_status_t md_json_writeb(const md_json_t *json, md_json_fmt_t fmt, struct apr_bucket_brigade *bb);
+const char *md_json_writep(const md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt);
+apr_status_t md_json_writef(const md_json_t *json, apr_pool_t *p, 
                             md_json_fmt_t fmt, struct apr_file_t *f);
-apr_status_t md_json_fcreatex(md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt, 
+apr_status_t md_json_fcreatex(const md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt, 
                               const char *fpath, apr_fileperms_t perms);
-apr_status_t md_json_freplace(md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt, 
+apr_status_t md_json_freplace(const md_json_t *json, apr_pool_t *p, md_json_fmt_t fmt, 
                               const char *fpath, apr_fileperms_t perms);
 
 apr_status_t md_json_readb(md_json_t **pjson, apr_pool_t *pool, struct apr_bucket_brigade *bb);
@@ -137,4 +145,9 @@ apr_status_t md_json_read_http(md_json_t
 
 apr_status_t md_json_copy_to(md_json_t *dest, const md_json_t *src, ...);
 
+const char *md_json_dump_state(const md_json_t *json, apr_pool_t *p);
+
+apr_status_t md_json_set_timeperiod(const struct md_timeperiod_t *tp, md_json_t *json, ...);
+apr_status_t md_json_get_timeperiod(struct md_timeperiod_t *tp, md_json_t *json, ...);
+
 #endif /* md_json_h */

Modified: httpd/httpd/branches/2.4.x/modules/md/md_jws.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_jws.c?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_jws.c (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_jws.c Fri Oct 25 13:27:12 2019
@@ -32,13 +32,13 @@ static int header_set(void *data, const
 }
 
 apr_status_t md_jws_sign(md_json_t **pmsg, apr_pool_t *p,
-                         const char *payload, size_t len, 
-                         struct apr_table_t *protected, 
+                         md_data_t *payload, struct apr_table_t *protected, 
                          struct md_pkey_t *pkey, const char *key_id)
 {
     md_json_t *msg, *jprotected;
     const char *prot64, *pay64, *sign64, *sign, *prot;
     apr_status_t rv = APR_SUCCESS;
+    md_data_t data;
 
     *pmsg = NULL;
     
@@ -64,9 +64,11 @@ apr_status_t md_jws_sign(md_json_t **pms
     }
     
     if (rv == APR_SUCCESS) {
-        prot64 = md_util_base64url_encode(prot, strlen(prot), p);
+        data.data = prot;
+        data.len = strlen(prot);
+        prot64 = md_util_base64url_encode(&data, p);
         md_json_sets(prot64, msg, "protected", NULL);
-        pay64 = md_util_base64url_encode(payload, len, p);
+        pay64 = md_util_base64url_encode(payload, p);
 
         md_json_sets(pay64, msg, "payload", NULL);
         sign = apr_psprintf(p, "%s.%s", prot64, pay64);
@@ -91,7 +93,7 @@ apr_status_t md_jws_sign(md_json_t **pms
 apr_status_t md_jws_pkey_thumb(const char **pthumb, apr_pool_t *p, struct md_pkey_t *pkey)
 {
     const char *e64, *n64, *s;
-    md_data data;
+    md_data_t data;
     apr_status_t rv;
     
     e64 = md_pkey_get_rsa_e64(pkey, p);

Modified: httpd/httpd/branches/2.4.x/modules/md/md_jws.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/md/md_jws.h?rev=1868930&r1=1868929&r2=1868930&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/md/md_jws.h (original)
+++ httpd/httpd/branches/2.4.x/modules/md/md_jws.h Fri Oct 25 13:27:12 2019
@@ -20,9 +20,10 @@
 struct apr_table_t;
 struct md_json_t;
 struct md_pkey_t;
+struct md_data_t;
 
 apr_status_t md_jws_sign(md_json_t **pmsg, apr_pool_t *p,
-                         const char *payload, size_t len, struct apr_table_t *protected, 
+                         struct md_data_t *payload, struct apr_table_t *protected, 
                          struct md_pkey_t *pkey, const char *key_id);
 
 apr_status_t md_jws_pkey_thumb(const char **pthumb, apr_pool_t *p, struct md_pkey_t *pkey);