summaryrefslogtreecommitdiff
path: root/modules/caddyhttp/reverseproxy/fastcgi/client_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'modules/caddyhttp/reverseproxy/fastcgi/client_test.go')
-rw-r--r--modules/caddyhttp/reverseproxy/fastcgi/client_test.go301
1 files changed, 301 insertions, 0 deletions
diff --git a/modules/caddyhttp/reverseproxy/fastcgi/client_test.go b/modules/caddyhttp/reverseproxy/fastcgi/client_test.go
new file mode 100644
index 0000000..c090f3c
--- /dev/null
+++ b/modules/caddyhttp/reverseproxy/fastcgi/client_test.go
@@ -0,0 +1,301 @@
+// Copyright 2015 Matthew Holt and The Caddy Authors
+//
+// 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
+// specify remote fcgi responder 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) {
+
+ if err := req.ParseMultipartForm(100000000); err != nil {
+ log.Printf("[ERROR] failed to parse: %v", err)
+ }
+
+ 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())
+ if _, err := fo.Write(buf); err != nil {
+ log.Printf("[ERROR] failed to write buffer: %v\n", err)
+ }
+ if _, err := h.Write(buf); err != nil {
+ log.Printf("[ERROR] failed to write buffer: %v\n", err)
+ }
+ }
+ 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 {
+ log.Println("listener creation failed: ", err)
+ }
+
+ srv := new(FastCGIServer)
+ if err := fcgi.Serve(listener, srv); err != nil {
+ log.Print("[ERROR] failed to start server: ", err)
+ }
+ }()
+
+ 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)
+
+ if err := os.Remove(path0); err != nil {
+ log.Println("[ERROR] failed to remove path: ", err)
+ }
+ if err := os.Remove(path1); err != nil {
+ log.Println("[ERROR] failed to remove path: ", err)
+ }
+}