mirror of
https://github.com/autc04/Retro68.git
synced 2024-12-11 19:49:32 +00:00
481 lines
11 KiB
Go
481 lines
11 KiB
Go
// Copyright 2011 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Tests for package cgi
|
|
|
|
package cgi
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func newRequest(httpreq string) *http.Request {
|
|
buf := bufio.NewReader(strings.NewReader(httpreq))
|
|
req, err := http.ReadRequest(buf)
|
|
if err != nil {
|
|
panic("cgi: bogus http request in test: " + httpreq)
|
|
}
|
|
req.RemoteAddr = "1.2.3.4"
|
|
return req
|
|
}
|
|
|
|
func runCgiTest(t *testing.T, h *Handler, httpreq string, expectedMap map[string]string) *httptest.ResponseRecorder {
|
|
rw := httptest.NewRecorder()
|
|
req := newRequest(httpreq)
|
|
h.ServeHTTP(rw, req)
|
|
|
|
// Make a map to hold the test map that the CGI returns.
|
|
m := make(map[string]string)
|
|
m["_body"] = rw.Body.String()
|
|
linesRead := 0
|
|
readlines:
|
|
for {
|
|
line, err := rw.Body.ReadString('\n')
|
|
switch {
|
|
case err == io.EOF:
|
|
break readlines
|
|
case err != nil:
|
|
t.Fatalf("unexpected error reading from CGI: %v", err)
|
|
}
|
|
linesRead++
|
|
trimmedLine := strings.TrimRight(line, "\r\n")
|
|
split := strings.SplitN(trimmedLine, "=", 2)
|
|
if len(split) != 2 {
|
|
t.Fatalf("Unexpected %d parts from invalid line number %v: %q; existing map=%v",
|
|
len(split), linesRead, line, m)
|
|
}
|
|
m[split[0]] = split[1]
|
|
}
|
|
|
|
for key, expected := range expectedMap {
|
|
if got := m[key]; got != expected {
|
|
t.Errorf("for key %q got %q; expected %q", key, got, expected)
|
|
}
|
|
}
|
|
return rw
|
|
}
|
|
|
|
var cgiTested = false
|
|
var cgiWorks bool
|
|
|
|
func skipTest(t *testing.T) bool {
|
|
if !cgiTested {
|
|
cgiTested = true
|
|
cgiWorks = exec.Command("./testdata/test.cgi").Run() == nil
|
|
}
|
|
if !cgiWorks {
|
|
// No Perl on Windows, needed by test.cgi
|
|
// TODO: make the child process be Go, not Perl.
|
|
t.Logf("Skipping test: test.cgi failed.")
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func TestCGIBasicGet(t *testing.T) {
|
|
if skipTest(t) {
|
|
return
|
|
}
|
|
h := &Handler{
|
|
Path: "testdata/test.cgi",
|
|
Root: "/test.cgi",
|
|
}
|
|
expectedMap := map[string]string{
|
|
"test": "Hello CGI",
|
|
"param-a": "b",
|
|
"param-foo": "bar",
|
|
"env-GATEWAY_INTERFACE": "CGI/1.1",
|
|
"env-HTTP_HOST": "example.com",
|
|
"env-PATH_INFO": "",
|
|
"env-QUERY_STRING": "foo=bar&a=b",
|
|
"env-REMOTE_ADDR": "1.2.3.4",
|
|
"env-REMOTE_HOST": "1.2.3.4",
|
|
"env-REQUEST_METHOD": "GET",
|
|
"env-REQUEST_URI": "/test.cgi?foo=bar&a=b",
|
|
"env-SCRIPT_FILENAME": "testdata/test.cgi",
|
|
"env-SCRIPT_NAME": "/test.cgi",
|
|
"env-SERVER_NAME": "example.com",
|
|
"env-SERVER_PORT": "80",
|
|
"env-SERVER_SOFTWARE": "go",
|
|
}
|
|
replay := runCgiTest(t, h, "GET /test.cgi?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
|
|
|
|
if expected, got := "text/html", replay.Header().Get("Content-Type"); got != expected {
|
|
t.Errorf("got a Content-Type of %q; expected %q", got, expected)
|
|
}
|
|
if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
|
|
t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
|
|
}
|
|
}
|
|
|
|
func TestCGIBasicGetAbsPath(t *testing.T) {
|
|
if skipTest(t) {
|
|
return
|
|
}
|
|
pwd, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatalf("getwd error: %v", err)
|
|
}
|
|
h := &Handler{
|
|
Path: pwd + "/testdata/test.cgi",
|
|
Root: "/test.cgi",
|
|
}
|
|
expectedMap := map[string]string{
|
|
"env-REQUEST_URI": "/test.cgi?foo=bar&a=b",
|
|
"env-SCRIPT_FILENAME": pwd + "/testdata/test.cgi",
|
|
"env-SCRIPT_NAME": "/test.cgi",
|
|
}
|
|
runCgiTest(t, h, "GET /test.cgi?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
|
|
}
|
|
|
|
func TestPathInfo(t *testing.T) {
|
|
if skipTest(t) {
|
|
return
|
|
}
|
|
h := &Handler{
|
|
Path: "testdata/test.cgi",
|
|
Root: "/test.cgi",
|
|
}
|
|
expectedMap := map[string]string{
|
|
"param-a": "b",
|
|
"env-PATH_INFO": "/extrapath",
|
|
"env-QUERY_STRING": "a=b",
|
|
"env-REQUEST_URI": "/test.cgi/extrapath?a=b",
|
|
"env-SCRIPT_FILENAME": "testdata/test.cgi",
|
|
"env-SCRIPT_NAME": "/test.cgi",
|
|
}
|
|
runCgiTest(t, h, "GET /test.cgi/extrapath?a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
|
|
}
|
|
|
|
func TestPathInfoDirRoot(t *testing.T) {
|
|
if skipTest(t) {
|
|
return
|
|
}
|
|
h := &Handler{
|
|
Path: "testdata/test.cgi",
|
|
Root: "/myscript/",
|
|
}
|
|
expectedMap := map[string]string{
|
|
"env-PATH_INFO": "bar",
|
|
"env-QUERY_STRING": "a=b",
|
|
"env-REQUEST_URI": "/myscript/bar?a=b",
|
|
"env-SCRIPT_FILENAME": "testdata/test.cgi",
|
|
"env-SCRIPT_NAME": "/myscript/",
|
|
}
|
|
runCgiTest(t, h, "GET /myscript/bar?a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
|
|
}
|
|
|
|
func TestDupHeaders(t *testing.T) {
|
|
if skipTest(t) {
|
|
return
|
|
}
|
|
h := &Handler{
|
|
Path: "testdata/test.cgi",
|
|
}
|
|
expectedMap := map[string]string{
|
|
"env-REQUEST_URI": "/myscript/bar?a=b",
|
|
"env-SCRIPT_FILENAME": "testdata/test.cgi",
|
|
"env-HTTP_COOKIE": "nom=NOM; yum=YUM",
|
|
"env-HTTP_X_FOO": "val1, val2",
|
|
}
|
|
runCgiTest(t, h, "GET /myscript/bar?a=b HTTP/1.0\n"+
|
|
"Cookie: nom=NOM\n"+
|
|
"Cookie: yum=YUM\n"+
|
|
"X-Foo: val1\n"+
|
|
"X-Foo: val2\n"+
|
|
"Host: example.com\n\n",
|
|
expectedMap)
|
|
}
|
|
|
|
func TestPathInfoNoRoot(t *testing.T) {
|
|
if skipTest(t) {
|
|
return
|
|
}
|
|
h := &Handler{
|
|
Path: "testdata/test.cgi",
|
|
Root: "",
|
|
}
|
|
expectedMap := map[string]string{
|
|
"env-PATH_INFO": "/bar",
|
|
"env-QUERY_STRING": "a=b",
|
|
"env-REQUEST_URI": "/bar?a=b",
|
|
"env-SCRIPT_FILENAME": "testdata/test.cgi",
|
|
"env-SCRIPT_NAME": "/",
|
|
}
|
|
runCgiTest(t, h, "GET /bar?a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
|
|
}
|
|
|
|
func TestCGIBasicPost(t *testing.T) {
|
|
if skipTest(t) {
|
|
return
|
|
}
|
|
postReq := `POST /test.cgi?a=b HTTP/1.0
|
|
Host: example.com
|
|
Content-Type: application/x-www-form-urlencoded
|
|
Content-Length: 15
|
|
|
|
postfoo=postbar`
|
|
h := &Handler{
|
|
Path: "testdata/test.cgi",
|
|
Root: "/test.cgi",
|
|
}
|
|
expectedMap := map[string]string{
|
|
"test": "Hello CGI",
|
|
"param-postfoo": "postbar",
|
|
"env-REQUEST_METHOD": "POST",
|
|
"env-CONTENT_LENGTH": "15",
|
|
"env-REQUEST_URI": "/test.cgi?a=b",
|
|
}
|
|
runCgiTest(t, h, postReq, expectedMap)
|
|
}
|
|
|
|
func chunk(s string) string {
|
|
return fmt.Sprintf("%x\r\n%s\r\n", len(s), s)
|
|
}
|
|
|
|
// The CGI spec doesn't allow chunked requests.
|
|
func TestCGIPostChunked(t *testing.T) {
|
|
if skipTest(t) {
|
|
return
|
|
}
|
|
postReq := `POST /test.cgi?a=b HTTP/1.1
|
|
Host: example.com
|
|
Content-Type: application/x-www-form-urlencoded
|
|
Transfer-Encoding: chunked
|
|
|
|
` + chunk("postfoo") + chunk("=") + chunk("postbar") + chunk("")
|
|
|
|
h := &Handler{
|
|
Path: "testdata/test.cgi",
|
|
Root: "/test.cgi",
|
|
}
|
|
expectedMap := map[string]string{}
|
|
resp := runCgiTest(t, h, postReq, expectedMap)
|
|
if got, expected := resp.Code, http.StatusBadRequest; got != expected {
|
|
t.Fatalf("Expected %v response code from chunked request body; got %d",
|
|
expected, got)
|
|
}
|
|
}
|
|
|
|
func TestRedirect(t *testing.T) {
|
|
if skipTest(t) {
|
|
return
|
|
}
|
|
h := &Handler{
|
|
Path: "testdata/test.cgi",
|
|
Root: "/test.cgi",
|
|
}
|
|
rec := runCgiTest(t, h, "GET /test.cgi?loc=http://foo.com/ HTTP/1.0\nHost: example.com\n\n", nil)
|
|
if e, g := 302, rec.Code; e != g {
|
|
t.Errorf("expected status code %d; got %d", e, g)
|
|
}
|
|
if e, g := "http://foo.com/", rec.Header().Get("Location"); e != g {
|
|
t.Errorf("expected Location header of %q; got %q", e, g)
|
|
}
|
|
}
|
|
|
|
func TestInternalRedirect(t *testing.T) {
|
|
if skipTest(t) {
|
|
return
|
|
}
|
|
baseHandler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
fmt.Fprintf(rw, "basepath=%s\n", req.URL.Path)
|
|
fmt.Fprintf(rw, "remoteaddr=%s\n", req.RemoteAddr)
|
|
})
|
|
h := &Handler{
|
|
Path: "testdata/test.cgi",
|
|
Root: "/test.cgi",
|
|
PathLocationHandler: baseHandler,
|
|
}
|
|
expectedMap := map[string]string{
|
|
"basepath": "/foo",
|
|
"remoteaddr": "1.2.3.4",
|
|
}
|
|
runCgiTest(t, h, "GET /test.cgi?loc=/foo HTTP/1.0\nHost: example.com\n\n", expectedMap)
|
|
}
|
|
|
|
// TestCopyError tests that we kill the process if there's an error copying
|
|
// its output. (for example, from the client having gone away)
|
|
func TestCopyError(t *testing.T) {
|
|
if skipTest(t) || runtime.GOOS == "windows" {
|
|
return
|
|
}
|
|
h := &Handler{
|
|
Path: "testdata/test.cgi",
|
|
Root: "/test.cgi",
|
|
}
|
|
ts := httptest.NewServer(h)
|
|
defer ts.Close()
|
|
|
|
conn, err := net.Dial("tcp", ts.Listener.Addr().String())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
req, _ := http.NewRequest("GET", "http://example.com/test.cgi?bigresponse=1", nil)
|
|
err = req.Write(conn)
|
|
if err != nil {
|
|
t.Fatalf("Write: %v", err)
|
|
}
|
|
|
|
res, err := http.ReadResponse(bufio.NewReader(conn), req)
|
|
if err != nil {
|
|
t.Fatalf("ReadResponse: %v", err)
|
|
}
|
|
|
|
pidstr := res.Header.Get("X-CGI-Pid")
|
|
if pidstr == "" {
|
|
t.Fatalf("expected an X-CGI-Pid header in response")
|
|
}
|
|
pid, err := strconv.Atoi(pidstr)
|
|
if err != nil {
|
|
t.Fatalf("invalid X-CGI-Pid value")
|
|
}
|
|
|
|
var buf [5000]byte
|
|
n, err := io.ReadFull(res.Body, buf[:])
|
|
if err != nil {
|
|
t.Fatalf("ReadFull: %d bytes, %v", n, err)
|
|
}
|
|
|
|
childRunning := func() bool {
|
|
p, err := os.FindProcess(pid)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return p.Signal(syscall.Signal(0)) == nil
|
|
}
|
|
|
|
if !childRunning() {
|
|
t.Fatalf("pre-conn.Close, expected child to be running")
|
|
}
|
|
conn.Close()
|
|
|
|
tries := 0
|
|
for tries < 25 && childRunning() {
|
|
time.Sleep(50 * time.Millisecond * time.Duration(tries))
|
|
tries++
|
|
}
|
|
if childRunning() {
|
|
t.Fatalf("post-conn.Close, expected child to be gone")
|
|
}
|
|
}
|
|
|
|
func TestDirUnix(t *testing.T) {
|
|
if skipTest(t) || runtime.GOOS == "windows" {
|
|
return
|
|
}
|
|
|
|
cwd, _ := os.Getwd()
|
|
h := &Handler{
|
|
Path: "testdata/test.cgi",
|
|
Root: "/test.cgi",
|
|
Dir: cwd,
|
|
}
|
|
expectedMap := map[string]string{
|
|
"cwd": cwd,
|
|
}
|
|
runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)
|
|
|
|
cwd, _ = os.Getwd()
|
|
cwd = filepath.Join(cwd, "testdata")
|
|
h = &Handler{
|
|
Path: "testdata/test.cgi",
|
|
Root: "/test.cgi",
|
|
}
|
|
abswd, _ := filepath.EvalSymlinks(cwd)
|
|
expectedMap = map[string]string{
|
|
"cwd": abswd,
|
|
}
|
|
runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)
|
|
}
|
|
|
|
func TestDirWindows(t *testing.T) {
|
|
if skipTest(t) || runtime.GOOS != "windows" {
|
|
return
|
|
}
|
|
|
|
cgifile, _ := filepath.Abs("testdata/test.cgi")
|
|
|
|
var perl string
|
|
var err error
|
|
perl, err = exec.LookPath("perl")
|
|
if err != nil {
|
|
return
|
|
}
|
|
perl, _ = filepath.Abs(perl)
|
|
|
|
cwd, _ := os.Getwd()
|
|
h := &Handler{
|
|
Path: perl,
|
|
Root: "/test.cgi",
|
|
Dir: cwd,
|
|
Args: []string{cgifile},
|
|
Env: []string{"SCRIPT_FILENAME=" + cgifile},
|
|
}
|
|
expectedMap := map[string]string{
|
|
"cwd": cwd,
|
|
}
|
|
runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)
|
|
|
|
// If not specify Dir on windows, working directory should be
|
|
// base directory of perl.
|
|
cwd, _ = filepath.Split(perl)
|
|
if cwd != "" && cwd[len(cwd)-1] == filepath.Separator {
|
|
cwd = cwd[:len(cwd)-1]
|
|
}
|
|
h = &Handler{
|
|
Path: perl,
|
|
Root: "/test.cgi",
|
|
Args: []string{cgifile},
|
|
Env: []string{"SCRIPT_FILENAME=" + cgifile},
|
|
}
|
|
expectedMap = map[string]string{
|
|
"cwd": cwd,
|
|
}
|
|
runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)
|
|
}
|
|
|
|
func TestEnvOverride(t *testing.T) {
|
|
cgifile, _ := filepath.Abs("testdata/test.cgi")
|
|
|
|
var perl string
|
|
var err error
|
|
perl, err = exec.LookPath("perl")
|
|
if err != nil {
|
|
return
|
|
}
|
|
perl, _ = filepath.Abs(perl)
|
|
|
|
cwd, _ := os.Getwd()
|
|
h := &Handler{
|
|
Path: perl,
|
|
Root: "/test.cgi",
|
|
Dir: cwd,
|
|
Args: []string{cgifile},
|
|
Env: []string{
|
|
"SCRIPT_FILENAME=" + cgifile,
|
|
"REQUEST_URI=/foo/bar"},
|
|
}
|
|
expectedMap := map[string]string{
|
|
"cwd": cwd,
|
|
"env-SCRIPT_FILENAME": cgifile,
|
|
"env-REQUEST_URI": "/foo/bar",
|
|
}
|
|
runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)
|
|
}
|