about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2009-04-08 17:42:23 -0700
committerEric Wong <normalperson@yhbt.net>2009-04-08 17:42:23 -0700
commite26ebc985b882c38da50fb0104791a5f2c0f8522 (patch)
treeaed3ecdde023b3809c7ee9de6c8e8ed63fe1f8b0
parent9625ad39b73d3d1443ff097e9113d1ec9c9d5f00 (diff)
downloadunicorn-e26ebc985b882c38da50fb0104791a5f2c0f8522.tar.gz
Pass "https" to "rack.url_scheme" if the X-Forwarded-Proto
header matches "https".  X-Forwarded-Proto is a semi-standard
header that Ruby frameworks seem to respect; so we use that.

We won't support ENV['HTTPS'] since that can only be set at
start time and some app servers supporting https also support
http.

Currently, "rack.url_scheme" only allows "http" and "https",
so we won't set anything else to avoid breaking Rack::Lint.
-rw-r--r--ext/unicorn/http11/http11.c16
-rw-r--r--lib/unicorn/http_request.rb1
-rw-r--r--test/unit/test_request.rb21
3 files changed, 37 insertions, 1 deletions
diff --git a/ext/unicorn/http11/http11.c b/ext/unicorn/http11/http11.c
index 995bf2a..a262d18 100644
--- a/ext/unicorn/http11/http11.c
+++ b/ext/unicorn/http11/http11.c
@@ -24,6 +24,7 @@ static VALUE sym_http_body;
 #define HTTP_PREFIX "HTTP_"
 #define HTTP_PREFIX_LEN (sizeof(HTTP_PREFIX) - 1)
 
+static VALUE global_rack_url_scheme;
 static VALUE global_request_method;
 static VALUE global_request_uri;
 static VALUE global_fragment;
@@ -37,6 +38,7 @@ static VALUE global_server_port;
 static VALUE global_server_protocol;
 static VALUE global_server_protocol_value;
 static VALUE global_http_host;
+static VALUE global_http_x_forwarded_proto;
 static VALUE global_port_80;
 static VALUE global_localhost;
 
@@ -106,6 +108,7 @@ static struct common_field common_http_fields[] = {
         f("USER_AGENT"),
         f("VIA"),
         f("X_FORWARDED_FOR"), /* common for proxies */
+        f("X_FORWARDED_PROTO"), /* common for proxies */
         f("X_REAL_IP"), /* common for proxies */
         f("WARNING")
 # undef f
@@ -248,6 +251,17 @@ static void header_done(void *data, const char *at, size_t length)
   VALUE temp = Qnil;
   char *colon = NULL;
 
+  /* set rack.url_scheme to "https" or "http" */
+  if ((temp = rb_hash_aref(req, global_http_x_forwarded_proto)) != Qnil) {
+    if (strcmp("https", RSTRING_PTR(temp)))
+      temp = rb_str_new("http", 4);
+    /* we leave temp alone if it's "https" */
+  } else {
+    temp = rb_str_new("http", 4);
+  }
+  rb_hash_aset(req, global_rack_url_scheme, temp);
+
+  /* set the SERVER_NAME and SERVER_PORT variables */
   if((temp = rb_hash_aref(req, global_http_host)) != Qnil) {
     colon = memchr(RSTRING_PTR(temp), ':', RSTRING_LEN(temp));
     if(colon != NULL) {
@@ -372,6 +386,7 @@ void Init_http11()
 
   mUnicorn = rb_define_module("Unicorn");
 
+  DEF_GLOBAL(rack_url_scheme, "rack.url_scheme");
   DEF_GLOBAL(request_method, "REQUEST_METHOD");
   DEF_GLOBAL(request_uri, "REQUEST_URI");
   DEF_GLOBAL(fragment, "FRAGMENT");
@@ -385,6 +400,7 @@ void Init_http11()
   DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
   DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
   DEF_GLOBAL(http_host, "HTTP_HOST");
+  DEF_GLOBAL(http_x_forwarded_proto, "HTTP_X_FORWARDED_PROTO");
   DEF_GLOBAL(port_80, "80");
   DEF_GLOBAL(localhost, "localhost");
 
diff --git a/lib/unicorn/http_request.rb b/lib/unicorn/http_request.rb
index 750deea..a3a1d4d 100644
--- a/lib/unicorn/http_request.rb
+++ b/lib/unicorn/http_request.rb
@@ -19,7 +19,6 @@ module Unicorn
        "rack.multiprocess" => true,
        "rack.multithread" => false,
        "rack.run_once" => false,
-       "rack.url_scheme" => "http",
        "rack.version" => [0, 1],
        "SCRIPT_NAME" => "",
 
diff --git a/test/unit/test_request.rb b/test/unit/test_request.rb
index dacbac0..5109c7b 100644
--- a/test/unit/test_request.rb
+++ b/test/unit/test_request.rb
@@ -55,10 +55,31 @@ class RequestTest < Test::Unit::TestCase
     assert_nothing_raised { res = @lint.call(env) }
   end
 
+  def test_x_forwarded_proto
+    res = env = nil
+    client = MockRequest.new("GET / HTTP/1.1\r\n" \
+                             "X-Forwarded-Proto: https\r\n" \
+                             "Host: foo\r\n\r\n")
+    assert_nothing_raised { env = @request.read(client) }
+    assert_equal "https", env['rack.url_scheme']
+    assert_nothing_raised { res = @lint.call(env) }
+  end
+
+  def test_x_forwarded_proto_invalid
+    res = env = nil
+    client = MockRequest.new("GET / HTTP/1.1\r\n" \
+                             "X-Forwarded-Proto: ftp\r\n" \
+                             "Host: foo\r\n\r\n")
+    assert_nothing_raised { env = @request.read(client) }
+    assert_equal "http", env['rack.url_scheme']
+    assert_nothing_raised { res = @lint.call(env) }
+  end
+
   def test_rack_lint_get
     client = MockRequest.new("GET / HTTP/1.1\r\nHost: foo\r\n\r\n")
     res = env = nil
     assert_nothing_raised { env = @request.read(client) }
+    assert_equal "http", env['rack.url_scheme']
     assert_equal '666.666.666.666', env['REMOTE_ADDR']
     assert_nothing_raised { res = @lint.call(env) }
   end