Index: mod_ssl.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/ssl/mod_ssl.c,v
retrieving revision 1.74.2.6
diff -u -r1.74.2.6 mod_ssl.c
--- mod_ssl.c	9 Feb 2004 20:53:20 -0000	1.74.2.6
+++ mod_ssl.c	4 Mar 2004 22:17:16 -0000
@@ -94,7 +94,7 @@
     /*
      * Per-server context configuration directives
      */
-    SSL_CMD_SRV(Engine, FLAG,
+    SSL_CMD_SRV(Engine, TAKE1,
                 "SSL switch for the protocol engine "
                 "(`on', `off')")
     SSL_CMD_ALL(CipherSuite, TAKE1,
@@ -255,7 +255,7 @@
 
     SSLConnRec *sslconn;
 
-    if (!sc->enabled) {
+    if (sc->enabled == SSL_ENABLED_FALSE) {
         return 0;
     }
 
@@ -266,7 +266,7 @@
     return 1;
 }
 
-static int ssl_hook_pre_connection(conn_rec *c, void *csd)
+int ssl_init_ssl_connection(conn_rec *c)
 {
     SSLSrvConfigRec *sc = mySrvConfig(c->base_server);
     SSL *ssl;
@@ -275,40 +275,14 @@
     modssl_ctx_t *mctx;
 
     /*
-     * Immediately stop processing if SSL is disabled for this connection
+     * Seed the Pseudo Random Number Generator (PRNG)
      */
-    if (!(sc && (sc->enabled ||
-                 (sslconn && sslconn->is_proxy))))
-    {
-        return DECLINED;
-    }
+    ssl_rand_seed(c->base_server, c->pool, SSL_RSCTX_CONNECT, "");
 
-    /*
-     * Create SSL context
-     */
     if (!sslconn) {
         sslconn = ssl_init_connection_ctx(c);
     }
 
-    if (sslconn->disabled) {
-        return DECLINED;
-    }
-
-    /*
-     * Remember the connection information for
-     * later access inside callback functions
-     */
-
-    ap_log_error(APLOG_MARK, APLOG_INFO, 0, c->base_server,
-                 "Connection to child %ld established "
-                 "(server %s, client %s)", c->id, sc->vhost_id, 
-                 c->remote_ip ? c->remote_ip : "unknown");
-
-    /*
-     * Seed the Pseudo Random Number Generator (PRNG)
-     */
-    ssl_rand_seed(c->base_server, c->pool, SSL_RSCTX_CONNECT, "");
-
     mctx = sslconn->is_proxy ? sc->proxy : sc->server;
 
     /*
@@ -364,7 +338,7 @@
 {
     SSLSrvConfigRec *sc = mySrvConfig(r->server);
 
-    if (sc->enabled == FALSE) {
+    if (sc->enabled == SSL_ENABLED_FALSE) {
         return NULL;
     }
 
@@ -375,13 +349,61 @@
 {
     SSLSrvConfigRec *sc = mySrvConfig(r->server);
 
-    if (sc->enabled == FALSE) {
+    if (sc->enabled == SSL_ENABLED_FALSE) {
         return 0;
     }
 
     return 443;
 }
 
+static int ssl_hook_pre_connection(conn_rec *c, void *csd)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(c->base_server);
+    SSLConnRec *sslconn = myConnConfig(c);
+
+    /*
+     * Immediately stop processing if SSL is disabled for this connection
+     */
+    if (!(sc && (sc->enabled == SSL_ENABLED_TRUE ||
+                 (sslconn && sslconn->is_proxy))))
+    {
+        return DECLINED;
+    }
+
+    /*
+     * Create SSL context
+     */
+    if (!sslconn) {
+        sslconn = ssl_init_connection_ctx(c);
+    }
+
+    if (sslconn->disabled) {
+        return DECLINED;
+    }
+
+    /*
+     * Remember the connection information for
+     * later access inside callback functions
+     */
+
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, c->base_server,
+                 "Connection to child %ld established "
+                 "(server %s, client %s)", c->id, sc->vhost_id, 
+                 c->remote_ip ? c->remote_ip : "unknown");
+
+    return ssl_init_ssl_connection(c);
+}
+
+
+static void ssl_hook_Insert_Filter(request_rec *r)
+{
+    SSLSrvConfigRec *sc = mySrvConfig(r->server);
+
+    if (sc->enabled == SSL_ENABLED_OPTIONAL) {
+        ap_add_output_filter("UPGRADE_FILTER", NULL, r, r->connection);
+    }
+}
+
 /*
  *  the module registration phase
  */
@@ -402,6 +424,8 @@
     ap_hook_access_checker(ssl_hook_Access,        NULL,NULL, APR_HOOK_MIDDLE);
     ap_hook_auth_checker  (ssl_hook_Auth,          NULL,NULL, APR_HOOK_MIDDLE);
     ap_hook_post_read_request(ssl_hook_ReadReq,    NULL,NULL, APR_HOOK_MIDDLE);
+    ap_hook_insert_filter (ssl_hook_Insert_Filter, NULL,NULL, APR_HOOK_MIDDLE);
+/*    ap_hook_handler       (ssl_hook_Upgrade,       NULL,NULL, APR_HOOK_MIDDLE); */
 
     ssl_var_register();
 
Index: ssl_engine_config.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/ssl/ssl_engine_config.c,v
retrieving revision 1.70.2.7
diff -u -r1.70.2.7 ssl_engine_config.c
--- ssl_engine_config.c	9 Feb 2004 20:53:20 -0000	1.70.2.7
+++ ssl_engine_config.c	4 Mar 2004 22:17:16 -0000
@@ -171,7 +171,7 @@
     SSLSrvConfigRec *sc = apr_palloc(p, sizeof(*sc));
 
     sc->mc                     = NULL;
-    sc->enabled                = UNSET;
+    sc->enabled                = SSL_ENABLED_FALSE;
     sc->proxy_enabled          = UNSET;
     sc->vhost_id               = NULL;  /* set during module init */
     sc->vhost_id_len           = 0;     /* set during module init */
@@ -257,7 +257,8 @@
     SSLSrvConfigRec *mrg  = ssl_config_server_new(p);
 
     cfgMerge(mc, NULL);
-    cfgMergeBool(enabled);
+    cfgMerge(enabled, SSL_ENABLED_UNSET);
+
     cfgMergeBool(proxy_enabled);
     cfgMergeInt(session_cache_timeout);
 
@@ -616,13 +617,24 @@
     return NULL;
 }
 
-const char *ssl_cmd_SSLEngine(cmd_parms *cmd, void *dcfg, int flag)
+const char *ssl_cmd_SSLEngine(cmd_parms *cmd, void *dcfg, const char *arg)
 {
     SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
 
-    sc->enabled = flag ? TRUE : FALSE;
-
+    if (!strcasecmp(arg, "On")) {
+        sc->enabled = SSL_ENABLED_TRUE;
     return NULL;
+    }
+    else if (!strcasecmp(arg, "Off")) {
+        sc->enabled = SSL_ENABLED_FALSE;
+        return NULL;
+    }
+    else if (!strcasecmp(arg, "Optional")) {
+        sc->enabled = SSL_ENABLED_OPTIONAL;
+        return NULL;
+    }
+
+    return "Argument must be On, Off, or Optional";
 }
 
 const char *ssl_cmd_SSLCipherSuite(cmd_parms *cmd,
Index: ssl_engine_init.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/ssl/ssl_engine_init.c,v
retrieving revision 1.106.2.11
diff -u -r1.106.2.11 ssl_engine_init.c
--- ssl_engine_init.c	9 Feb 2004 20:53:20 -0000	1.106.2.11
+++ ssl_engine_init.c	4 Mar 2004 22:17:16 -0000
@@ -212,11 +212,11 @@
         sc->vhost_id = ssl_util_vhostid(p, s);
         sc->vhost_id_len = strlen(sc->vhost_id);
 
+       /* If sc->enabled is UNSET, then SSL is optional on this vhost  */
         /* Fix up stuff that may not have been set */
-        if (sc->enabled == UNSET) {
-            sc->enabled = FALSE;
+        if (sc->enabled == SSL_ENABLED_UNSET) {
+            sc->enabled = SSL_ENABLED_FALSE;
         }
-
         if (sc->proxy_enabled == UNSET) {
             sc->proxy_enabled = FALSE;
         }
@@ -947,7 +947,9 @@
                               apr_pool_t *ptemp,
                               SSLSrvConfigRec *sc)
 {
-    if (sc->enabled) {
+    /* Initialize the server if SSL is enabled or optional.
+     */
+    if ((sc->enabled == SSL_ENABLED_TRUE) || (sc->enabled == SSL_ENABLED_OPTIONAL)) {
         ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
                      "Configuring server for SSL protocol");
         ssl_init_server_ctx(s, p, ptemp, sc);
@@ -975,7 +977,7 @@
     for (s = base_server; s; s = s->next) {
         sc = mySrvConfig(s);
 
-        if (sc->enabled && (s->port == DEFAULT_HTTP_PORT)) {
+        if ((sc->enabled == SSL_ENABLED_TRUE) && (s->port == DEFAULT_HTTP_PORT)) {
             ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
                          base_server,
                          "Init: (%s) You configured HTTPS(%d) "
@@ -984,7 +986,7 @@
                          DEFAULT_HTTPS_PORT, DEFAULT_HTTP_PORT);
         }
 
-        if (!sc->enabled && (s->port == DEFAULT_HTTPS_PORT)) {
+        if ((sc->enabled == SSL_ENABLED_FALSE) && (s->port == DEFAULT_HTTPS_PORT)) {
             ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
                          base_server,
                          "Init: (%s) You configured HTTP(%d) "
@@ -1005,7 +1007,7 @@
     for (s = base_server; s; s = s->next) {
         sc = mySrvConfig(s);
 
-        if (!(sc->enabled && s->addrs)) {
+        if (!((sc->enabled == SSL_ENABLED_TRUE) && s->addrs)) {
             continue;
         }
 
Index: ssl_engine_io.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/ssl/ssl_engine_io.c,v
retrieving revision 1.100.2.11
diff -u -r1.100.2.11 ssl_engine_io.c
--- ssl_engine_io.c	9 Feb 2004 20:53:20 -0000	1.100.2.11
+++ ssl_engine_io.c	4 Mar 2004 22:17:16 -0000
@@ -1160,6 +1160,93 @@
     return APR_SUCCESS;
 }
 
+static apr_status_t ssl_io_filter_Upgrade(ap_filter_t *f,
+                                         apr_bucket_brigade *bb)
+
+{
+#define SWITCH_STATUS_LINE "HTTP/1.1 101 Switching Protocols"
+#define UPGRADE_HEADER "Upgrade: TLS/1.0 HTTP/1.1"
+#define CONNECTION_HEADER "Connection: Upgrade"
+    const char *upgrade;
+    const char *connection;
+    apr_bucket_brigade *upgradebb;
+    request_rec *r = f->r;
+    SSLConnRec *sslconn;
+    SSL *ssl;
+
+    /* Just remove the filter, if it doesn't work the first time, it won't
+     * work at all for this request.
+     */
+    ap_remove_output_filter(f);
+
+    /* No need to ensure that this is a server with optional SSL, the filter
+     * is only inserted if that is true.
+     */
+
+    upgrade = apr_table_get(r->headers_in, "Upgrade");
+    if (upgrade == NULL) {
+        return ap_pass_brigade(f->next, bb);
+    }
+    connection = apr_table_get(r->headers_in, "Connection");
+
+    apr_table_unset(r->headers_out, "Upgrade");
+
+    /* XXX: I don't think the requirement that the client sends exactly 
+     * "Connection: Upgrade" is correct; the only requirement here is 
+     * on the client to send a  Connection header including the "upgrade" 
+     * token.
+     */
+    if (strcmp(connection, "Upgrade") || strcmp(upgrade, "TLS/1.0")) {
+        return ap_pass_brigade(f->next, bb);
+    }
+
+    if (r->method_number == M_OPTIONS) {
+        apr_bucket *b = NULL;
+        /* This is a mandatory SSL upgrade. */
+
+        upgradebb = apr_brigade_create(r->pool, f->c->bucket_alloc);
+
+        ap_fputstrs(f->next, upgradebb, SWITCH_STATUS_LINE, CRLF,
+                    UPGRADE_HEADER, CRLF, CONNECTION_HEADER, CRLF, CRLF, NULL);
+
+        b = apr_bucket_flush_create(f->c->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(upgradebb, b);
+
+        ap_pass_brigade(f->next, upgradebb);
+    }
+    else {
+        /* This is optional, and should be configurable, for now don't bother
+         * doing anything.
+         */
+        return ap_pass_brigade(f->next, bb);
+    }
+
+    ssl_init_ssl_connection(f->c);
+
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
+                 "Awaiting re-negotiation handshake");
+
+    sslconn = myConnConfig(f->c);
+    ssl = sslconn->ssl;
+
+    /* XXX: Should replace SSL_set_state with SSL_renegotiate(ssl);
+     * However, this causes failures in perl-framework currently, 
+     * perhaps pre-test if we have already negotiated?
+     */
+    SSL_set_state(ssl, SSL_ST_ACCEPT);
+    SSL_do_handshake(ssl);
+
+    if (SSL_get_state(ssl) != SSL_ST_OK) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                     "Re-negotiation handshake failed: "
+                "Not accepted by client!?");
+
+        return AP_FILTER_ERROR;
+    }
+
+    return OK;
+}
+
 static apr_status_t ssl_io_filter_input(ap_filter_t *f,
                                         apr_bucket_brigade *bb,
                                         ap_input_mode_t mode,
@@ -1396,6 +1483,11 @@
 
 void ssl_io_filter_register(apr_pool_t *p)
 {
+    /* This filter MUST be after the HTTP_HEADER filter, but it also must be
+     * a resource-level filter so it has the request_rec.
+     */
+    ap_register_output_filter ("UPGRADE_FILTER", ssl_io_filter_Upgrade, NULL, AP_FTYPE_PROTOCOL + 5);
+
     ap_register_input_filter  (ssl_io_filter, ssl_io_filter_input,  NULL, AP_FTYPE_CONNECTION + 5);
     ap_register_output_filter (ssl_io_filter, ssl_io_filter_output, NULL, AP_FTYPE_CONNECTION + 5);
     return;
Index: ssl_engine_kernel.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/ssl/ssl_engine_kernel.c,v
retrieving revision 1.82.2.12
diff -u -r1.82.2.12 ssl_engine_kernel.c
--- ssl_engine_kernel.c	9 Feb 2004 20:53:20 -0000	1.82.2.12
+++ ssl_engine_kernel.c	4 Mar 2004 22:17:16 -0000
@@ -190,6 +190,16 @@
      * Support for SSLRequireSSL directive
      */
     if (dc->bSSLRequired && !ssl) {
+        if (sc->enabled == SSL_ENABLED_OPTIONAL) {
+            /* This vhost was configured for optional SSL, just tell the
+             * client that we need to upgrade.
+             */
+            apr_table_setn(r->err_headers_out, "Upgrade", "TLS/1.0, HTTP/1.1");
+            apr_table_setn(r->err_headers_out, "Connection", "Upgrade");
+
+            return HTTP_UPGRADE_REQUIRED;
+        }
+
         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 
                       "access to %s failed, reason: %s",
                       r->filename, "SSL connection required");
@@ -203,7 +213,7 @@
     /*
      * Check to see if SSL protocol is on
      */
-    if (!(sc->enabled || ssl)) {
+    if (!((sc->enabled == SSL_ENABLED_TRUE) || (sc->enabled == SSL_ENABLED_OPTIONAL) || ssl)) {
         return DECLINED;
     }
     /*
@@ -846,7 +856,7 @@
      * - ssl not enabled
      * - client did not present a certificate
      */
-    if (!(sc->enabled && sslconn->ssl && sslconn->client_cert) ||
+    if (!((sc->enabled == SSL_ENABLED_TRUE || sc->enabled == SSL_ENABLED_OPTIONAL) && sslconn->ssl && sslconn->client_cert) ||
         !(dc->nOptions & SSL_OPT_FAKEBASICAUTH) || r->user)
     {
         return DECLINED;
@@ -998,10 +1008,14 @@
     SSL *ssl;
     int i;
 
+    if (sc->enabled == SSL_ENABLED_OPTIONAL) {
+        apr_table_setn(r->headers_out, "Upgrade", "TLS/1.0, HTTP/1.1");
+    }
+
     /*
      * Check to see if SSL is on
      */
-    if (!(sc->enabled && sslconn && (ssl = sslconn->ssl))) {
+    if (!(((sc->enabled == SSL_ENABLED_TRUE) || (sc->enabled == SSL_ENABLED_OPTIONAL)) && sslconn && (ssl = sslconn->ssl))) {
         return DECLINED;
     }
 
Index: ssl_engine_pphrase.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/ssl/ssl_engine_pphrase.c,v
retrieving revision 1.42.2.5
diff -u -r1.42.2.5 ssl_engine_pphrase.c
--- ssl_engine_pphrase.c	9 Feb 2004 20:53:20 -0000	1.42.2.5
+++ ssl_engine_pphrase.c	4 Mar 2004 22:17:16 -0000
@@ -173,7 +173,7 @@
     for (pServ = s; pServ != NULL; pServ = pServ->next) {
         sc = mySrvConfig(pServ);
 
-        if (!sc->enabled)
+        if (sc->enabled == SSL_ENABLED_FALSE)
             continue;
 
         cpVHostID = ssl_util_vhostid(p, pServ);
Index: ssl_util.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/ssl/ssl_util.c,v
retrieving revision 1.35.2.6
diff -u -r1.35.2.6 ssl_util.c
--- ssl_util.c	9 Feb 2004 20:53:20 -0000	1.35.2.6
+++ ssl_util.c	4 Mar 2004 22:17:16 -0000
@@ -50,7 +50,7 @@
         port = s->port;
     else {
         sc = mySrvConfig(s);
-        if (sc->enabled)
+        if (sc->enabled == SSL_ENABLED_TRUE)
             port = DEFAULT_HTTPS_PORT;
         else
             port = DEFAULT_HTTP_PORT;
Index: mod_ssl.h
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/ssl/mod_ssl.h,v
retrieving revision 1.122.2.9
diff -u -r1.122.2.9 mod_ssl.h
--- mod_ssl.h	9 Feb 2004 20:53:20 -0000	1.122.2.9
+++ mod_ssl.h	4 Mar 2004 22:17:16 -0000
@@ -329,6 +329,16 @@
 } ssl_mutexmode_t;
 
 /*
+ * Define the SSL enabled state
+ */
+typedef enum {
+    SSL_ENABLED_UNSET    = UNSET,
+    SSL_ENABLED_FALSE    = 0,
+    SSL_ENABLED_TRUE     = 1,
+	SSL_ENABLED_OPTIONAL = 3
+} ssl_enabled_t;
+
+/*
  * Define the SSL requirement structure
  */
 typedef struct {
@@ -477,7 +487,7 @@
 
 struct SSLSrvConfigRec {
     SSLModConfigRec *mc;
-    BOOL             enabled;
+    ssl_enabled_t    enabled;
     BOOL             proxy_enabled;
     const char      *vhost_id;
     int              vhost_id_len;
@@ -526,7 +536,7 @@
 const char  *ssl_cmd_SSLPassPhraseDialog(cmd_parms *, void *, const char *);
 const char  *ssl_cmd_SSLCryptoDevice(cmd_parms *, void *, const char *);
 const char  *ssl_cmd_SSLRandomSeed(cmd_parms *, void *, const char *, const char *, const char *);
-const char  *ssl_cmd_SSLEngine(cmd_parms *, void *, int);
+const char  *ssl_cmd_SSLEngine(cmd_parms *, void *, const char *);
 const char  *ssl_cmd_SSLCipherSuite(cmd_parms *, void *, const char *);
 const char  *ssl_cmd_SSLCertificateFile(cmd_parms *, void *, const char *);
 const char  *ssl_cmd_SSLCertificateKeyFile(cmd_parms *, void *, const char *);
@@ -573,6 +583,7 @@
 int          ssl_hook_Access(request_rec *);
 int          ssl_hook_Fixup(request_rec *);
 int          ssl_hook_ReadReq(request_rec *);
+int          ssl_hook_Upgrade(request_rec *);
 
 /*  OpenSSL callbacks */
 RSA         *ssl_callback_TmpRSA(SSL *, int, int);
@@ -694,6 +705,8 @@
 char        *ssl_util_algotypestr(ssl_algo_t);
 char        *ssl_util_ptxtsub(apr_pool_t *, const char *, const char *, char *);
 void         ssl_util_thread_setup(apr_pool_t *);
+int          ssl_init_ssl_connection(conn_rec *c);
+
 
 #define APR_SHM_MAXSIZE (64 * 1024 * 1024)
 #endif /* __MOD_SSL_H__ */
 
