187 lines
4.2 KiB
Go
187 lines
4.2 KiB
Go
//
|
|
// The software in this package is published under the terms of the Mozilla
|
|
// Public License, version 2.0 a copy of which has been included with this
|
|
// distribution in the LICENSE file.
|
|
//
|
|
|
|
package testutil
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
type HTTPServer struct {
|
|
URL string
|
|
Timeout time.Duration
|
|
started bool
|
|
request chan *http.Request
|
|
response chan ResponseFunc
|
|
}
|
|
|
|
type Response struct {
|
|
Status int
|
|
Headers map[string]string
|
|
Body string
|
|
}
|
|
|
|
var DefaultClient = &http.Client{
|
|
Transport: &http.Transport{
|
|
Proxy: http.ProxyFromEnvironment,
|
|
},
|
|
}
|
|
|
|
func NewHTTPServer() *HTTPServer {
|
|
return &HTTPServer{URL: "http://localhost:4444", Timeout: 5 * time.Second}
|
|
}
|
|
|
|
type ResponseFunc func(path string) Response
|
|
|
|
func (s *HTTPServer) Start() {
|
|
if s.started {
|
|
return
|
|
}
|
|
s.started = true
|
|
s.request = make(chan *http.Request, 1024)
|
|
s.response = make(chan ResponseFunc, 1024)
|
|
u, err := url.Parse(s.URL)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
l, err := net.Listen("tcp", u.Host)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
go http.Serve(l, s)
|
|
|
|
s.Response(203, nil, "")
|
|
for {
|
|
// Wait for it to be up.
|
|
resp, err := http.Get(s.URL)
|
|
if err == nil && resp.StatusCode == 203 {
|
|
break
|
|
}
|
|
time.Sleep(1e8)
|
|
}
|
|
s.WaitRequest() // Consume dummy request.
|
|
}
|
|
|
|
// Flush discards all pending requests and responses.
|
|
func (s *HTTPServer) Flush() {
|
|
for {
|
|
select {
|
|
case <-s.request:
|
|
case <-s.response:
|
|
default:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func body(req *http.Request) string {
|
|
data, err := ioutil.ReadAll(req.Body)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return string(data)
|
|
}
|
|
|
|
func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
req.ParseMultipartForm(1e6)
|
|
data, err := ioutil.ReadAll(req.Body)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
req.Body = ioutil.NopCloser(bytes.NewBuffer(data))
|
|
s.request <- req
|
|
var resp Response
|
|
select {
|
|
case respFunc := <-s.response:
|
|
resp = respFunc(req.URL.Path)
|
|
case <-time.After(s.Timeout):
|
|
const msg = "ERROR: Timeout waiting for test to prepare a response\n"
|
|
fmt.Fprintf(os.Stderr, msg)
|
|
resp = Response{500, nil, msg}
|
|
}
|
|
if resp.Headers != nil {
|
|
h := w.Header()
|
|
for k, v := range resp.Headers {
|
|
h.Set(k, v)
|
|
}
|
|
}
|
|
if resp.Status != 0 {
|
|
w.WriteHeader(resp.Status)
|
|
}
|
|
w.Write([]byte(resp.Body))
|
|
}
|
|
|
|
// WaitRequests returns the next n requests made to the http server from
|
|
// the queue. If not enough requests were previously made, it waits until
|
|
// the timeout value for them to be made.
|
|
func (s *HTTPServer) WaitRequests(n int) []*http.Request {
|
|
reqs := make([]*http.Request, 0, n)
|
|
for i := 0; i < n; i++ {
|
|
select {
|
|
case req := <-s.request:
|
|
reqs = append(reqs, req)
|
|
case <-time.After(s.Timeout):
|
|
panic("Timeout waiting for request")
|
|
}
|
|
}
|
|
return reqs
|
|
}
|
|
|
|
// WaitRequest returns the next request made to the http server from
|
|
// the queue. If no requests were previously made, it waits until the
|
|
// timeout value for one to be made.
|
|
func (s *HTTPServer) WaitRequest() *http.Request {
|
|
return s.WaitRequests(1)[0]
|
|
}
|
|
|
|
// ResponseFunc prepares the test server to respond the following n
|
|
// requests using f to build each response.
|
|
func (s *HTTPServer) ResponseFunc(n int, f ResponseFunc) {
|
|
for i := 0; i < n; i++ {
|
|
s.response <- f
|
|
}
|
|
}
|
|
|
|
// ResponseMap maps request paths to responses.
|
|
type ResponseMap map[string]Response
|
|
|
|
// ResponseMap prepares the test server to respond the following n
|
|
// requests using the m to obtain the responses.
|
|
func (s *HTTPServer) ResponseMap(n int, m ResponseMap) {
|
|
f := func(path string) Response {
|
|
for rpath, resp := range m {
|
|
if rpath == path {
|
|
return resp
|
|
}
|
|
}
|
|
body := "Path not found in response map: " + path
|
|
return Response{Status: 500, Body: body}
|
|
}
|
|
s.ResponseFunc(n, f)
|
|
}
|
|
|
|
// Responses prepares the test server to respond the following n requests
|
|
// using the provided response parameters.
|
|
func (s *HTTPServer) Responses(n int, status int, headers map[string]string, body string) {
|
|
f := func(path string) Response {
|
|
return Response{status, headers, body}
|
|
}
|
|
s.ResponseFunc(n, f)
|
|
}
|
|
|
|
// Response prepares the test server to respond the following request
|
|
// using the provided response parameters.
|
|
func (s *HTTPServer) Response(status int, headers map[string]string, body string) {
|
|
s.Responses(1, status, headers, body)
|
|
}
|