]> granicus.if.org Git - apache/commitdiff
http: add ap_fgetline() and AP_GETLINE_NONBLOCK flag.
authorYann Ylavic <ylavic@apache.org>
Fri, 20 Apr 2018 14:30:19 +0000 (14:30 +0000)
committerYann Ylavic <ylavic@apache.org>
Fri, 20 Apr 2018 14:30:19 +0000 (14:30 +0000)
It allows to read a line directly from an input filter, in blocking mode
or not. Since no request_rec is needed, a pool may be given.

Existing ap_[r]getline() function are now based off ap_fgetline() by calling:
    ap_fgetline(s, n, read, r->proto_input_filters, flags, bb, r->pool);

Will follow up with a new ap_get_mime_headers_*() flavor which can be used by
any filter that needs non-blocking and not necessarily has a request_rec (e.g.
ap_http_filter() to read proxied response trailers).

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1829659 13f79535-47bb-0310-9956-ffa450edef68

include/ap_mmn.h
include/http_protocol.h
server/protocol.c

index 25cd91b90050c61c9b912c003658ada463179438..eb66cf9c0078390208c0067dccd543bc0614c549 100644 (file)
  * 20171014.9 (2.5.1-dev)  Add response_field_size to proxy_worker_shared 
  * 20180417.1 (2.5.1-dev)  Toggle ap_filter_input|output_pending API to _NONSTD
  * 20180417.2 (2.5.1-dev)  Add AP_GETLINE_NOSPC_EOL flag to http_protocol.h
+ * 20180417.3 (2.5.1-dev)  Add ap_fgetline() and AP_GETLINE_NONBLOCK flag
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
 #ifndef MODULE_MAGIC_NUMBER_MAJOR
 #define MODULE_MAGIC_NUMBER_MAJOR 20180417
 #endif
-#define MODULE_MAGIC_NUMBER_MINOR 2                 /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 3                 /* 0...n */
 
 /**
  * Determine if the server's current MODULE_MAGIC_NUMBER is at least a
index f38b413b83ebafa9106d38e5a19fca7b284dc277..bf15f9837bd0891479d12b8174a1c67c0fd719fe 100644 (file)
@@ -630,19 +630,18 @@ AP_DECLARE(apr_status_t) ap_get_basic_auth_components(const request_rec *r,
  */
 AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri);
 
-#define AP_GETLINE_FOLD 1 /* Whether to merge continuation lines */
-#define AP_GETLINE_CRLF 2 /* Whether line ends must be in the form CR LF */
-#define AP_GETLINE_NOSPC_EOL 4 /* Whether to consume up to and including the
-                                  end of line on APR_ENOSPC */
+#define AP_GETLINE_FOLD      (1 << 0) /* Whether to merge continuation lines */
+#define AP_GETLINE_CRLF      (1 << 1) /* Whether line ends must be CRLF */
+#define AP_GETLINE_NOSPC_EOL (1 << 2) /* Whether to consume up to and including
+                                         the end of line on APR_ENOSPC */
+#define AP_GETLINE_NONBLOCK  (1 << 3) /* Whether to read non-blocking */
 
 /**
  * Get the next line of input for the request
  * @param s The buffer into which to read the line
  * @param n The size of the buffer
  * @param r The request
- * @param flags Bit flag of multiple parsing options
- *              AP_GETLINE_FOLD Whether to merge continuation lines
- *              AP_GETLINE_CRLF Whether line ends must be in the form CR LF
+ * @param flags Bit mask of AP_GETLINE_* options
  * @return The length of the line, if successful
  *         n, if the line is too big to fit in the buffer
  *         -1 for miscellaneous errors
@@ -650,45 +649,56 @@ AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri);
 AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int flags);
 
 /**
- * Get the next line of input for the request
- *
- * Note: on ASCII boxes, ap_rgetline is a macro which simply calls
- *       ap_rgetline_core to get the line of input.
- *
- *       on EBCDIC boxes, ap_rgetline is a wrapper function which
- *       translates ASCII protocol lines to the local EBCDIC code page
- *       after getting the line of input.
+ * Get the next line from an input filter
  *
  * @param s Pointer to the pointer to the buffer into which the line
  *          should be read; if *s==NULL, a buffer of the necessary size
- *          to hold the data will be allocated from the request pool
+ *          to hold the data will be allocated from \p p
  * @param n The size of the buffer
  * @param read The length of the line.
- * @param r The request
- * @param flags Bit flag of multiple parsing options
- *              AP_GETLINE_FOLD Whether to merge continuation lines
- *              AP_GETLINE_CRLF Whether line ends must be in the form CR LF
+ * @param f Input filter to read from
+ * @param flags Bit mask of AP_GETLINE_* options
  * @param bb Working brigade to use when reading buckets
+ * @param p The pool to allocate the buffer from (if needed)
  * @return APR_SUCCESS, if successful
  *         APR_ENOSPC, if the line is too big to fit in the buffer
  *         Other errors where appropriate
  */
+AP_DECLARE(apr_status_t) ap_fgetline(char **s, apr_size_t n,
+                                     apr_size_t *read, ap_filter_t *f,
+                                     int flags, apr_bucket_brigade *bb,
+                                     apr_pool_t *p);
+
+/**
+ * @see ap_fgetline
+ *
+ * Note: genuinely calls, ap_fgetline(s, n, read, r->proto_input_filters,
+ *                                    flags, bb, r->pool)
+ */
+AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
+                                          apr_size_t *read, request_rec *r,
+                                          int flags, apr_bucket_brigade *bb);
+
+/**
+ * @see ap_rgetline_core
+ *
+ * Note: on ASCII boxes, ap_rgetline is a macro which simply calls
+ *       ap_rgetline_core to get the line of input.
+ *
+ *       on EBCDIC boxes, ap_rgetline is a wrapper function which
+ *       translates ASCII protocol lines to the local EBCDIC code page
+ *       after getting the line of input.
+ *
+ */
 #if APR_CHARSET_EBCDIC
 AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n,
-                                     apr_size_t *read,
-                                     request_rec *r, int flags,
-                                     apr_bucket_brigade *bb);
+                                     apr_size_t *read, request_rec *r,
+                                     int flags, apr_bucket_brigade *bb);
 #else /* ASCII box */
 #define ap_rgetline(s, n, read, r, flags, bb) \
         ap_rgetline_core((s), (n), (read), (r), (flags), (bb))
 #endif
 
-/** @see ap_rgetline */
-AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
-                                          apr_size_t *read,
-                                          request_rec *r, int flags,
-                                          apr_bucket_brigade *bb);
-
 /**
  * Get the method number associated with the given string, assumed to
  * contain an HTTP method.  Returns M_INVALID if not recognized.
index 27611ed5089192014ada0ec2170cddc031043395..ff67115cb41a635410344d38f2fe2b225f19c4d2 100644 (file)
@@ -188,15 +188,11 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(request_rec *r, apr_time_t mtime)
     return (mtime > now) ? now : mtime;
 }
 
-/* Get a line of protocol input, including any continuation lines
+/* Get a line from an input filter, including any continuation lines
  * caused by MIME folding (or broken clients) if fold != 0, and place it
  * in the buffer s, of size n bytes, without the ending newline.
- * 
- * Pulls from r->proto_input_filters instead of r->input_filters for
- * stricter protocol adherence and better input filter behavior during
- * chunked trailer processing (for http).
  *
- * If s is NULL, ap_rgetline_core will allocate necessary memory from r->pool.
+ * If s is NULL, ap_fgetline_impl will allocate necessary memory from p.
  *
  * Returns APR_SUCCESS if there are no problems and sets *read to be
  * the full length of s.
@@ -213,9 +209,10 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(request_rec *r, apr_time_t mtime)
  *        If no LF is detected on the last line due to a dropped connection
  *        or a full buffer, that's considered an error.
  */
-AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
-                                          apr_size_t *read, request_rec *r,
-                                          int flags, apr_bucket_brigade *bb)
+static apr_status_t ap_fgetline_impl(char **s, apr_size_t n,
+                                     apr_size_t *read, ap_filter_t *f,
+                                     int flags, apr_bucket_brigade *bb,
+                                     apr_pool_t *p)
 {
     apr_status_t rv;
     apr_bucket *e;
@@ -226,6 +223,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
     int crlf = flags & AP_GETLINE_CRLF;
     int nospc_eol = flags & AP_GETLINE_NOSPC_EOL;
     int saw_eol = 0, saw_nospc = 0;
+    apr_read_type_e block;
 
     if (!n) {
         /* Needs room for NUL byte at least */
@@ -233,6 +231,9 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
         return APR_BADARG;
     }
 
+    block = (flags & AP_GETLINE_NONBLOCK) ? APR_NONBLOCK_READ
+                                          : APR_BLOCK_READ;
+
     /*
      * Initialize last_char as otherwise a random value will be compared
      * against APR_ASCII_LF at the end of the loop if bb only contains
@@ -243,17 +244,22 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
 
     do {
         apr_brigade_cleanup(bb);
-        rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_GETLINE,
-                            APR_BLOCK_READ, 0);
+        rv = ap_get_brigade(f, bb, AP_MODE_GETLINE, block, 0);
         if (rv != APR_SUCCESS) {
             goto cleanup;
         }
 
         /* Something horribly wrong happened.  Someone didn't block! 
          * (this also happens at the end of each keepalive connection)
+         * (this also happens when non-blocking is asked too, not that wrong)
          */
         if (APR_BRIGADE_EMPTY(bb)) {
-            rv = APR_EGENERAL;
+            if (block != APR_NONBLOCK_READ) {
+                rv = APR_EGENERAL;
+            }
+            else {
+                rv = APR_EAGAIN;
+            }
             goto cleanup;
         }
 
@@ -338,7 +344,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
                 /* We'll assume the common case where one bucket is enough. */
                 if (!*s) {
                     current_alloc = len;
-                    *s = apr_palloc(r->pool, current_alloc + 1);
+                    *s = apr_palloc(p, current_alloc + 1);
                 }
                 else if (bytes_handled + len > current_alloc) {
                     /* Increase the buffer size */
@@ -349,7 +355,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
                         new_size = (bytes_handled + len) * 2;
                     }
 
-                    new_buffer = apr_palloc(r->pool, new_size + 1);
+                    new_buffer = apr_palloc(p, new_size + 1);
 
                     /* Copy what we already had. */
                     memcpy(new_buffer, *s, bytes_handled);
@@ -408,8 +414,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
             apr_brigade_cleanup(bb);
 
             /* We only care about the first byte. */
-            rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_SPECULATIVE,
-                                APR_BLOCK_READ, 1);
+            rv = ap_get_brigade(f, bb, AP_MODE_SPECULATIVE, block, 1);
             if (rv != APR_SUCCESS) {
                 goto cleanup;
             }
@@ -464,8 +469,8 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
 
                     next_size = n - bytes_handled;
 
-                    rv = ap_rgetline_core(&tmp, next_size, &next_len, r,
-                                          flags & ~AP_GETLINE_FOLD, bb);
+                    rv = ap_fgetline_impl(&tmp, next_size, &next_len, f,
+                                          flags & ~AP_GETLINE_FOLD, bb, p);
                     if (rv != APR_SUCCESS) {
                         goto cleanup;
                     }
@@ -475,7 +480,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
                         apr_size_t new_size = bytes_handled + next_len + 1;
 
                         /* we need to alloc an extra byte for a null */
-                        new_buffer = apr_palloc(r->pool, new_size);
+                        new_buffer = apr_palloc(p, new_size);
 
                         /* Copy what we already had. */
                         memcpy(new_buffer, *s, bytes_handled);
@@ -517,10 +522,31 @@ cleanup:
     return rv;
 }
 
+AP_DECLARE(apr_status_t) ap_fgetline(char **s, apr_size_t n,
+                                     apr_size_t *read, ap_filter_t *f,
+                                     int flags, apr_bucket_brigade *bb,
+                                     apr_pool_t *p)
+{
+    return ap_fgetline_impl(s, n, read, f, flags, bb, p);
+}
+
+/* Same as ap_fgetline(), working on r's pool and protocol input filters.
+ * Pulls from r->proto_input_filters instead of r->input_filters for
+ * stricter protocol adherence and better input filter behavior during
+ * chunked trailer processing (for http).
+ */
+AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
+                                          apr_size_t *read, request_rec *r,
+                                          int flags, apr_bucket_brigade *bb)
+{
+    return ap_fgetline_impl(s, n, read, r->proto_input_filters, flags,
+                            bb, r->pool);
+}
+
 #if APR_CHARSET_EBCDIC
 AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n,
                                      apr_size_t *read, request_rec *r,
-                                     int fold, apr_bucket_brigade *bb)
+                                     int flags, apr_bucket_brigade *bb)
 {
     /* on ASCII boxes, ap_rgetline is a macro which simply invokes
      * ap_rgetline_core with the same parms
@@ -532,8 +558,9 @@ AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n,
      */
     apr_status_t rv;
 
-    rv = ap_rgetline_core(s, n, read, r, fold, bb);
-    if (rv == APR_SUCCESS || APR_STATUS_IS_ENOSPC(rv)) {
+    rv = ap_fgetline_impl(s, n, read, r->proto_input_filters, flags,
+                          bb, r->pool);
+    if (*read) {
         ap_xlate_proto_from_ascii(*s, *read);
     }
     return rv;
@@ -542,7 +569,6 @@ AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n,
 
 AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int flags)
 {
-    char *tmp_s = s;
     apr_status_t rv;
     apr_size_t len;
     apr_bucket_brigade *tmp_bb;
@@ -553,7 +579,8 @@ AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int flags)
     }
 
     tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
-    rv = ap_rgetline(&tmp_s, n, &len, r, flags, tmp_bb);
+    rv = ap_fgetline_impl(&s, n, &len, r->proto_input_filters, flags,
+                          tmp_bb, r->pool);
     apr_brigade_destroy(tmp_bb);
 
     /* Map the out-of-space condition to the old API. */