From e8e02c969b3b98817ab04f95040a0d7422a8ae95 Mon Sep 17 00:00:00 2001
From: Roel Standaert <roel@emweb.be>
Date: Mon, 10 Jan 2022 18:45:15 +0100
Subject: [PATCH] WT-9591: make Http::Request::urlScheme() look at
 X-Forwarded-Proto, just like WEnvironment

This could e.g. cause issues with SAML if TLS was being terminated by a
reverse proxy. Now, just like for clientAddress() and hostName(), we're
considering whether we're behind a reverse proxy and checking
X-Forwarded-Proto.
---
 ReleaseNotes.html     | 11 +++++++++++
 src/Wt/Http/Request.C |  6 +++++-
 src/Wt/Http/Request.h |  3 +++
 src/Wt/WEnvironment.C | 14 +-------------
 src/web/WebRequest.C  | 17 +++++++++++++++++
 src/web/WebRequest.h  |  2 ++
 6 files changed, 39 insertions(+), 14 deletions(-)

diff --git a/ReleaseNotes.html b/ReleaseNotes.html
index 693da1285..41d6bd760 100644
--- a/ReleaseNotes.html
+++ b/ReleaseNotes.html
@@ -20,6 +20,17 @@ newer version of Wt. It lists changes in the library that may break
 the way you build Wt, the way you configure Wt or the Wt API and
 behaviour.
 
+<h2>Release 4.7.0 (March 2022)</h2>
+
+<ul>
+  <li>
+    <a href="https://redmine.webtoolkit.eu/issues/9591" target="_blank">Issue #9591</a>:
+    <a href="https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1Http_1_1Request.html#a970b7d266c6bead893ec6334520807bf">
+      Http::Request::urlScheme()
+    </a> now looks at <code>X-Forwarded-Proto</code> if Wt is behind a trusted reverse proxy.
+  </li>
+</ul>
+
 <h2>Release 4.6.1 (December 23, 2021)</h2>
 
 <p>
diff --git a/src/Wt/Http/Request.C b/src/Wt/Http/Request.C
index e2cd7b7f7..87fb0f69b 100644
--- a/src/Wt/Http/Request.C
+++ b/src/Wt/Http/Request.C
@@ -169,7 +169,11 @@ std::string Request::queryString() const
 
 std::string Request::urlScheme() const
 {
-  return request_ ? request_->urlScheme() : std::string();
+  if (!request_)
+    return std::string();
+
+  WServer *server = WServer::instance();
+  return request_->urlScheme(server->configuration());
 }
 
 std::string Request::headerValue(const std::string& field) const
diff --git a/src/Wt/Http/Request.h b/src/Wt/Http/Request.h
index c2b81929b..d05572b28 100644
--- a/src/Wt/Http/Request.h
+++ b/src/Wt/Http/Request.h
@@ -296,6 +296,9 @@ public:
   /*! \brief Returns the url scheme used.
    *
    * This is either <tt>"http"</tt> or <tt>"https"</tt>
+   *
+   * If we're behind a trusted reverse proxy, the value of X-Forwarded-Proto
+   * will be used if it is present.
    */
   std::string urlScheme() const;
 
diff --git a/src/Wt/WEnvironment.C b/src/Wt/WEnvironment.C
index 52c83b044..ac9320fd7 100644
--- a/src/Wt/WEnvironment.C
+++ b/src/Wt/WEnvironment.C
@@ -95,21 +95,9 @@ void WEnvironment::updateHostName(const WebRequest& request)
 
 void WEnvironment::updateUrlScheme(const WebRequest& request) 
 {
-  urlScheme_       = str(request.urlScheme());
-
   Configuration& conf = session_->controller()->configuration();
 
-  if (conf.behindReverseProxy() ||
-      conf.isTrustedProxy(request.remoteAddr())) {
-    std::string forwardedProto = str(request.headerValue("X-Forwarded-Proto"));
-    if (!forwardedProto.empty()) {
-      std::string::size_type i = forwardedProto.rfind(',');
-      if (i == std::string::npos)
-        urlScheme_ = forwardedProto;
-      else
-        urlScheme_ = forwardedProto.substr(i+1);
-    }
-  }
+  urlScheme_ = request.urlScheme(conf);
 }
 
 
diff --git a/src/web/WebRequest.C b/src/web/WebRequest.C
index 3bc1006b5..2b3a4db19 100644
--- a/src/web/WebRequest.C
+++ b/src/web/WebRequest.C
@@ -443,4 +443,21 @@ std::string WebRequest::hostName(const Configuration &conf) const
   return host;
 }
 
+std::string WebRequest::urlScheme(const Configuration &conf) const
+{
+  if (conf.behindReverseProxy() ||
+      conf.isTrustedProxy(remoteAddr())) {
+    std::string forwardedProto = str(headerValue("X-Forwarded-Proto"));
+    if (!forwardedProto.empty()) {
+      std::string::size_type i = forwardedProto.rfind(',');
+      if (i == std::string::npos)
+        return forwardedProto;
+      else
+        return forwardedProto.substr(i+1);
+    }
+  }
+
+  return urlScheme();
+}
+
 }
diff --git a/src/web/WebRequest.h b/src/web/WebRequest.h
index 3a41c8686..f70e9fb3c 100644
--- a/src/web/WebRequest.h
+++ b/src/web/WebRequest.h
@@ -202,6 +202,8 @@ public:
 
   std::string hostName(const Configuration & conf) const;
 
+  std::string urlScheme(const Configuration & conf) const;
+
 protected:
   const EntryPoint *entryPoint_;
 
-- 
2.25.1

