about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--ext/unicorn_http/global_variables.h4
-rw-r--r--ext/unicorn_http/unicorn_http.rl28
-rw-r--r--test/unit/test_http_parser.rb55
3 files changed, 82 insertions, 5 deletions
diff --git a/ext/unicorn_http/global_variables.h b/ext/unicorn_http/global_variables.h
index 274f456..cdbc42d 100644
--- a/ext/unicorn_http/global_variables.h
+++ b/ext/unicorn_http/global_variables.h
@@ -15,6 +15,7 @@ static VALUE g_server_port;
 static VALUE g_server_protocol;
 static VALUE g_http_host;
 static VALUE g_http_x_forwarded_proto;
+static VALUE g_http_x_forwarded_ssl;
 static VALUE g_http_transfer_encoding;
 static VALUE g_content_length;
 static VALUE g_http_trailer;
@@ -23,6 +24,7 @@ static VALUE g_port_80;
 static VALUE g_port_443;
 static VALUE g_localhost;
 static VALUE g_http;
+static VALUE g_https;
 static VALUE g_http_09;
 static VALUE g_http_10;
 static VALUE g_http_11;
@@ -73,10 +75,12 @@ static void init_globals(void)
   DEF_GLOBAL(server_port, "SERVER_PORT");
   DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
   DEF_GLOBAL(http_x_forwarded_proto, "HTTP_X_FORWARDED_PROTO");
+  DEF_GLOBAL(http_x_forwarded_ssl, "HTTP_X_FORWARDED_SSL");
   DEF_GLOBAL(port_80, "80");
   DEF_GLOBAL(port_443, "443");
   DEF_GLOBAL(localhost, "localhost");
   DEF_GLOBAL(http, "http");
+  DEF_GLOBAL(https, "https");
   DEF_GLOBAL(http_11, "HTTP/1.1");
   DEF_GLOBAL(http_10, "HTTP/1.0");
   DEF_GLOBAL(http_09, "HTTP/0.9");
diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl
index ba7ecb3..05e88bc 100644
--- a/ext/unicorn_http/unicorn_http.rl
+++ b/ext/unicorn_http/unicorn_http.rl
@@ -422,13 +422,31 @@ static void finalize_header(struct http_parser *hp)
   VALUE server_name = g_localhost;
   VALUE server_port = g_port_80;
 
-  /* set rack.url_scheme to "https" or "http", no others are allowed by Rack */
+  /*
+   * set rack.url_scheme to "https" or "http", no others are allowed by Rack
+   * this resembles the Rack::Request#scheme method as of rack commit
+   * 35bb5ba6746b5d346de9202c004cc926039650c7
+   */
   if (NIL_P(temp)) {
-    temp = rb_hash_aref(hp->env, g_http_x_forwarded_proto);
-    if (!NIL_P(temp) && STR_CSTR_EQ(temp, "https"))
+    temp = rb_hash_aref(hp->env, g_http_x_forwarded_ssl);
+    if (!NIL_P(temp) && STR_CSTR_EQ(temp, "on")) {
       server_port = g_port_443;
-    else
-      temp = g_http;
+      temp = g_https;
+    } else {
+      temp = rb_hash_aref(hp->env, g_http_x_forwarded_proto);
+      if (NIL_P(temp)) {
+        temp = g_http;
+      } else {
+        long len = RSTRING_LEN(temp);
+        if (len >= 5 && !memcmp(RSTRING_PTR(temp), "https", 5)) {
+          if (len != 5)
+            temp = g_https;
+          server_port = g_port_443;
+        } else {
+          temp = g_http;
+        }
+      }
+    }
     rb_hash_aset(hp->env, g_rack_url_scheme, temp);
   } else if (STR_CSTR_EQ(temp, "https")) {
     server_port = g_port_443;
diff --git a/test/unit/test_http_parser.rb b/test/unit/test_http_parser.rb
index 31cb2cb..a11740d 100644
--- a/test/unit/test_http_parser.rb
+++ b/test/unit/test_http_parser.rb
@@ -147,6 +147,61 @@ class HttpParserTest < Test::Unit::TestCase
     assert parser.keepalive?
   end
 
+  def test_parse_xfp_https_chained
+    parser = HttpParser.new
+    req = {}
+    tmp = "GET / HTTP/1.0\r\n" \
+          "X-Forwarded-Proto: https,http\r\n\r\n"
+    assert_equal req, parser.headers(req, tmp)
+    assert_equal '443', req['SERVER_PORT'], req.inspect
+    assert_equal 'https', req['rack.url_scheme'], req.inspect
+    assert_equal '', tmp
+  end
+
+  def test_parse_xfp_https_chained_backwards
+    parser = HttpParser.new
+    req = {}
+    tmp = "GET / HTTP/1.0\r\n" \
+          "X-Forwarded-Proto: http,https\r\n\r\n"
+    assert_equal req, parser.headers(req, tmp)
+    assert_equal '80', req['SERVER_PORT'], req.inspect
+    assert_equal 'http', req['rack.url_scheme'], req.inspect
+    assert_equal '', tmp
+  end
+
+  def test_parse_xfp_gopher_is_ignored
+    parser = HttpParser.new
+    req = {}
+    tmp = "GET / HTTP/1.0\r\n" \
+          "X-Forwarded-Proto: gopher\r\n\r\n"
+    assert_equal req, parser.headers(req, tmp)
+    assert_equal '80', req['SERVER_PORT'], req.inspect
+    assert_equal 'http', req['rack.url_scheme'], req.inspect
+    assert_equal '', tmp
+  end
+
+  def test_parse_x_forwarded_ssl_on
+    parser = HttpParser.new
+    req = {}
+    tmp = "GET / HTTP/1.0\r\n" \
+          "X-Forwarded-Ssl: on\r\n\r\n"
+    assert_equal req, parser.headers(req, tmp)
+    assert_equal '443', req['SERVER_PORT'], req.inspect
+    assert_equal 'https', req['rack.url_scheme'], req.inspect
+    assert_equal '', tmp
+  end
+
+  def test_parse_x_forwarded_ssl_off
+    parser = HttpParser.new
+    req = {}
+    tmp = "GET / HTTP/1.0\r\n" \
+          "X-Forwarded-Ssl: off\r\n\r\n"
+    assert_equal req, parser.headers(req, tmp)
+    assert_equal '80', req['SERVER_PORT'], req.inspect
+    assert_equal 'http', req['rack.url_scheme'], req.inspect
+    assert_equal '', tmp
+  end
+
   def test_parse_strange_headers
     parser = HttpParser.new
     req = {}