You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
678 lines
17 KiB
678 lines
17 KiB
9 years ago
|
package httpdown_test
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"crypto/tls"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"net"
|
||
|
"net/http"
|
||
|
"os"
|
||
|
"regexp"
|
||
|
"sync"
|
||
|
"sync/atomic"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/facebookgo/clock"
|
||
|
"github.com/facebookgo/ensure"
|
||
|
"github.com/facebookgo/freeport"
|
||
|
"github.com/facebookgo/httpdown"
|
||
|
"github.com/facebookgo/stats"
|
||
|
)
|
||
|
|
||
|
type onCloseListener struct {
|
||
|
net.Listener
|
||
|
mutex sync.Mutex
|
||
|
onClose chan struct{}
|
||
|
}
|
||
|
|
||
|
func (o *onCloseListener) Close() error {
|
||
|
// Listener is closed twice, once by Grace, and once by the http library, so
|
||
|
// we guard against a double close of the chan.
|
||
|
defer func() {
|
||
|
o.mutex.Lock()
|
||
|
defer o.mutex.Unlock()
|
||
|
if o.onClose != nil {
|
||
|
close(o.onClose)
|
||
|
o.onClose = nil
|
||
|
}
|
||
|
}()
|
||
|
return o.Listener.Close()
|
||
|
}
|
||
|
|
||
|
func NewOnCloseListener(l net.Listener) (net.Listener, chan struct{}) {
|
||
|
c := make(chan struct{})
|
||
|
return &onCloseListener{Listener: l, onClose: c}, c
|
||
|
}
|
||
|
|
||
|
type closeErrListener struct {
|
||
|
net.Listener
|
||
|
err error
|
||
|
}
|
||
|
|
||
|
func (c *closeErrListener) Close() error {
|
||
|
c.Listener.Close()
|
||
|
return c.err
|
||
|
}
|
||
|
|
||
|
type acceptErrListener struct {
|
||
|
net.Listener
|
||
|
err chan error
|
||
|
}
|
||
|
|
||
|
func (c *acceptErrListener) Accept() (net.Conn, error) {
|
||
|
return nil, <-c.err
|
||
|
}
|
||
|
|
||
|
type closeErrConn struct {
|
||
|
net.Conn
|
||
|
unblockClose chan chan struct{}
|
||
|
}
|
||
|
|
||
|
func (c *closeErrConn) Close() error {
|
||
|
ch := <-c.unblockClose
|
||
|
|
||
|
// Close gets called multiple times, but only the first one gets this ch
|
||
|
if ch != nil {
|
||
|
defer close(ch)
|
||
|
}
|
||
|
|
||
|
return c.Conn.Close()
|
||
|
}
|
||
|
|
||
|
type closeErrConnListener struct {
|
||
|
net.Listener
|
||
|
unblockClose chan chan struct{}
|
||
|
}
|
||
|
|
||
|
func (l *closeErrConnListener) Accept() (net.Conn, error) {
|
||
|
c, err := l.Listener.Accept()
|
||
|
if err != nil {
|
||
|
return c, err
|
||
|
}
|
||
|
return &closeErrConn{Conn: c, unblockClose: l.unblockClose}, nil
|
||
|
}
|
||
|
|
||
|
func TestHTTPStopWithNoRequest(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||
|
ensure.Nil(t, err)
|
||
|
|
||
|
statsDone := make(chan struct{}, 2)
|
||
|
hc := &stats.HookClient{
|
||
|
BumpSumHook: func(key string, val float64) {
|
||
|
if key == "serve" && val == 1 {
|
||
|
statsDone <- struct{}{}
|
||
|
}
|
||
|
if key == "stop" && val == 1 {
|
||
|
statsDone <- struct{}{}
|
||
|
}
|
||
|
},
|
||
|
}
|
||
|
|
||
|
server := &http.Server{}
|
||
|
down := &httpdown.HTTP{Stats: hc}
|
||
|
s := down.Serve(server, listener)
|
||
|
ensure.Nil(t, s.Stop())
|
||
|
<-statsDone
|
||
|
<-statsDone
|
||
|
}
|
||
|
|
||
|
func TestHTTPStopWithFinishedRequest(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
hello := []byte("hello")
|
||
|
fin := make(chan struct{})
|
||
|
okHandler := func(w http.ResponseWriter, r *http.Request) {
|
||
|
defer close(fin)
|
||
|
w.Write(hello)
|
||
|
}
|
||
|
|
||
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||
|
ensure.Nil(t, err)
|
||
|
server := &http.Server{Handler: http.HandlerFunc(okHandler)}
|
||
|
transport := &http.Transport{}
|
||
|
client := &http.Client{Transport: transport}
|
||
|
down := &httpdown.HTTP{}
|
||
|
s := down.Serve(server, listener)
|
||
|
res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
|
||
|
ensure.Nil(t, err)
|
||
|
actualBody, err := ioutil.ReadAll(res.Body)
|
||
|
ensure.Nil(t, err)
|
||
|
ensure.DeepEqual(t, actualBody, hello)
|
||
|
ensure.Nil(t, res.Body.Close())
|
||
|
|
||
|
// At this point the request is finished, and the connection should be alive
|
||
|
// but idle (because we have keep alive enabled by default in our Transport).
|
||
|
ensure.Nil(t, s.Stop())
|
||
|
<-fin
|
||
|
|
||
|
ensure.Nil(t, s.Wait())
|
||
|
}
|
||
|
|
||
|
func TestHTTPStopWithActiveRequest(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
const count = 10000
|
||
|
hello := []byte("hello")
|
||
|
finOkHandler := make(chan struct{})
|
||
|
okHandler := func(w http.ResponseWriter, r *http.Request) {
|
||
|
defer close(finOkHandler)
|
||
|
w.WriteHeader(200)
|
||
|
for i := 0; i < count; i++ {
|
||
|
w.Write(hello)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||
|
ensure.Nil(t, err)
|
||
|
server := &http.Server{Handler: http.HandlerFunc(okHandler)}
|
||
|
transport := &http.Transport{}
|
||
|
client := &http.Client{Transport: transport}
|
||
|
down := &httpdown.HTTP{}
|
||
|
s := down.Serve(server, listener)
|
||
|
res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
|
||
|
ensure.Nil(t, err)
|
||
|
|
||
|
finStop := make(chan struct{})
|
||
|
go func() {
|
||
|
defer close(finStop)
|
||
|
ensure.Nil(t, s.Stop())
|
||
|
}()
|
||
|
|
||
|
actualBody, err := ioutil.ReadAll(res.Body)
|
||
|
ensure.Nil(t, err)
|
||
|
ensure.DeepEqual(t, actualBody, bytes.Repeat(hello, count))
|
||
|
ensure.Nil(t, res.Body.Close())
|
||
|
<-finOkHandler
|
||
|
<-finStop
|
||
|
}
|
||
|
|
||
|
func TestNewRequestAfterStop(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
const count = 10000
|
||
|
hello := []byte("hello")
|
||
|
finOkHandler := make(chan struct{})
|
||
|
unblockOkHandler := make(chan struct{})
|
||
|
okHandler := func(w http.ResponseWriter, r *http.Request) {
|
||
|
defer close(finOkHandler)
|
||
|
w.WriteHeader(200)
|
||
|
const diff = 500
|
||
|
for i := 0; i < count-diff; i++ {
|
||
|
w.Write(hello)
|
||
|
}
|
||
|
<-unblockOkHandler
|
||
|
for i := 0; i < diff; i++ {
|
||
|
w.Write(hello)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||
|
listener, onClose := NewOnCloseListener(listener)
|
||
|
ensure.Nil(t, err)
|
||
|
server := &http.Server{Handler: http.HandlerFunc(okHandler)}
|
||
|
transport := &http.Transport{}
|
||
|
client := &http.Client{Transport: transport}
|
||
|
down := &httpdown.HTTP{}
|
||
|
s := down.Serve(server, listener)
|
||
|
res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
|
||
|
ensure.Nil(t, err)
|
||
|
|
||
|
finStop := make(chan struct{})
|
||
|
go func() {
|
||
|
defer close(finStop)
|
||
|
ensure.Nil(t, s.Stop())
|
||
|
}()
|
||
|
|
||
|
// Wait until the listener is closed.
|
||
|
<-onClose
|
||
|
|
||
|
// Now the next request should not be able to connect as the listener is
|
||
|
// now closed.
|
||
|
_, err = client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
|
||
|
|
||
|
// We should just get "connection refused" here, but sometimes, very rarely,
|
||
|
// we get a "connection reset" instead. Unclear why this happens.
|
||
|
ensure.Err(t, err, regexp.MustCompile("(connection refused|connection reset by peer)$"))
|
||
|
|
||
|
// Unblock the handler and ensure we finish writing the rest of the body
|
||
|
// successfully.
|
||
|
close(unblockOkHandler)
|
||
|
actualBody, err := ioutil.ReadAll(res.Body)
|
||
|
ensure.Nil(t, err)
|
||
|
ensure.DeepEqual(t, actualBody, bytes.Repeat(hello, count))
|
||
|
ensure.Nil(t, res.Body.Close())
|
||
|
<-finOkHandler
|
||
|
<-finStop
|
||
|
}
|
||
|
|
||
|
func TestHTTPListenerCloseError(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
expectedError := errors.New("foo")
|
||
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||
|
listener = &closeErrListener{Listener: listener, err: expectedError}
|
||
|
ensure.Nil(t, err)
|
||
|
server := &http.Server{}
|
||
|
down := &httpdown.HTTP{}
|
||
|
s := down.Serve(server, listener)
|
||
|
ensure.DeepEqual(t, s.Stop(), expectedError)
|
||
|
}
|
||
|
|
||
|
func TestHTTPServeError(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
expectedError := errors.New("foo")
|
||
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||
|
errChan := make(chan error)
|
||
|
listener = &acceptErrListener{Listener: listener, err: errChan}
|
||
|
ensure.Nil(t, err)
|
||
|
server := &http.Server{}
|
||
|
down := &httpdown.HTTP{}
|
||
|
s := down.Serve(server, listener)
|
||
|
errChan <- expectedError
|
||
|
ensure.DeepEqual(t, s.Wait(), expectedError)
|
||
|
ensure.Nil(t, s.Stop())
|
||
|
}
|
||
|
|
||
|
func TestHTTPWithinStopTimeout(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
hello := []byte("hello")
|
||
|
finOkHandler := make(chan struct{})
|
||
|
okHandler := func(w http.ResponseWriter, r *http.Request) {
|
||
|
defer close(finOkHandler)
|
||
|
w.WriteHeader(200)
|
||
|
w.Write(hello)
|
||
|
}
|
||
|
|
||
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||
|
ensure.Nil(t, err)
|
||
|
server := &http.Server{Handler: http.HandlerFunc(okHandler)}
|
||
|
transport := &http.Transport{}
|
||
|
client := &http.Client{Transport: transport}
|
||
|
down := &httpdown.HTTP{StopTimeout: time.Minute}
|
||
|
s := down.Serve(server, listener)
|
||
|
res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
|
||
|
ensure.Nil(t, err)
|
||
|
|
||
|
finStop := make(chan struct{})
|
||
|
go func() {
|
||
|
defer close(finStop)
|
||
|
ensure.Nil(t, s.Stop())
|
||
|
}()
|
||
|
|
||
|
actualBody, err := ioutil.ReadAll(res.Body)
|
||
|
ensure.Nil(t, err)
|
||
|
ensure.DeepEqual(t, actualBody, hello)
|
||
|
ensure.Nil(t, res.Body.Close())
|
||
|
<-finOkHandler
|
||
|
<-finStop
|
||
|
}
|
||
|
|
||
|
func TestHTTPStopTimeoutMissed(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
klock := clock.NewMock()
|
||
|
|
||
|
const count = 10000
|
||
|
hello := []byte("hello")
|
||
|
finOkHandler := make(chan struct{})
|
||
|
unblockOkHandler := make(chan struct{})
|
||
|
okHandler := func(w http.ResponseWriter, r *http.Request) {
|
||
|
defer close(finOkHandler)
|
||
|
w.Header().Set("Content-Length", fmt.Sprint(len(hello)*count))
|
||
|
w.WriteHeader(200)
|
||
|
for i := 0; i < count/2; i++ {
|
||
|
w.Write(hello)
|
||
|
}
|
||
|
<-unblockOkHandler
|
||
|
for i := 0; i < count/2; i++ {
|
||
|
w.Write(hello)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||
|
ensure.Nil(t, err)
|
||
|
server := &http.Server{Handler: http.HandlerFunc(okHandler)}
|
||
|
transport := &http.Transport{}
|
||
|
client := &http.Client{Transport: transport}
|
||
|
down := &httpdown.HTTP{
|
||
|
StopTimeout: time.Minute,
|
||
|
Clock: klock,
|
||
|
}
|
||
|
s := down.Serve(server, listener)
|
||
|
res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
|
||
|
ensure.Nil(t, err)
|
||
|
|
||
|
finStop := make(chan struct{})
|
||
|
go func() {
|
||
|
defer close(finStop)
|
||
|
ensure.Nil(t, s.Stop())
|
||
|
}()
|
||
|
|
||
|
klock.Wait(clock.Calls{After: 1}) // wait for Stop to call After
|
||
|
klock.Add(down.StopTimeout)
|
||
|
|
||
|
_, err = ioutil.ReadAll(res.Body)
|
||
|
ensure.Err(t, err, regexp.MustCompile("^unexpected EOF$"))
|
||
|
ensure.Nil(t, res.Body.Close())
|
||
|
close(unblockOkHandler)
|
||
|
<-finOkHandler
|
||
|
<-finStop
|
||
|
}
|
||
|
|
||
|
func TestHTTPKillTimeout(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
klock := clock.NewMock()
|
||
|
|
||
|
statsDone := make(chan struct{}, 1)
|
||
|
hc := &stats.HookClient{
|
||
|
BumpSumHook: func(key string, val float64) {
|
||
|
if key == "kill" && val == 1 {
|
||
|
statsDone <- struct{}{}
|
||
|
}
|
||
|
},
|
||
|
}
|
||
|
|
||
|
const count = 10000
|
||
|
hello := []byte("hello")
|
||
|
finOkHandler := make(chan struct{})
|
||
|
unblockOkHandler := make(chan struct{})
|
||
|
okHandler := func(w http.ResponseWriter, r *http.Request) {
|
||
|
defer close(finOkHandler)
|
||
|
w.Header().Set("Content-Length", fmt.Sprint(len(hello)*count))
|
||
|
w.WriteHeader(200)
|
||
|
for i := 0; i < count/2; i++ {
|
||
|
w.Write(hello)
|
||
|
}
|
||
|
<-unblockOkHandler
|
||
|
for i := 0; i < count/2; i++ {
|
||
|
w.Write(hello)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||
|
ensure.Nil(t, err)
|
||
|
server := &http.Server{Handler: http.HandlerFunc(okHandler)}
|
||
|
transport := &http.Transport{}
|
||
|
client := &http.Client{Transport: transport}
|
||
|
down := &httpdown.HTTP{
|
||
|
StopTimeout: time.Minute,
|
||
|
KillTimeout: time.Minute,
|
||
|
Stats: hc,
|
||
|
Clock: klock,
|
||
|
}
|
||
|
s := down.Serve(server, listener)
|
||
|
res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
|
||
|
ensure.Nil(t, err)
|
||
|
|
||
|
finStop := make(chan struct{})
|
||
|
go func() {
|
||
|
defer close(finStop)
|
||
|
ensure.Nil(t, s.Stop())
|
||
|
}()
|
||
|
|
||
|
klock.Wait(clock.Calls{After: 1}) // wait for Stop to call After
|
||
|
klock.Add(down.StopTimeout)
|
||
|
|
||
|
_, err = ioutil.ReadAll(res.Body)
|
||
|
ensure.Err(t, err, regexp.MustCompile("^unexpected EOF$"))
|
||
|
ensure.Nil(t, res.Body.Close())
|
||
|
close(unblockOkHandler)
|
||
|
<-finOkHandler
|
||
|
<-finStop
|
||
|
<-statsDone
|
||
|
}
|
||
|
|
||
|
func TestHTTPKillTimeoutMissed(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
klock := clock.NewMock()
|
||
|
|
||
|
statsDone := make(chan struct{}, 1)
|
||
|
hc := &stats.HookClient{
|
||
|
BumpSumHook: func(key string, val float64) {
|
||
|
if key == "kill.timeout" && val == 1 {
|
||
|
statsDone <- struct{}{}
|
||
|
}
|
||
|
},
|
||
|
}
|
||
|
|
||
|
const count = 10000
|
||
|
hello := []byte("hello")
|
||
|
finOkHandler := make(chan struct{})
|
||
|
unblockOkHandler := make(chan struct{})
|
||
|
okHandler := func(w http.ResponseWriter, r *http.Request) {
|
||
|
defer close(finOkHandler)
|
||
|
w.Header().Set("Content-Length", fmt.Sprint(len(hello)*count))
|
||
|
w.WriteHeader(200)
|
||
|
for i := 0; i < count/2; i++ {
|
||
|
w.Write(hello)
|
||
|
}
|
||
|
<-unblockOkHandler
|
||
|
for i := 0; i < count/2; i++ {
|
||
|
w.Write(hello)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||
|
ensure.Nil(t, err)
|
||
|
unblockConnClose := make(chan chan struct{}, 1)
|
||
|
listener = &closeErrConnListener{
|
||
|
Listener: listener,
|
||
|
unblockClose: unblockConnClose,
|
||
|
}
|
||
|
|
||
|
server := &http.Server{Handler: http.HandlerFunc(okHandler)}
|
||
|
transport := &http.Transport{}
|
||
|
client := &http.Client{Transport: transport}
|
||
|
down := &httpdown.HTTP{
|
||
|
StopTimeout: time.Minute,
|
||
|
KillTimeout: time.Minute,
|
||
|
Stats: hc,
|
||
|
Clock: klock,
|
||
|
}
|
||
|
s := down.Serve(server, listener)
|
||
|
res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
|
||
|
ensure.Nil(t, err)
|
||
|
|
||
|
// Start the Stop process.
|
||
|
finStop := make(chan struct{})
|
||
|
go func() {
|
||
|
defer close(finStop)
|
||
|
ensure.Nil(t, s.Stop())
|
||
|
}()
|
||
|
|
||
|
klock.Wait(clock.Calls{After: 1}) // wait for Stop to call After
|
||
|
klock.Add(down.StopTimeout) // trigger stop timeout
|
||
|
klock.Wait(clock.Calls{After: 2}) // wait for Kill to call After
|
||
|
klock.Add(down.KillTimeout) // trigger kill timeout
|
||
|
|
||
|
// We hit both the StopTimeout & the KillTimeout.
|
||
|
<-finStop
|
||
|
|
||
|
// Then we unblock the Close, so we get an unexpected EOF since we close
|
||
|
// before we finish writing the response.
|
||
|
connCloseDone := make(chan struct{})
|
||
|
unblockConnClose <- connCloseDone
|
||
|
<-connCloseDone
|
||
|
close(unblockConnClose)
|
||
|
|
||
|
// Then we unblock the handler which tries to write the rest of the data.
|
||
|
close(unblockOkHandler)
|
||
|
|
||
|
_, err = ioutil.ReadAll(res.Body)
|
||
|
ensure.Err(t, err, regexp.MustCompile("^unexpected EOF$"))
|
||
|
ensure.Nil(t, res.Body.Close())
|
||
|
<-finOkHandler
|
||
|
<-statsDone
|
||
|
}
|
||
|
|
||
|
func TestDoubleStop(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||
|
ensure.Nil(t, err)
|
||
|
server := &http.Server{}
|
||
|
down := &httpdown.HTTP{}
|
||
|
s := down.Serve(server, listener)
|
||
|
ensure.Nil(t, s.Stop())
|
||
|
ensure.Nil(t, s.Stop())
|
||
|
}
|
||
|
|
||
|
func TestExistingConnState(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
hello := []byte("hello")
|
||
|
fin := make(chan struct{})
|
||
|
okHandler := func(w http.ResponseWriter, r *http.Request) {
|
||
|
defer close(fin)
|
||
|
w.Write(hello)
|
||
|
}
|
||
|
|
||
|
var called int32
|
||
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||
|
ensure.Nil(t, err)
|
||
|
server := &http.Server{
|
||
|
Handler: http.HandlerFunc(okHandler),
|
||
|
ConnState: func(c net.Conn, s http.ConnState) {
|
||
|
atomic.AddInt32(&called, 1)
|
||
|
},
|
||
|
}
|
||
|
transport := &http.Transport{}
|
||
|
client := &http.Client{Transport: transport}
|
||
|
down := &httpdown.HTTP{}
|
||
|
s := down.Serve(server, listener)
|
||
|
res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
|
||
|
ensure.Nil(t, err)
|
||
|
actualBody, err := ioutil.ReadAll(res.Body)
|
||
|
ensure.Nil(t, err)
|
||
|
ensure.DeepEqual(t, actualBody, hello)
|
||
|
ensure.Nil(t, res.Body.Close())
|
||
|
|
||
|
ensure.Nil(t, s.Stop())
|
||
|
<-fin
|
||
|
|
||
|
ensure.True(t, atomic.LoadInt32(&called) > 0)
|
||
|
}
|
||
|
|
||
|
func TestHTTPDefaultListenError(t *testing.T) {
|
||
|
if os.Getuid() == 0 {
|
||
|
t.Skip("cant run this test as root")
|
||
|
}
|
||
|
|
||
|
statsDone := make(chan struct{}, 1)
|
||
|
hc := &stats.HookClient{
|
||
|
BumpSumHook: func(key string, val float64) {
|
||
|
if key == "listen.error" && val == 1 {
|
||
|
statsDone <- struct{}{}
|
||
|
}
|
||
|
},
|
||
|
}
|
||
|
|
||
|
t.Parallel()
|
||
|
down := &httpdown.HTTP{Stats: hc}
|
||
|
_, err := down.ListenAndServe(&http.Server{})
|
||
|
ensure.Err(t, err, regexp.MustCompile("listen tcp :80: bind: permission denied"))
|
||
|
<-statsDone
|
||
|
}
|
||
|
|
||
|
func TestHTTPSDefaultListenError(t *testing.T) {
|
||
|
if os.Getuid() == 0 {
|
||
|
t.Skip("cant run this test as root")
|
||
|
}
|
||
|
t.Parallel()
|
||
|
|
||
|
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
|
||
|
if err != nil {
|
||
|
t.Fatalf("error loading cert: %v", err)
|
||
|
}
|
||
|
|
||
|
down := &httpdown.HTTP{}
|
||
|
_, err = down.ListenAndServe(&http.Server{
|
||
|
TLSConfig: &tls.Config{
|
||
|
NextProtos: []string{"http/1.1"},
|
||
|
Certificates: []tls.Certificate{cert},
|
||
|
},
|
||
|
})
|
||
|
ensure.Err(t, err, regexp.MustCompile("listen tcp :443: bind: permission denied"))
|
||
|
}
|
||
|
|
||
|
func TestTLS(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
port, err := freeport.Get()
|
||
|
ensure.Nil(t, err)
|
||
|
|
||
|
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
|
||
|
if err != nil {
|
||
|
t.Fatalf("error loading cert: %v", err)
|
||
|
}
|
||
|
const count = 10000
|
||
|
hello := []byte("hello")
|
||
|
finOkHandler := make(chan struct{})
|
||
|
okHandler := func(w http.ResponseWriter, r *http.Request) {
|
||
|
defer close(finOkHandler)
|
||
|
w.WriteHeader(200)
|
||
|
for i := 0; i < count; i++ {
|
||
|
w.Write(hello)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
server := &http.Server{
|
||
|
Addr: fmt.Sprintf("0.0.0.0:%d", port),
|
||
|
Handler: http.HandlerFunc(okHandler),
|
||
|
TLSConfig: &tls.Config{
|
||
|
NextProtos: []string{"http/1.1"},
|
||
|
Certificates: []tls.Certificate{cert},
|
||
|
},
|
||
|
}
|
||
|
transport := &http.Transport{
|
||
|
TLSClientConfig: &tls.Config{
|
||
|
InsecureSkipVerify: true,
|
||
|
},
|
||
|
}
|
||
|
client := &http.Client{Transport: transport}
|
||
|
down := &httpdown.HTTP{}
|
||
|
s, err := down.ListenAndServe(server)
|
||
|
ensure.Nil(t, err)
|
||
|
res, err := client.Get(fmt.Sprintf("https://%s/", server.Addr))
|
||
|
ensure.Nil(t, err)
|
||
|
|
||
|
finStop := make(chan struct{})
|
||
|
go func() {
|
||
|
defer close(finStop)
|
||
|
ensure.Nil(t, s.Stop())
|
||
|
}()
|
||
|
|
||
|
actualBody, err := ioutil.ReadAll(res.Body)
|
||
|
ensure.Nil(t, err)
|
||
|
ensure.DeepEqual(t, actualBody, bytes.Repeat(hello, count))
|
||
|
ensure.Nil(t, res.Body.Close())
|
||
|
<-finOkHandler
|
||
|
<-finStop
|
||
|
}
|
||
|
|
||
|
// localhostCert is a PEM-encoded TLS cert with SAN IPs
|
||
|
// "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end
|
||
|
// of ASN.1 time).
|
||
|
// generated from src/pkg/crypto/tls:
|
||
|
// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
||
|
var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
|
||
|
MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
|
||
|
bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj
|
||
|
bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBALyCfqwwip8BvTKgVKGdmjZTU8DD
|
||
|
ndR+WALmFPIRqn89bOU3s30olKiqYEju/SFoEvMyFRT/TWEhXHDaufThqaMCAwEA
|
||
|
AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud
|
||
|
EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA
|
||
|
AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAr/09uy108p51rheIOSnz4zgduyTl
|
||
|
M+4AmRo8/U1twEZLgfAGG/GZjREv2y4mCEUIM3HebCAqlA5jpRg76Rf8jw==
|
||
|
-----END CERTIFICATE-----`)
|
||
|
|
||
|
// localhostKey is the private key for localhostCert.
|
||
|
var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||
|
MIIBOQIBAAJBALyCfqwwip8BvTKgVKGdmjZTU8DDndR+WALmFPIRqn89bOU3s30o
|
||
|
lKiqYEju/SFoEvMyFRT/TWEhXHDaufThqaMCAwEAAQJAPXuWUxTV8XyAt8VhNQER
|
||
|
LgzJcUKb9JVsoS1nwXgPksXnPDKnL9ax8VERrdNr+nZbj2Q9cDSXBUovfdtehcdP
|
||
|
qQIhAO48ZsPylbTrmtjDEKiHT2Ik04rLotZYS2U873J6I7WlAiEAypDjYxXyafv/
|
||
|
Yo1pm9onwcetQKMW8CS3AjuV9Axzj6cCIEx2Il19fEMG4zny0WPlmbrcKvD/DpJQ
|
||
|
4FHrzsYlIVTpAiAas7S1uAvneqd0l02HlN9OxQKKlbUNXNme+rnOnOGS2wIgS0jW
|
||
|
zl1jvrOSJeP1PpAHohWz6LOhEr8uvltWkN6x3vE=
|
||
|
-----END RSA PRIVATE KEY-----`)
|