diff options
-rw-r--r-- | ext/unicorn_http/global_variables.h | 4 | ||||
-rw-r--r-- | ext/unicorn_http/unicorn_http.rl | 28 | ||||
-rw-r--r-- | test/unit/test_http_parser.rb | 55 |
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 = {} |