2020-10-27 22:27:15 +00:00
|
|
|
package connection
|
|
|
|
|
|
|
|
import (
|
2021-07-16 15:14:37 +00:00
|
|
|
"context"
|
2020-10-27 22:27:15 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
2020-11-18 11:53:59 +00:00
|
|
|
"testing"
|
2020-10-27 22:27:15 +00:00
|
|
|
"time"
|
|
|
|
|
2021-10-25 18:51:52 +00:00
|
|
|
"github.com/gobwas/ws/wsutil"
|
2020-11-25 06:55:13 +00:00
|
|
|
"github.com/rs/zerolog"
|
2020-11-18 11:53:59 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
2021-07-16 15:14:37 +00:00
|
|
|
|
|
|
|
"github.com/cloudflare/cloudflared/ingress"
|
2020-10-27 22:27:15 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
largeFileSize = 2 * 1024 * 1024
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2021-07-16 15:14:37 +00:00
|
|
|
unusedWarpRoutingService = (*ingress.WarpRoutingService)(nil)
|
|
|
|
testConfig = &Config{
|
2020-12-09 21:46:53 +00:00
|
|
|
OriginProxy: &mockOriginProxy{},
|
|
|
|
GracePeriod: time.Millisecond * 100,
|
2020-10-27 22:27:15 +00:00
|
|
|
}
|
2020-11-25 06:55:13 +00:00
|
|
|
log = zerolog.Nop()
|
2020-10-27 22:27:15 +00:00
|
|
|
testOriginURL = &url.URL{
|
|
|
|
Scheme: "https",
|
|
|
|
Host: "connectiontest.argotunnel.com",
|
|
|
|
}
|
|
|
|
testLargeResp = make([]byte, largeFileSize)
|
|
|
|
)
|
|
|
|
|
|
|
|
type testRequest struct {
|
|
|
|
name string
|
|
|
|
endpoint string
|
|
|
|
expectedStatus int
|
|
|
|
expectedBody []byte
|
|
|
|
isProxyError bool
|
|
|
|
}
|
|
|
|
|
2021-07-16 15:14:37 +00:00
|
|
|
type mockOriginProxy struct{}
|
2020-10-27 22:27:15 +00:00
|
|
|
|
2021-07-16 15:14:37 +00:00
|
|
|
func (moc *mockOriginProxy) ProxyHTTP(
|
|
|
|
w ResponseWriter,
|
|
|
|
req *http.Request,
|
|
|
|
isWebsocket bool,
|
|
|
|
) error {
|
|
|
|
if isWebsocket {
|
2021-10-25 18:51:52 +00:00
|
|
|
return wsEndpoint(w, req)
|
2020-10-27 22:27:15 +00:00
|
|
|
}
|
2021-07-16 15:14:37 +00:00
|
|
|
switch req.URL.Path {
|
2020-10-27 22:27:15 +00:00
|
|
|
case "/ok":
|
|
|
|
originRespEndpoint(w, http.StatusOK, []byte(http.StatusText(http.StatusOK)))
|
|
|
|
case "/large_file":
|
|
|
|
originRespEndpoint(w, http.StatusOK, testLargeResp)
|
|
|
|
case "/400":
|
|
|
|
originRespEndpoint(w, http.StatusBadRequest, []byte(http.StatusText(http.StatusBadRequest)))
|
|
|
|
case "/500":
|
|
|
|
originRespEndpoint(w, http.StatusInternalServerError, []byte(http.StatusText(http.StatusInternalServerError)))
|
|
|
|
case "/error":
|
|
|
|
return fmt.Errorf("Failed to proxy to origin")
|
|
|
|
default:
|
|
|
|
originRespEndpoint(w, http.StatusNotFound, []byte("page not found"))
|
|
|
|
}
|
|
|
|
return nil
|
2021-07-16 15:14:37 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (moc *mockOriginProxy) ProxyTCP(
|
|
|
|
ctx context.Context,
|
|
|
|
rwa ReadWriteAcker,
|
|
|
|
r *TCPRequest,
|
|
|
|
) error {
|
|
|
|
return nil
|
2020-10-27 22:27:15 +00:00
|
|
|
}
|
|
|
|
|
2021-10-25 18:51:52 +00:00
|
|
|
type nowriter struct {
|
|
|
|
io.Reader
|
2020-10-27 22:27:15 +00:00
|
|
|
}
|
|
|
|
|
2021-10-25 18:51:52 +00:00
|
|
|
func (nowriter) Write(p []byte) (int, error) {
|
|
|
|
return 0, fmt.Errorf("Writer not implemented")
|
2020-10-27 22:27:15 +00:00
|
|
|
}
|
|
|
|
|
2021-10-25 18:51:52 +00:00
|
|
|
func wsEndpoint(w ResponseWriter, r *http.Request) error {
|
2020-10-27 22:27:15 +00:00
|
|
|
resp := &http.Response{
|
|
|
|
StatusCode: http.StatusSwitchingProtocols,
|
|
|
|
}
|
2021-10-25 18:51:52 +00:00
|
|
|
_ = w.WriteRespHeaders(resp.StatusCode, resp.Header)
|
|
|
|
clientReader := nowriter{r.Body}
|
2020-10-27 22:27:15 +00:00
|
|
|
go func() {
|
2021-10-25 18:51:52 +00:00
|
|
|
for {
|
|
|
|
data, err := wsutil.ReadClientText(clientReader)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err := wsutil.WriteServerText(w, data); err != nil {
|
|
|
|
return
|
|
|
|
}
|
2020-10-27 22:27:15 +00:00
|
|
|
}
|
|
|
|
}()
|
2021-10-25 18:51:52 +00:00
|
|
|
<-r.Context().Done()
|
2020-10-27 22:27:15 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func originRespEndpoint(w ResponseWriter, status int, data []byte) {
|
|
|
|
resp := &http.Response{
|
|
|
|
StatusCode: status,
|
|
|
|
}
|
2020-12-09 21:46:53 +00:00
|
|
|
_ = w.WriteRespHeaders(resp.StatusCode, resp.Header)
|
2020-11-25 06:55:13 +00:00
|
|
|
_, _ = w.Write(data)
|
2020-10-27 22:27:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type mockConnectedFuse struct{}
|
|
|
|
|
|
|
|
func (mcf mockConnectedFuse) Connected() {}
|
|
|
|
|
|
|
|
func (mcf mockConnectedFuse) IsConnected() bool {
|
|
|
|
return true
|
|
|
|
}
|
2020-11-18 11:53:59 +00:00
|
|
|
|
|
|
|
func TestIsEventStream(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
headers http.Header
|
|
|
|
isEventStream bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
headers: newHeader("Content-Type", "text/event-stream"),
|
|
|
|
isEventStream: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
headers: newHeader("content-type", "text/event-stream"),
|
|
|
|
isEventStream: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
headers: newHeader("Content-Type", "text/event-stream; charset=utf-8"),
|
|
|
|
isEventStream: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
headers: newHeader("Content-Type", "application/json"),
|
|
|
|
isEventStream: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
headers: http.Header{},
|
|
|
|
isEventStream: false,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, test := range tests {
|
|
|
|
assert.Equal(t, test.isEventStream, IsServerSentEvent(test.headers))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newHeader(key, value string) http.Header {
|
|
|
|
header := http.Header{}
|
|
|
|
header.Add(key, value)
|
|
|
|
return header
|
|
|
|
}
|