mirror of
https://github.com/openfaas/faas.git
synced 2025-06-23 23:33:25 +00:00
Verify not goroutine leaks in the log proxy
**What** - Add test to verify that the log proxy shutsdown correctly when the client cancels - Add test to verify that the log proxy shutsdown correctly when the logs provider closes the connection Signed-off-by: Lucas Roesler <roesler.lucas@gmail.com>
This commit is contained in:
committed by
Alex Ellis
parent
e7e91ecd15
commit
00c734a136
133
gateway/handlers/logs_test.go
Normal file
133
gateway/handlers/logs_test.go
Normal file
@ -0,0 +1,133 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
func Test_logsProxyDoesNotLeakGoroutinesWhenProviderClosesConnection(t *testing.T) {
|
||||
defer goleak.VerifyNoLeaks(t)
|
||||
|
||||
expectedMsg := "name: funcFoo msg: test message"
|
||||
|
||||
// mock log provider that sends one line and immediately closes the connection
|
||||
mockLogsUpstreamEndpoint := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
t.Fatalf("expected method '%s' but got '%s'", http.MethodGet, r.Method)
|
||||
}
|
||||
|
||||
if r.URL.Path != upstreamLogsEndpoint {
|
||||
t.Fatalf("expected path '%s' but got '%s'", upstreamLogsEndpoint, r.URL.Path)
|
||||
}
|
||||
|
||||
w.Header().Set(http.CanonicalHeaderKey("Connection"), "Keep-Alive")
|
||||
w.Header().Set(http.CanonicalHeaderKey("Transfer-Encoding"), "chunked")
|
||||
w.Header().Set(http.CanonicalHeaderKey("Content-Type"), "application/x-ndjson")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
msg := fmt.Sprintf("name: %s msg: test message", r.URL.Query().Get("name"))
|
||||
_, err := w.Write([]byte(msg))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to write test log message: %s", err)
|
||||
}
|
||||
}))
|
||||
defer mockLogsUpstreamEndpoint.Close()
|
||||
|
||||
logProviderURL, _ := url.Parse(mockLogsUpstreamEndpoint.URL)
|
||||
|
||||
logHandler := NewLogHandlerFunc(*logProviderURL)
|
||||
testSrv := httptest.NewServer(http.HandlerFunc(logHandler))
|
||||
defer testSrv.Close()
|
||||
|
||||
resp, err := http.Get(testSrv.URL + "?name=funcFoo")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error sneding log request: %s", err)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error reading teh response body: %s", err)
|
||||
}
|
||||
|
||||
if string(body) != string(expectedMsg) {
|
||||
t.Fatalf("expected log message %s, got: %s", expectedMsg, body)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_logsProxyDoesNotLeakGoroutinesWhenClientClosesConnection(t *testing.T) {
|
||||
defer goleak.VerifyNoLeaks(t)
|
||||
|
||||
// mock log provider that sends one line and holds until we cancel the context
|
||||
mockLogsUpstreamEndpoint := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
cn, ok := w.(http.CloseNotifier)
|
||||
if !ok {
|
||||
http.Error(w, "cannot stream", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
flusher, ok := w.(http.Flusher)
|
||||
if !ok {
|
||||
http.Error(w, "cannot stream", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method != http.MethodGet {
|
||||
t.Fatalf("expected method '%s' but got '%s'", http.MethodGet, r.Method)
|
||||
}
|
||||
|
||||
if r.URL.Path != upstreamLogsEndpoint {
|
||||
t.Fatalf("expected path '%s' but got '%s'", upstreamLogsEndpoint, r.URL.Path)
|
||||
}
|
||||
|
||||
w.Header().Set(http.CanonicalHeaderKey("Connection"), "Keep-Alive")
|
||||
w.Header().Set(http.CanonicalHeaderKey("Transfer-Encoding"), "chunked")
|
||||
w.Header().Set(http.CanonicalHeaderKey("Content-Type"), "application/x-ndjson")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
msg := fmt.Sprintf("name: %s msg: test message", r.URL.Query().Get("name"))
|
||||
_, err := w.Write([]byte(msg))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to write test log message: %s", err)
|
||||
}
|
||||
|
||||
flusher.Flush()
|
||||
|
||||
// "wait for connection to close"
|
||||
<-cn.CloseNotify()
|
||||
|
||||
}))
|
||||
defer mockLogsUpstreamEndpoint.Close()
|
||||
|
||||
logProviderURL, _ := url.Parse(mockLogsUpstreamEndpoint.URL)
|
||||
|
||||
logHandler := NewLogHandlerFunc(*logProviderURL)
|
||||
testSrv := httptest.NewServer(http.HandlerFunc(logHandler))
|
||||
defer testSrv.Close()
|
||||
|
||||
reqContext, cancel := context.WithCancel(context.Background())
|
||||
req, _ := http.NewRequest(http.MethodGet, testSrv.URL+"?name=funcFoo", nil)
|
||||
|
||||
req = req.WithContext(reqContext)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error sending log request: %s", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer resp.Body.Close()
|
||||
_, err := ioutil.ReadAll(resp.Body)
|
||||
if err != context.Canceled {
|
||||
t.Fatalf("unexpected error reading the response body: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
cancel()
|
||||
}
|
Reference in New Issue
Block a user