2017-09-23 13:56:58 +08:00
|
|
|
// Copyright 2015 Light Code Labs, LLC
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2016-06-06 12:39:23 +08:00
|
|
|
package fastcgi
|
|
|
|
|
|
|
|
import (
|
httpserver/all: Clean up and standardize request URL handling (#1633)
* httpserver/all: Clean up and standardize request URL handling
The HTTP server now always creates a context value on the request which
is a copy of the request's URL struct. It should not be modified by
middlewares, but it is safe to get the value out of the request and make
changes to it locally-scoped. Thus, the value in the context always
stores the original request URL information as it was received. Any
rewrites that happen will be to the request's URL field directly.
The HTTP server no longer cleans /sanitizes the request URL. It made too
many strong assumptions and ended up making a lot of middleware more
complicated, including upstream proxying (and fastcgi). To alleviate
this complexity, we no longer change the request URL. Middlewares are
responsible to access the disk safely by using http.Dir or, if not
actually opening files, they can use httpserver.SafePath().
I'm hoping this will address issues with #1624, #1584, #1582, and others.
* staticfiles: Fix test on Windows
@abiosoft: I still can't figure out exactly what this is for. 😅
* Use (potentially) changed URL for browse redirects, as before
* Use filepath.ToSlash, clean up a couple proxy test cases
* Oops, fix variable name
2017-05-02 13:11:10 +08:00
|
|
|
"context"
|
2016-06-06 12:39:23 +08:00
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"net/http/fcgi"
|
|
|
|
"net/http/httptest"
|
|
|
|
"net/url"
|
2018-05-09 08:21:15 +08:00
|
|
|
"path/filepath"
|
2016-06-06 12:39:23 +08:00
|
|
|
"strconv"
|
2016-09-28 08:12:22 +08:00
|
|
|
"sync"
|
2016-06-06 12:39:23 +08:00
|
|
|
"testing"
|
2016-11-20 00:05:29 +08:00
|
|
|
"time"
|
httpserver/all: Clean up and standardize request URL handling (#1633)
* httpserver/all: Clean up and standardize request URL handling
The HTTP server now always creates a context value on the request which
is a copy of the request's URL struct. It should not be modified by
middlewares, but it is safe to get the value out of the request and make
changes to it locally-scoped. Thus, the value in the context always
stores the original request URL information as it was received. Any
rewrites that happen will be to the request's URL field directly.
The HTTP server no longer cleans /sanitizes the request URL. It made too
many strong assumptions and ended up making a lot of middleware more
complicated, including upstream proxying (and fastcgi). To alleviate
this complexity, we no longer change the request URL. Middlewares are
responsible to access the disk safely by using http.Dir or, if not
actually opening files, they can use httpserver.SafePath().
I'm hoping this will address issues with #1624, #1584, #1582, and others.
* staticfiles: Fix test on Windows
@abiosoft: I still can't figure out exactly what this is for. 😅
* Use (potentially) changed URL for browse redirects, as before
* Use filepath.ToSlash, clean up a couple proxy test cases
* Oops, fix variable name
2017-05-02 13:11:10 +08:00
|
|
|
|
2017-09-02 12:15:53 +08:00
|
|
|
"github.com/mholt/caddy"
|
httpserver/all: Clean up and standardize request URL handling (#1633)
* httpserver/all: Clean up and standardize request URL handling
The HTTP server now always creates a context value on the request which
is a copy of the request's URL struct. It should not be modified by
middlewares, but it is safe to get the value out of the request and make
changes to it locally-scoped. Thus, the value in the context always
stores the original request URL information as it was received. Any
rewrites that happen will be to the request's URL field directly.
The HTTP server no longer cleans /sanitizes the request URL. It made too
many strong assumptions and ended up making a lot of middleware more
complicated, including upstream proxying (and fastcgi). To alleviate
this complexity, we no longer change the request URL. Middlewares are
responsible to access the disk safely by using http.Dir or, if not
actually opening files, they can use httpserver.SafePath().
I'm hoping this will address issues with #1624, #1584, #1582, and others.
* staticfiles: Fix test on Windows
@abiosoft: I still can't figure out exactly what this is for. 😅
* Use (potentially) changed URL for browse redirects, as before
* Use filepath.ToSlash, clean up a couple proxy test cases
* Oops, fix variable name
2017-05-02 13:11:10 +08:00
|
|
|
"github.com/mholt/caddy/caddyhttp/httpserver"
|
2016-06-06 12:39:23 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestServeHTTP(t *testing.T) {
|
|
|
|
body := "This is some test body content"
|
|
|
|
|
|
|
|
bodyLenStr := strconv.Itoa(len(body))
|
|
|
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Unable to create listener for test: %v", err)
|
|
|
|
}
|
|
|
|
defer listener.Close()
|
|
|
|
go fcgi.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Header().Set("Content-Length", bodyLenStr)
|
|
|
|
w.Write([]byte(body))
|
|
|
|
}))
|
|
|
|
|
|
|
|
handler := Handler{
|
2017-07-19 02:52:53 +08:00
|
|
|
Next: nil,
|
|
|
|
Rules: []Rule{{Path: "/", balancer: address(listener.Addr().String())}},
|
2016-06-06 12:39:23 +08:00
|
|
|
}
|
|
|
|
r, err := http.NewRequest("GET", "/", nil)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Unable to create request: %v", err)
|
|
|
|
}
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
|
|
|
status, err := handler.ServeHTTP(w, r)
|
|
|
|
|
|
|
|
if got, want := status, 0; got != want {
|
|
|
|
t.Errorf("Expected returned status code to be %d, got %d", want, got)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Expected nil error, got: %v", err)
|
|
|
|
}
|
|
|
|
if got, want := w.Header().Get("Content-Length"), bodyLenStr; got != want {
|
|
|
|
t.Errorf("Expected Content-Length to be '%s', got: '%s'", want, got)
|
|
|
|
}
|
|
|
|
if got, want := w.Body.String(), body; got != want {
|
|
|
|
t.Errorf("Expected response body to be '%s', got: '%s'", want, got)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRuleParseAddress(t *testing.T) {
|
|
|
|
getClientTestTable := []struct {
|
|
|
|
rule *Rule
|
|
|
|
expectednetwork string
|
|
|
|
expectedaddress string
|
|
|
|
}{
|
2017-07-19 02:52:53 +08:00
|
|
|
{&Rule{balancer: address("tcp://172.17.0.1:9000")}, "tcp", "172.17.0.1:9000"},
|
|
|
|
{&Rule{balancer: address("fastcgi://localhost:9000")}, "tcp", "localhost:9000"},
|
|
|
|
{&Rule{balancer: address("172.17.0.15")}, "tcp", "172.17.0.15"},
|
|
|
|
{&Rule{balancer: address("/my/unix/socket")}, "unix", "/my/unix/socket"},
|
|
|
|
{&Rule{balancer: address("unix:/second/unix/socket")}, "unix", "/second/unix/socket"},
|
2016-06-06 12:39:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, entry := range getClientTestTable {
|
2017-10-03 21:17:54 +08:00
|
|
|
addr, err := entry.rule.Address()
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Unexpected error in retrieving address: %s", err.Error())
|
|
|
|
}
|
|
|
|
if actualnetwork, _ := parseAddress(addr); actualnetwork != entry.expectednetwork {
|
|
|
|
t.Errorf("Unexpected network for address string %v. Got %v, expected %v", addr, actualnetwork, entry.expectednetwork)
|
2016-06-06 12:39:23 +08:00
|
|
|
}
|
2017-10-03 21:17:54 +08:00
|
|
|
if _, actualaddress := parseAddress(addr); actualaddress != entry.expectedaddress {
|
|
|
|
t.Errorf("Unexpected parsed address for address string %v. Got %v, expected %v", addr, actualaddress, entry.expectedaddress)
|
2016-06-06 12:39:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRuleIgnoredPath(t *testing.T) {
|
|
|
|
rule := &Rule{
|
|
|
|
Path: "/fastcgi",
|
|
|
|
IgnoredSubPaths: []string{"/download", "/static"},
|
|
|
|
}
|
|
|
|
tests := []struct {
|
|
|
|
url string
|
|
|
|
expected bool
|
|
|
|
}{
|
|
|
|
{"/fastcgi", true},
|
|
|
|
{"/fastcgi/dl", true},
|
|
|
|
{"/fastcgi/download", false},
|
|
|
|
{"/fastcgi/download/static", false},
|
|
|
|
{"/fastcgi/static", false},
|
|
|
|
{"/fastcgi/static/download", false},
|
|
|
|
{"/fastcgi/something/download", true},
|
|
|
|
{"/fastcgi/something/static", true},
|
|
|
|
{"/fastcgi//static", false},
|
|
|
|
{"/fastcgi//static//download", false},
|
|
|
|
{"/fastcgi//download", false},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, test := range tests {
|
|
|
|
allowed := rule.AllowedPath(test.url)
|
|
|
|
if test.expected != allowed {
|
|
|
|
t.Errorf("Test %d: expected %v found %v", i, test.expected, allowed)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBuildEnv(t *testing.T) {
|
|
|
|
testBuildEnv := func(r *http.Request, rule Rule, fpath string, envExpected map[string]string) {
|
|
|
|
var h Handler
|
|
|
|
env, err := h.buildEnv(r, rule, fpath)
|
|
|
|
if err != nil {
|
|
|
|
t.Error("Unexpected error:", err.Error())
|
|
|
|
}
|
|
|
|
for k, v := range envExpected {
|
|
|
|
if env[k] != v {
|
|
|
|
t.Errorf("Unexpected %v. Got %v, expected %v", k, env[k], v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-02 12:15:53 +08:00
|
|
|
rule := Rule{
|
|
|
|
Ext: ".php",
|
|
|
|
SplitPath: ".php",
|
|
|
|
IndexFiles: []string{"index.php"},
|
|
|
|
}
|
httpserver/all: Clean up and standardize request URL handling (#1633)
* httpserver/all: Clean up and standardize request URL handling
The HTTP server now always creates a context value on the request which
is a copy of the request's URL struct. It should not be modified by
middlewares, but it is safe to get the value out of the request and make
changes to it locally-scoped. Thus, the value in the context always
stores the original request URL information as it was received. Any
rewrites that happen will be to the request's URL field directly.
The HTTP server no longer cleans /sanitizes the request URL. It made too
many strong assumptions and ended up making a lot of middleware more
complicated, including upstream proxying (and fastcgi). To alleviate
this complexity, we no longer change the request URL. Middlewares are
responsible to access the disk safely by using http.Dir or, if not
actually opening files, they can use httpserver.SafePath().
I'm hoping this will address issues with #1624, #1584, #1582, and others.
* staticfiles: Fix test on Windows
@abiosoft: I still can't figure out exactly what this is for. 😅
* Use (potentially) changed URL for browse redirects, as before
* Use filepath.ToSlash, clean up a couple proxy test cases
* Oops, fix variable name
2017-05-02 13:11:10 +08:00
|
|
|
url, err := url.Parse("http://localhost:2015/fgci_test.php?test=foobar")
|
2016-06-06 12:39:23 +08:00
|
|
|
if err != nil {
|
|
|
|
t.Error("Unexpected error:", err.Error())
|
|
|
|
}
|
|
|
|
|
2016-06-29 20:41:52 +08:00
|
|
|
var newReq = func() *http.Request {
|
httpserver/all: Clean up and standardize request URL handling (#1633)
* httpserver/all: Clean up and standardize request URL handling
The HTTP server now always creates a context value on the request which
is a copy of the request's URL struct. It should not be modified by
middlewares, but it is safe to get the value out of the request and make
changes to it locally-scoped. Thus, the value in the context always
stores the original request URL information as it was received. Any
rewrites that happen will be to the request's URL field directly.
The HTTP server no longer cleans /sanitizes the request URL. It made too
many strong assumptions and ended up making a lot of middleware more
complicated, including upstream proxying (and fastcgi). To alleviate
this complexity, we no longer change the request URL. Middlewares are
responsible to access the disk safely by using http.Dir or, if not
actually opening files, they can use httpserver.SafePath().
I'm hoping this will address issues with #1624, #1584, #1582, and others.
* staticfiles: Fix test on Windows
@abiosoft: I still can't figure out exactly what this is for. 😅
* Use (potentially) changed URL for browse redirects, as before
* Use filepath.ToSlash, clean up a couple proxy test cases
* Oops, fix variable name
2017-05-02 13:11:10 +08:00
|
|
|
r := http.Request{
|
2016-06-29 20:41:52 +08:00
|
|
|
Method: "GET",
|
|
|
|
URL: url,
|
|
|
|
Proto: "HTTP/1.1",
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 1,
|
|
|
|
Host: "localhost:2015",
|
|
|
|
RemoteAddr: "[2b02:1810:4f2d:9400:70ab:f822:be8a:9093]:51688",
|
|
|
|
RequestURI: "/fgci_test.php",
|
2016-10-17 02:11:52 +08:00
|
|
|
Header: map[string][]string{
|
|
|
|
"Foo": {"Bar", "two"},
|
|
|
|
},
|
2016-06-29 20:41:52 +08:00
|
|
|
}
|
httpserver/all: Clean up and standardize request URL handling (#1633)
* httpserver/all: Clean up and standardize request URL handling
The HTTP server now always creates a context value on the request which
is a copy of the request's URL struct. It should not be modified by
middlewares, but it is safe to get the value out of the request and make
changes to it locally-scoped. Thus, the value in the context always
stores the original request URL information as it was received. Any
rewrites that happen will be to the request's URL field directly.
The HTTP server no longer cleans /sanitizes the request URL. It made too
many strong assumptions and ended up making a lot of middleware more
complicated, including upstream proxying (and fastcgi). To alleviate
this complexity, we no longer change the request URL. Middlewares are
responsible to access the disk safely by using http.Dir or, if not
actually opening files, they can use httpserver.SafePath().
I'm hoping this will address issues with #1624, #1584, #1582, and others.
* staticfiles: Fix test on Windows
@abiosoft: I still can't figure out exactly what this is for. 😅
* Use (potentially) changed URL for browse redirects, as before
* Use filepath.ToSlash, clean up a couple proxy test cases
* Oops, fix variable name
2017-05-02 13:11:10 +08:00
|
|
|
ctx := context.WithValue(r.Context(), httpserver.OriginalURLCtxKey, *r.URL)
|
|
|
|
return r.WithContext(ctx)
|
2016-06-06 12:39:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
fpath := "/fgci_test.php"
|
|
|
|
|
2016-06-29 20:41:52 +08:00
|
|
|
var newEnv = func() map[string]string {
|
|
|
|
return map[string]string{
|
|
|
|
"REMOTE_ADDR": "2b02:1810:4f2d:9400:70ab:f822:be8a:9093",
|
|
|
|
"REMOTE_PORT": "51688",
|
|
|
|
"SERVER_PROTOCOL": "HTTP/1.1",
|
httpserver/all: Clean up and standardize request URL handling (#1633)
* httpserver/all: Clean up and standardize request URL handling
The HTTP server now always creates a context value on the request which
is a copy of the request's URL struct. It should not be modified by
middlewares, but it is safe to get the value out of the request and make
changes to it locally-scoped. Thus, the value in the context always
stores the original request URL information as it was received. Any
rewrites that happen will be to the request's URL field directly.
The HTTP server no longer cleans /sanitizes the request URL. It made too
many strong assumptions and ended up making a lot of middleware more
complicated, including upstream proxying (and fastcgi). To alleviate
this complexity, we no longer change the request URL. Middlewares are
responsible to access the disk safely by using http.Dir or, if not
actually opening files, they can use httpserver.SafePath().
I'm hoping this will address issues with #1624, #1584, #1582, and others.
* staticfiles: Fix test on Windows
@abiosoft: I still can't figure out exactly what this is for. 😅
* Use (potentially) changed URL for browse redirects, as before
* Use filepath.ToSlash, clean up a couple proxy test cases
* Oops, fix variable name
2017-05-02 13:11:10 +08:00
|
|
|
"QUERY_STRING": "test=foobar",
|
2016-06-29 20:41:52 +08:00
|
|
|
"REQUEST_METHOD": "GET",
|
|
|
|
"HTTP_HOST": "localhost:2015",
|
2017-09-02 12:15:53 +08:00
|
|
|
"SCRIPT_NAME": "/fgci_test.php",
|
2016-06-29 20:41:52 +08:00
|
|
|
}
|
2016-06-06 12:39:23 +08:00
|
|
|
}
|
|
|
|
|
2016-06-29 20:41:52 +08:00
|
|
|
// request
|
|
|
|
var r *http.Request
|
|
|
|
|
|
|
|
// expected environment variables
|
|
|
|
var envExpected map[string]string
|
|
|
|
|
2016-06-06 12:39:23 +08:00
|
|
|
// 1. Test for full canonical IPv6 address
|
2016-06-29 20:41:52 +08:00
|
|
|
r = newReq()
|
|
|
|
testBuildEnv(r, rule, fpath, envExpected)
|
2016-06-06 12:39:23 +08:00
|
|
|
|
|
|
|
// 2. Test for shorthand notation of IPv6 address
|
2016-06-29 20:41:52 +08:00
|
|
|
r = newReq()
|
2016-06-06 12:39:23 +08:00
|
|
|
r.RemoteAddr = "[::1]:51688"
|
2016-06-29 20:41:52 +08:00
|
|
|
envExpected = newEnv()
|
2016-06-06 12:39:23 +08:00
|
|
|
envExpected["REMOTE_ADDR"] = "::1"
|
2016-06-29 20:41:52 +08:00
|
|
|
testBuildEnv(r, rule, fpath, envExpected)
|
2016-06-06 12:39:23 +08:00
|
|
|
|
|
|
|
// 3. Test for IPv4 address
|
2016-06-29 20:41:52 +08:00
|
|
|
r = newReq()
|
2016-06-06 12:39:23 +08:00
|
|
|
r.RemoteAddr = "192.168.0.10:51688"
|
2016-06-29 20:41:52 +08:00
|
|
|
envExpected = newEnv()
|
2016-06-06 12:39:23 +08:00
|
|
|
envExpected["REMOTE_ADDR"] = "192.168.0.10"
|
2016-06-29 20:41:52 +08:00
|
|
|
testBuildEnv(r, rule, fpath, envExpected)
|
|
|
|
|
|
|
|
// 4. Test for environment variable
|
|
|
|
r = newReq()
|
|
|
|
rule.EnvVars = [][2]string{
|
|
|
|
{"HTTP_HOST", "localhost:2016"},
|
|
|
|
{"REQUEST_METHOD", "POST"},
|
|
|
|
}
|
|
|
|
envExpected = newEnv()
|
|
|
|
envExpected["HTTP_HOST"] = "localhost:2016"
|
|
|
|
envExpected["REQUEST_METHOD"] = "POST"
|
|
|
|
testBuildEnv(r, rule, fpath, envExpected)
|
|
|
|
|
|
|
|
// 5. Test for environment variable placeholders
|
|
|
|
r = newReq()
|
|
|
|
rule.EnvVars = [][2]string{
|
|
|
|
{"HTTP_HOST", "{host}"},
|
|
|
|
{"CUSTOM_URI", "custom_uri{uri}"},
|
|
|
|
{"CUSTOM_QUERY", "custom=true&{query}"},
|
|
|
|
}
|
|
|
|
envExpected = newEnv()
|
|
|
|
envExpected["HTTP_HOST"] = "localhost:2015"
|
httpserver/all: Clean up and standardize request URL handling (#1633)
* httpserver/all: Clean up and standardize request URL handling
The HTTP server now always creates a context value on the request which
is a copy of the request's URL struct. It should not be modified by
middlewares, but it is safe to get the value out of the request and make
changes to it locally-scoped. Thus, the value in the context always
stores the original request URL information as it was received. Any
rewrites that happen will be to the request's URL field directly.
The HTTP server no longer cleans /sanitizes the request URL. It made too
many strong assumptions and ended up making a lot of middleware more
complicated, including upstream proxying (and fastcgi). To alleviate
this complexity, we no longer change the request URL. Middlewares are
responsible to access the disk safely by using http.Dir or, if not
actually opening files, they can use httpserver.SafePath().
I'm hoping this will address issues with #1624, #1584, #1582, and others.
* staticfiles: Fix test on Windows
@abiosoft: I still can't figure out exactly what this is for. 😅
* Use (potentially) changed URL for browse redirects, as before
* Use filepath.ToSlash, clean up a couple proxy test cases
* Oops, fix variable name
2017-05-02 13:11:10 +08:00
|
|
|
envExpected["CUSTOM_URI"] = "custom_uri/fgci_test.php?test=foobar"
|
|
|
|
envExpected["CUSTOM_QUERY"] = "custom=true&test=foobar"
|
2016-06-29 20:41:52 +08:00
|
|
|
testBuildEnv(r, rule, fpath, envExpected)
|
2017-09-02 12:15:53 +08:00
|
|
|
|
|
|
|
// 6. Test SCRIPT_NAME includes path prefix
|
|
|
|
r = newReq()
|
|
|
|
ctx := context.WithValue(r.Context(), caddy.CtxKey("path_prefix"), "/test")
|
|
|
|
r = r.WithContext(ctx)
|
|
|
|
envExpected = newEnv()
|
|
|
|
envExpected["SCRIPT_NAME"] = "/test/fgci_test.php"
|
|
|
|
testBuildEnv(r, rule, fpath, envExpected)
|
2018-05-09 08:21:15 +08:00
|
|
|
|
|
|
|
// 7. Test SCRIPT_NAME,SCRIPT_FILENAME do not include PATH_INFO
|
|
|
|
fpath = "/fgci_test.php/extra/paths"
|
|
|
|
r = newReq()
|
|
|
|
envExpected = newEnv()
|
|
|
|
envExpected["PATH_INFO"] = "/extra/paths"
|
|
|
|
envExpected["SCRIPT_NAME"] = "/fgci_test.php"
|
|
|
|
envExpected["SCRIPT_FILENAME"] = filepath.FromSlash("/fgci_test.php")
|
|
|
|
testBuildEnv(r, rule, fpath, envExpected)
|
2018-05-17 08:12:26 +08:00
|
|
|
|
|
|
|
// 8. Test REQUEST_SCHEME in env
|
|
|
|
r = newReq()
|
|
|
|
envExpected = newEnv()
|
|
|
|
envExpected["REQUEST_SCHEME"] = "http"
|
|
|
|
testBuildEnv(r, rule, fpath, envExpected)
|
2016-06-06 12:39:23 +08:00
|
|
|
}
|
2016-11-20 00:05:29 +08:00
|
|
|
|
|
|
|
func TestReadTimeout(t *testing.T) {
|
2016-11-30 10:26:51 +08:00
|
|
|
tests := []struct {
|
|
|
|
sleep time.Duration
|
|
|
|
readTimeout time.Duration
|
|
|
|
shouldErr bool
|
|
|
|
}{
|
|
|
|
{75 * time.Millisecond, 50 * time.Millisecond, true},
|
|
|
|
{0, -1 * time.Second, true},
|
|
|
|
{0, time.Minute, false},
|
2016-11-20 00:05:29 +08:00
|
|
|
}
|
|
|
|
|
2016-11-30 10:26:51 +08:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
|
|
|
for i, test := range tests {
|
|
|
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Test %d: Unable to create listener for test: %v", i, err)
|
|
|
|
}
|
|
|
|
defer listener.Close()
|
|
|
|
|
|
|
|
handler := Handler{
|
|
|
|
Next: nil,
|
|
|
|
Rules: []Rule{
|
|
|
|
{
|
|
|
|
Path: "/",
|
2017-07-19 02:52:53 +08:00
|
|
|
balancer: address(listener.Addr().String()),
|
2016-11-30 10:26:51 +08:00
|
|
|
ReadTimeout: test.readTimeout,
|
|
|
|
},
|
2016-11-20 00:05:29 +08:00
|
|
|
},
|
2016-11-30 10:26:51 +08:00
|
|
|
}
|
|
|
|
r, err := http.NewRequest("GET", "/", nil)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Test %d: Unable to create request: %v", i, err)
|
|
|
|
}
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
|
|
|
wg.Add(1)
|
|
|
|
go fcgi.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
time.Sleep(test.sleep)
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
wg.Done()
|
|
|
|
}))
|
|
|
|
|
|
|
|
got, err := handler.ServeHTTP(w, r)
|
|
|
|
if test.shouldErr {
|
|
|
|
if err == nil {
|
|
|
|
t.Errorf("Test %d: Expected i/o timeout error but had none", i)
|
|
|
|
} else if err, ok := err.(net.Error); !ok || !err.Timeout() {
|
|
|
|
t.Errorf("Test %d: Expected i/o timeout error, got: '%s'", i, err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
want := http.StatusGatewayTimeout
|
|
|
|
if got != want {
|
|
|
|
t.Errorf("Test %d: Expected returned status code to be %d, got: %d",
|
|
|
|
i, want, got)
|
|
|
|
}
|
|
|
|
} else if err != nil {
|
|
|
|
t.Errorf("Test %d: Expected nil error, got: %v", i, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
wg.Wait()
|
2016-11-20 00:05:29 +08:00
|
|
|
}
|
2016-11-30 10:26:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestSendTimeout(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
sendTimeout time.Duration
|
|
|
|
shouldErr bool
|
|
|
|
}{
|
|
|
|
{-1 * time.Second, true},
|
|
|
|
{time.Minute, false},
|
2016-11-20 00:05:29 +08:00
|
|
|
}
|
|
|
|
|
2016-11-30 10:26:51 +08:00
|
|
|
for i, test := range tests {
|
|
|
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Test %d: Unable to create listener for test: %v", i, err)
|
|
|
|
}
|
|
|
|
defer listener.Close()
|
2016-11-26 00:30:51 +08:00
|
|
|
|
2016-11-30 10:26:51 +08:00
|
|
|
handler := Handler{
|
|
|
|
Next: nil,
|
|
|
|
Rules: []Rule{
|
|
|
|
{
|
|
|
|
Path: "/",
|
2017-07-19 02:52:53 +08:00
|
|
|
balancer: address(listener.Addr().String()),
|
2016-11-30 10:26:51 +08:00
|
|
|
SendTimeout: test.sendTimeout,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
r, err := http.NewRequest("GET", "/", nil)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Test %d: Unable to create request: %v", i, err)
|
|
|
|
}
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
|
|
|
go fcgi.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
}))
|
|
|
|
|
|
|
|
got, err := handler.ServeHTTP(w, r)
|
|
|
|
if test.shouldErr {
|
|
|
|
if err == nil {
|
|
|
|
t.Errorf("Test %d: Expected i/o timeout error but had none", i)
|
|
|
|
} else if err, ok := err.(net.Error); !ok || !err.Timeout() {
|
|
|
|
t.Errorf("Test %d: Expected i/o timeout error, got: '%s'", i, err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
want := http.StatusGatewayTimeout
|
|
|
|
if got != want {
|
|
|
|
t.Errorf("Test %d: Expected returned status code to be %d, got: %d",
|
|
|
|
i, want, got)
|
|
|
|
}
|
|
|
|
} else if err != nil {
|
|
|
|
t.Errorf("Test %d: Expected nil error, got: %v", i, err)
|
|
|
|
}
|
2016-11-20 00:05:29 +08:00
|
|
|
}
|
|
|
|
}
|
2017-07-19 02:52:53 +08:00
|
|
|
|
|
|
|
func TestBalancer(t *testing.T) {
|
|
|
|
tests := [][]string{
|
|
|
|
{"localhost", "host.local"},
|
|
|
|
{"localhost"},
|
|
|
|
{"localhost", "host.local", "example.com"},
|
|
|
|
{"localhost", "host.local", "example.com", "127.0.0.1"},
|
|
|
|
}
|
|
|
|
for i, test := range tests {
|
|
|
|
b := address(test...)
|
|
|
|
for _, host := range test {
|
2017-10-03 21:17:54 +08:00
|
|
|
a, err := b.Address()
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Unexpected error in trying to retrieve address: %s", err.Error())
|
|
|
|
}
|
2017-07-19 02:52:53 +08:00
|
|
|
if a != host {
|
|
|
|
t.Errorf("Test %d: expected %s, found %s", i, host, a)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func address(addresses ...string) balancer {
|
|
|
|
return &roundRobin{
|
|
|
|
addresses: addresses,
|
|
|
|
index: -1,
|
|
|
|
}
|
|
|
|
}
|