Skip to content

Commit bf25e31

Browse files
author
Michael Lauer
committed
scgi
1 parent 664e1c2 commit bf25e31

File tree

2 files changed

+108
-3
lines changed

2 files changed

+108
-3
lines changed

py_test.go

+42-3
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,35 @@
33
package gofcgisrv
44

55
import (
6+
"errors"
7+
"net"
68
"os"
79
"os/exec"
810
"strings"
911
"testing"
1012
"time"
1113
)
1214

15+
func waitForConn(addr string, timeout time.Duration) error {
16+
ticker := time.NewTicker(time.Millisecond * 10)
17+
timer := time.NewTimer(timeout)
18+
defer timer.Stop()
19+
defer ticker.Stop()
20+
for {
21+
select {
22+
case <-ticker.C:
23+
c, err := net.Dial("tcp", addr)
24+
if err == nil {
25+
c.Close()
26+
return nil
27+
}
28+
case <-timer.C:
29+
return errors.New("timeout")
30+
}
31+
}
32+
panic("Unreachable")
33+
}
34+
1335
func TestPyServer(t *testing.T) {
1436
cmd := exec.Command("python", "./testdata/cgi_test.py", "--port=9001")
1537
cmd.Stdout = os.Stdout
@@ -18,10 +40,9 @@ func TestPyServer(t *testing.T) {
1840
if err != nil {
1941
t.Fatalf("Error running cgi_test.py: %v", err)
2042
}
21-
defer time.Sleep(time.Millisecond * 10)
2243
defer cmd.Process.Kill()
2344

24-
time.Sleep(time.Millisecond * 100)
45+
waitForConn("127.0.0.1:9001", time.Second)
2546
s := NewServer("127.0.0.1:9001")
2647
testRequester(t, httpTestData{
2748
name: "py fastcgi",
@@ -33,7 +54,6 @@ func TestPyServer(t *testing.T) {
3354
}
3455

3556
func TestPyCGI(t *testing.T) {
36-
time.Sleep(time.Millisecond * 90)
3757
s := NewCGI("python", "./testdata/cgi_test.py", "--cgi")
3858
testRequester(t, httpTestData{
3959
name: "py cgi",
@@ -43,3 +63,22 @@ func TestPyCGI(t *testing.T) {
4363
expected: "This is a test",
4464
})
4565
}
66+
67+
func TestPySCGI(t *testing.T) {
68+
cmd := exec.Command("python", "./testdata/cgi_test.py", "--scgi", "--port=9002")
69+
// flup barfs some output. Why?? Seems wrong to me.
70+
err := cmd.Start()
71+
if err != nil {
72+
t.Fatalf("Error running cgi_test.py: %v", err)
73+
}
74+
defer cmd.Process.Kill()
75+
waitForConn("127.0.0.1:9002", time.Second)
76+
s := NewSCGI("127.0.0.1:9002")
77+
testRequester(t, httpTestData{
78+
name: "py scgi",
79+
f: s,
80+
body: strings.NewReader("This is a test"),
81+
status: 200,
82+
expected: "This is a test",
83+
})
84+
}

scgi.go

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package gofcgisrv
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"io"
7+
"net"
8+
"strings"
9+
)
10+
11+
type SCGIRequester struct {
12+
applicationAddr string
13+
}
14+
15+
func (sr *SCGIRequester) Request(env []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
16+
// Make a connection
17+
conn, err := net.Dial("tcp", sr.applicationAddr)
18+
if err != nil {
19+
return err
20+
}
21+
22+
// Send the environment
23+
header := bytes.NewBuffer(nil)
24+
// Content length has to go first.
25+
for _, envstring := range env {
26+
splits := strings.SplitN(envstring, "=", 2)
27+
if len(splits) == 2 && splits[0] == "CONTENT_LENGTH" {
28+
fmt.Fprintf(header, "%s\000%s\000", splits[0], splits[1])
29+
}
30+
}
31+
io.WriteString(header, "SCGI:\x001\x00")
32+
for _, envstring := range env {
33+
splits := strings.SplitN(envstring, "=", 2)
34+
if len(splits) == 2 && splits[0] != "CONTENT_LENGTH" {
35+
fmt.Fprintf(header, "%s\000%s\000", splits[0], splits[1])
36+
}
37+
}
38+
39+
_, err = fmt.Fprintf(conn, "%d:%s,", header.Len(), header.Bytes())
40+
if err != nil {
41+
conn.Close()
42+
return err
43+
}
44+
_, err = io.Copy(conn, stdin)
45+
if err != nil {
46+
conn.Close()
47+
return err
48+
}
49+
50+
// Flup needs the write side closed. I don't think that's right, but there it is.
51+
if cw, ok := conn.(interface {
52+
CloseWrite() error
53+
}); ok {
54+
cw.CloseWrite()
55+
}
56+
_, err = io.Copy(stdout, conn)
57+
// If we have an error, just log it to stderr.
58+
if err != nil {
59+
stderr.Write([]byte(err.Error()))
60+
}
61+
return nil
62+
}
63+
64+
func NewSCGI(addr string) *SCGIRequester {
65+
return &SCGIRequester{applicationAddr: addr}
66+
}

0 commit comments

Comments
 (0)