mirror of
https://github.com/caddyserver/caddy.git
synced 2024-11-26 02:09:47 +08:00
6a9aea04b1
Fixes #1961 According to RFC 7231 and RFC 7230, there's no reason a GET-Request can't have a body (other than it possibly not being supported by existing software). It's use is simply not defined, and is left to the application.
291 lines
7.0 KiB
Go
291 lines
7.0 KiB
Go
// 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.
|
|
|
|
// NOTE: These tests were adapted from the original
|
|
// repository from which this package was forked.
|
|
// The tests are slow (~10s) and in dire need of rewriting.
|
|
// As such, the tests have been disabled to speed up
|
|
// automated builds until they can be properly written.
|
|
|
|
package fastcgi
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/md5"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"math/rand"
|
|
"net"
|
|
"net/http"
|
|
"net/http/fcgi"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// test fcgi protocol includes:
|
|
// Get, Post, Post in multipart/form-data, and Post with files
|
|
// each key should be the md5 of the value or the file uploaded
|
|
// sepicify remote fcgi responer ip:port to test with php
|
|
// test failed if the remote fcgi(script) failed md5 verification
|
|
// and output "FAILED" in response
|
|
const (
|
|
scriptFile = "/tank/www/fcgic_test.php"
|
|
//ipPort = "remote-php-serv:59000"
|
|
ipPort = "127.0.0.1:59000"
|
|
)
|
|
|
|
var globalt *testing.T
|
|
|
|
type FastCGIServer struct{}
|
|
|
|
func (s FastCGIServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
|
|
|
|
req.ParseMultipartForm(100000000)
|
|
|
|
stat := "PASSED"
|
|
fmt.Fprintln(resp, "-")
|
|
fileNum := 0
|
|
{
|
|
length := 0
|
|
for k0, v0 := range req.Form {
|
|
h := md5.New()
|
|
io.WriteString(h, v0[0])
|
|
md5 := fmt.Sprintf("%x", h.Sum(nil))
|
|
|
|
length += len(k0)
|
|
length += len(v0[0])
|
|
|
|
// echo error when key != md5(val)
|
|
if md5 != k0 {
|
|
fmt.Fprintln(resp, "server:err ", md5, k0)
|
|
stat = "FAILED"
|
|
}
|
|
}
|
|
if req.MultipartForm != nil {
|
|
fileNum = len(req.MultipartForm.File)
|
|
for kn, fns := range req.MultipartForm.File {
|
|
//fmt.Fprintln(resp, "server:filekey ", kn )
|
|
length += len(kn)
|
|
for _, f := range fns {
|
|
fd, err := f.Open()
|
|
if err != nil {
|
|
log.Println("server:", err)
|
|
return
|
|
}
|
|
h := md5.New()
|
|
l0, err := io.Copy(h, fd)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
length += int(l0)
|
|
defer fd.Close()
|
|
md5 := fmt.Sprintf("%x", h.Sum(nil))
|
|
//fmt.Fprintln(resp, "server:filemd5 ", md5 )
|
|
|
|
if kn != md5 {
|
|
fmt.Fprintln(resp, "server:err ", md5, kn)
|
|
stat = "FAILED"
|
|
}
|
|
//fmt.Fprintln(resp, "server:filename ", f.Filename )
|
|
}
|
|
}
|
|
}
|
|
|
|
fmt.Fprintln(resp, "server:got data length", length)
|
|
}
|
|
fmt.Fprintln(resp, "-"+stat+"-POST(", len(req.Form), ")-FILE(", fileNum, ")--")
|
|
}
|
|
|
|
func sendFcgi(reqType int, fcgiParams map[string]string, data []byte, posts map[string]string, files map[string]string) (content []byte) {
|
|
fcgi, err := Dial("tcp", ipPort)
|
|
if err != nil {
|
|
log.Println("err:", err)
|
|
return
|
|
}
|
|
|
|
length := 0
|
|
|
|
var resp *http.Response
|
|
switch reqType {
|
|
case 0:
|
|
if len(data) > 0 {
|
|
length = len(data)
|
|
rd := bytes.NewReader(data)
|
|
resp, err = fcgi.Post(fcgiParams, "", "", rd, int64(rd.Len()))
|
|
} else if len(posts) > 0 {
|
|
values := url.Values{}
|
|
for k, v := range posts {
|
|
values.Set(k, v)
|
|
length += len(k) + 2 + len(v)
|
|
}
|
|
resp, err = fcgi.PostForm(fcgiParams, values)
|
|
} else {
|
|
rd := bytes.NewReader(data)
|
|
resp, err = fcgi.Get(fcgiParams, rd, int64(rd.Len()))
|
|
}
|
|
|
|
default:
|
|
values := url.Values{}
|
|
for k, v := range posts {
|
|
values.Set(k, v)
|
|
length += len(k) + 2 + len(v)
|
|
}
|
|
|
|
for k, v := range files {
|
|
fi, _ := os.Lstat(v)
|
|
length += len(k) + int(fi.Size())
|
|
}
|
|
resp, err = fcgi.PostFile(fcgiParams, values, files)
|
|
}
|
|
|
|
if err != nil {
|
|
log.Println("err:", err)
|
|
return
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
content, _ = ioutil.ReadAll(resp.Body)
|
|
|
|
log.Println("c: send data length ≈", length, string(content))
|
|
fcgi.Close()
|
|
time.Sleep(1 * time.Second)
|
|
|
|
if bytes.Contains(content, []byte("FAILED")) {
|
|
globalt.Error("Server return failed message")
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func generateRandFile(size int) (p string, m string) {
|
|
|
|
p = filepath.Join(os.TempDir(), "fcgict"+strconv.Itoa(rand.Int()))
|
|
|
|
// open output file
|
|
fo, err := os.Create(p)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
// close fo on exit and check for its returned error
|
|
defer func() {
|
|
if err := fo.Close(); err != nil {
|
|
panic(err)
|
|
}
|
|
}()
|
|
|
|
h := md5.New()
|
|
for i := 0; i < size/16; i++ {
|
|
buf := make([]byte, 16)
|
|
binary.PutVarint(buf, rand.Int63())
|
|
fo.Write(buf)
|
|
h.Write(buf)
|
|
}
|
|
m = fmt.Sprintf("%x", h.Sum(nil))
|
|
return
|
|
}
|
|
|
|
func DisabledTest(t *testing.T) {
|
|
// TODO: test chunked reader
|
|
globalt = t
|
|
|
|
rand.Seed(time.Now().UTC().UnixNano())
|
|
|
|
// server
|
|
go func() {
|
|
listener, err := net.Listen("tcp", ipPort)
|
|
if err != nil {
|
|
// handle error
|
|
log.Println("listener creation failed: ", err)
|
|
}
|
|
|
|
srv := new(FastCGIServer)
|
|
fcgi.Serve(listener, srv)
|
|
}()
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
// init
|
|
fcgiParams := make(map[string]string)
|
|
fcgiParams["REQUEST_METHOD"] = "GET"
|
|
fcgiParams["SERVER_PROTOCOL"] = "HTTP/1.1"
|
|
//fcgi_params["GATEWAY_INTERFACE"] = "CGI/1.1"
|
|
fcgiParams["SCRIPT_FILENAME"] = scriptFile
|
|
|
|
// simple GET
|
|
log.Println("test:", "get")
|
|
sendFcgi(0, fcgiParams, nil, nil, nil)
|
|
|
|
// simple post data
|
|
log.Println("test:", "post")
|
|
sendFcgi(0, fcgiParams, []byte("c4ca4238a0b923820dcc509a6f75849b=1&7b8b965ad4bca0e41ab51de7b31363a1=n"), nil, nil)
|
|
|
|
log.Println("test:", "post data (more than 60KB)")
|
|
data := ""
|
|
for i := 0x00; i < 0xff; i++ {
|
|
v0 := strings.Repeat(string(i), 256)
|
|
h := md5.New()
|
|
io.WriteString(h, v0)
|
|
k0 := fmt.Sprintf("%x", h.Sum(nil))
|
|
data += k0 + "=" + url.QueryEscape(v0) + "&"
|
|
}
|
|
sendFcgi(0, fcgiParams, []byte(data), nil, nil)
|
|
|
|
log.Println("test:", "post form (use url.Values)")
|
|
p0 := make(map[string]string, 1)
|
|
p0["c4ca4238a0b923820dcc509a6f75849b"] = "1"
|
|
p0["7b8b965ad4bca0e41ab51de7b31363a1"] = "n"
|
|
sendFcgi(1, fcgiParams, nil, p0, nil)
|
|
|
|
log.Println("test:", "post forms (256 keys, more than 1MB)")
|
|
p1 := make(map[string]string, 1)
|
|
for i := 0x00; i < 0xff; i++ {
|
|
v0 := strings.Repeat(string(i), 4096)
|
|
h := md5.New()
|
|
io.WriteString(h, v0)
|
|
k0 := fmt.Sprintf("%x", h.Sum(nil))
|
|
p1[k0] = v0
|
|
}
|
|
sendFcgi(1, fcgiParams, nil, p1, nil)
|
|
|
|
log.Println("test:", "post file (1 file, 500KB)) ")
|
|
f0 := make(map[string]string, 1)
|
|
path0, m0 := generateRandFile(500000)
|
|
f0[m0] = path0
|
|
sendFcgi(1, fcgiParams, nil, p1, f0)
|
|
|
|
log.Println("test:", "post multiple files (2 files, 5M each) and forms (256 keys, more than 1MB data")
|
|
path1, m1 := generateRandFile(5000000)
|
|
f0[m1] = path1
|
|
sendFcgi(1, fcgiParams, nil, p1, f0)
|
|
|
|
log.Println("test:", "post only files (2 files, 5M each)")
|
|
sendFcgi(1, fcgiParams, nil, nil, f0)
|
|
|
|
log.Println("test:", "post only 1 file")
|
|
delete(f0, "m0")
|
|
sendFcgi(1, fcgiParams, nil, nil, f0)
|
|
|
|
os.Remove(path0)
|
|
os.Remove(path1)
|
|
}
|