Add support for BIO_CTRL_EOF and implement the hit eof logic for ssl buckets
and APR files.

Potentially OpenSSL can check BIOs for EOF. For example, OpenSSL 1.1.1e [1] and
OpenSSL 3.0+ [2] have such checks.

[1] https://github.com/openssl/openssl/commit/db943f43a60d1b5b1277e4b5317e8f288e7a0a3a
[2] https://github.com/openssl/openssl/commit/d924dbf4ae127c68463bcbece04b6e06abc58928

Patch by: Denis Kovalchuk <denis.kovalchuk{_AT_}visualsvn.com>

* buckets/ssl_buckets.c
  (serf_ssl_context_t): New hit_eof variable.
  (bio_bucket_read): Properly set the hit_eof variable.
  (bio_bucket_ctrl): Rework to support BIO_CTRL_EOF.
  (bio_file_ctrl): New ctrl function for APR files to support BIO_CTRL_EOF.
  (bio_file_method,
   bio_meth_file_new): Use the new bio_file_ctrl().
  (ssl_init_context): Init the hit_eof variable.

* test/MockHTTPinC/MockHTTP_server.c
  (sslCtx_t): New hit_eof variable.
  (bio_apr_socket_ctrl): Rework to support BIO_CTRL_EOF.
  (bio_apr_socket_read): Properly set the hit_eof variable.
  (initSSLCtx): Init the hit_eof variable.


git-svn-id: https://svn.apache.org/repos/asf/serf/trunk@1902208 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/buckets/ssl_buckets.c b/buckets/ssl_buckets.c
index 901ddb6..4fd8e2c 100644
--- a/buckets/ssl_buckets.c
+++ b/buckets/ssl_buckets.c
@@ -161,6 +161,9 @@
     /* Should we read before we can write again? */
     int want_read;
     int handshake_done;
+    /* OpenSSL 1.1.1e introduced BIO_FLAGS_IN_EOF, but we implement
+       our own hit eof to support versions < 1.1.1e. */
+    int hit_eof;
 
     /* Client cert callbacks */
     serf_ssl_need_client_cert_t cert_callback;
@@ -383,7 +386,9 @@
         return -1; /* Raises: SSL_ERROR_SYSCALL; caller reads crypt_status */
     }
 
-    if (status && !APR_STATUS_IS_EOF(status)) {
+    if (APR_STATUS_IS_EOF(status)) {
+        ctx->hit_eof = TRUE;
+    } else if (status) {
         BIO_set_retry_read(bio); /* Signal SSL: Retry later */
     }
 
@@ -506,21 +511,43 @@
 
 static long bio_bucket_ctrl(BIO *bio, int cmd, long num, void *ptr)
 {
-    long ret = 1;
+    serf_ssl_context_t *ctx = bio_get_data(bio);
 
     switch (cmd) {
-    default:
-        /* abort(); */
-        break;
     case BIO_CTRL_FLUSH:
         /* At this point we can't force a flush. */
-        break;
+        return 1;
     case BIO_CTRL_PUSH:
     case BIO_CTRL_POP:
-        ret = 0;
-        break;
+        return 0;
+    case BIO_CTRL_EOF:
+        return ctx->hit_eof;
+    default:
+        /* abort(); */
+        return 1;
     }
-    return ret;
+}
+
+static long bio_file_ctrl(BIO *bio, int cmd, long num, void *ptr)
+{
+    apr_file_t *file = bio_get_data(bio);
+
+    switch (cmd) {
+    case BIO_CTRL_FLUSH:
+        /* At this point we can't force a flush. */
+        return 1;
+    case BIO_CTRL_PUSH:
+    case BIO_CTRL_POP:
+        return 0;
+    case BIO_CTRL_EOF:
+        if (apr_file_eof(file) == APR_EOF)
+            return 1;
+        else
+            return 0;
+    default:
+        /* abort(); */
+        return 1;
+    }
 }
 
 #ifdef SERF_NO_SSL_BIO_WRAPPERS
@@ -546,7 +573,7 @@
     bio_file_read,
     NULL,                        /* Is this called? */
     bio_file_gets,               /* Is this called? */
-    bio_bucket_ctrl,
+    bio_file_ctrl,
     bio_bucket_create,
     bio_bucket_destroy,
 #ifdef OPENSSL_VERSION_NUMBER
@@ -586,7 +613,7 @@
         BIO_meth_set_write(biom, bio_file_write);
         BIO_meth_set_read(biom, bio_file_read);
         BIO_meth_set_gets(biom, bio_file_gets);
-        BIO_meth_set_ctrl(biom, bio_bucket_ctrl);
+        BIO_meth_set_ctrl(biom, bio_file_ctrl);
         BIO_meth_set_create(biom, bio_bucket_create);
         BIO_meth_set_destroy(biom, bio_bucket_destroy);
     }
@@ -1820,6 +1847,7 @@
     ssl_ctx->crypt_status = APR_SUCCESS;
     ssl_ctx->want_read = FALSE;
     ssl_ctx->handshake_done = FALSE;
+    ssl_ctx->hit_eof = FALSE;
 
     return ssl_ctx;
 }
diff --git a/test/MockHTTPinC/MockHTTP_server.c b/test/MockHTTPinC/MockHTTP_server.c
index ceba318..b6eefcd 100644
--- a/test/MockHTTPinC/MockHTTP_server.c
+++ b/test/MockHTTPinC/MockHTTP_server.c
@@ -2242,6 +2242,7 @@
 struct sslCtx_t {
     bool handshake_done;
     bool renegotiate;
+    bool hit_eof;
     apr_status_t bio_status;
 
     SSL_CTX* ctx;
@@ -2324,21 +2325,25 @@
  */
 static long bio_apr_socket_ctrl(BIO *bio, int cmd, long num, void *ptr)
 {
-    long ret = 1;
+    _mhClientCtx_t *cctx = bio_get_data(bio);
+    sslCtx_t *ssl_ctx = cctx->ssl_ctx;
 
     switch (cmd) {
-        default:
-            /* abort(); */
-            break;
         case BIO_CTRL_FLUSH:
             /* At this point we can't force a flush. */
-            break;
+            return 1;
         case BIO_CTRL_PUSH:
         case BIO_CTRL_POP:
-            ret = 0;
-            break;
+            return 0;
+        case BIO_CTRL_EOF:
+            if (ssl_ctx->hit_eof == YES)
+                return 1;
+            else
+                return 0;
+        default:
+            /* abort(); */
+            return 1;
     }
-    return ret;
 }
 
 /**
@@ -2360,7 +2365,9 @@
         _mhLog(MH_VERBOSE, cctx->skt, "Read %d bytes from ssl socket with "
                "status %d.\n", len, status);
 
-    if (APR_STATUS_IS_EAGAIN(status)) {
+    if (APR_STATUS_IS_EOF(status)) {
+        ssl_ctx->hit_eof = YES;
+    } else if (APR_STATUS_IS_EAGAIN(status)) {
         BIO_set_retry_read(bio);
     }
 
@@ -2661,6 +2668,7 @@
 {
     sslCtx_t *ssl_ctx = apr_pcalloc(cctx->pool, sizeof(*ssl_ctx));
     cctx->ssl_ctx = ssl_ctx;
+    ssl_ctx->hit_eof = NO;
     ssl_ctx->bio_status = APR_SUCCESS;
 
     _mhLog(MH_VERBOSE, cctx->skt, "Initializing SSL context.\n");