1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
// Copyright 2017 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !windows
package docker
import (
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"reflect"
"sort"
"sync"
"testing"
"time"
)
func TestClientDoConcurrentStress(t *testing.T) {
t.Parallel()
var reqs []*http.Request
var mu sync.Mutex
handler := http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
mu.Lock()
reqs = append(reqs, r)
mu.Unlock()
})
var nativeSrvs []*httptest.Server
for i := 0; i < 3; i++ {
srv, cleanup, err := newNativeServer(handler)
if err != nil {
t.Fatal(err)
}
defer cleanup()
nativeSrvs = append(nativeSrvs, srv)
}
tests := []struct {
testCase string
srv *httptest.Server
scheme string
withTimeout bool
withTLSServer bool
withTLSClient bool
}{
{testCase: "http server", srv: httptest.NewUnstartedServer(handler), scheme: "http"},
{testCase: "native server", srv: nativeSrvs[0], scheme: nativeProtocol},
{testCase: "http with timeout", srv: httptest.NewUnstartedServer(handler), scheme: "http", withTimeout: true},
{testCase: "native with timeout", srv: nativeSrvs[1], scheme: nativeProtocol, withTimeout: true},
{testCase: "http with tls", srv: httptest.NewUnstartedServer(handler), scheme: "https", withTLSServer: true, withTLSClient: true},
{testCase: "native with client-only tls", srv: nativeSrvs[2], scheme: nativeProtocol, withTLSServer: false, withTLSClient: nativeProtocol == unixProtocol}, // TLS client only works with unix protocol
}
for _, tt := range tests {
tt := tt
t.Run(tt.testCase, func(t *testing.T) {
reqs = nil
var client *Client
var err error
endpoint := tt.scheme + "://" + tt.srv.Listener.Addr().String()
if tt.withTLSServer {
tt.srv.StartTLS()
} else {
tt.srv.Start()
}
defer tt.srv.Close()
if tt.withTLSClient {
certPEMBlock, certErr := ioutil.ReadFile("testing/data/cert.pem")
if certErr != nil {
t.Fatal(certErr)
}
keyPEMBlock, certErr := ioutil.ReadFile("testing/data/key.pem")
if certErr != nil {
t.Fatal(certErr)
}
client, err = NewTLSClientFromBytes(endpoint, certPEMBlock, keyPEMBlock, nil)
} else {
client, err = NewClient(endpoint)
}
if err != nil {
t.Fatal(err)
}
if tt.withTimeout {
client.SetTimeout(time.Minute)
}
n := 50
wg := sync.WaitGroup{}
var paths []string
errsCh := make(chan error, 3*n)
waiters := make(chan CloseWaiter, n)
for i := 0; i < n; i++ {
path := fmt.Sprintf("/%05d", i)
paths = append(paths, http.MethodGet+path)
paths = append(paths, http.MethodPost+path)
paths = append(paths, "HEAD"+path)
wg.Add(1)
go func() {
defer wg.Done()
_, clientErr := client.do(http.MethodGet, path, doOptions{})
if clientErr != nil {
errsCh <- clientErr
}
clientErr = client.stream(http.MethodPost, path, streamOptions{})
if clientErr != nil {
errsCh <- clientErr
}
cw, clientErr := client.hijack("HEAD", path, hijackOptions{})
if clientErr != nil {
errsCh <- clientErr
} else {
waiters <- cw
}
}()
}
wg.Wait()
close(errsCh)
close(waiters)
for cw := range waiters {
cw.Wait()
cw.Close()
}
for err = range errsCh {
t.Error(err)
}
var reqPaths []string
for _, r := range reqs {
reqPaths = append(reqPaths, r.Method+r.URL.Path)
}
sort.Strings(paths)
sort.Strings(reqPaths)
if !reflect.DeepEqual(reqPaths, paths) {
t.Fatalf("expected server request paths to equal %v, got: %v", paths, reqPaths)
}
})
}
}
|