forked from quic-go/quic-go
Merge pull request #537 from lucas-clemente/wip-no-docker
Replace docker with a directly launched Chrome for integration tests
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
|
||||
addons:
|
||||
hosts:
|
||||
@@ -6,9 +6,6 @@ addons:
|
||||
|
||||
language: go
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
go:
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
@@ -30,6 +27,8 @@ before_install:
|
||||
- go get github.com/onsi/gomega
|
||||
- export GOARCH=$TRAVIS_GOARCH
|
||||
- go env # for debugging
|
||||
- "export DISPLAY=:99.0"
|
||||
- "Xvfb $DISPLAY &"
|
||||
|
||||
script:
|
||||
- .travis/script.sh
|
||||
|
||||
@@ -9,5 +9,5 @@ fi
|
||||
|
||||
if [ ${TESTMODE} == "integration" ]; then
|
||||
ginkgo --randomizeAllSpecs --randomizeSuites --trace --progress -focus "Benchmark"
|
||||
ginkgo -r --randomizeAllSpecs --randomizeSuites --trace --progress integrationtests
|
||||
ginkgo -v -r --randomizeAllSpecs --randomizeSuites --trace --progress integrationtests
|
||||
fi
|
||||
|
||||
@@ -11,4 +11,5 @@
|
||||
- Remove the `tls.Config` from the `quic.Config`. The `tls.Config` must now be passed to the `Dial` and `Listen` functions as a separate parameter. See the [Godoc](https://godoc.org/github.com/lucas-clemente/quic-go) for details.
|
||||
- Changed the log level environment variable to only accept strings ("DEBUG", "INFO", "ERROR"), see [the wiki](https://github.com/lucas-clemente/quic-go/wiki/Logging) for more details.
|
||||
- Rename the `h2quic.QuicRoundTripper` to `h2quic.RoundTripper`
|
||||
- Changed `h2quic.Server.Serve()` to accept a `net.PacketConn`
|
||||
- Various bugfixes
|
||||
|
||||
@@ -69,11 +69,11 @@ func (s *Server) ListenAndServeTLS(certFile, keyFile string) error {
|
||||
}
|
||||
|
||||
// Serve an existing UDP connection.
|
||||
func (s *Server) Serve(conn *net.UDPConn) error {
|
||||
func (s *Server) Serve(conn net.PacketConn) error {
|
||||
return s.serveImpl(s.TLSConfig, conn)
|
||||
}
|
||||
|
||||
func (s *Server) serveImpl(tlsConfig *tls.Config, conn *net.UDPConn) error {
|
||||
func (s *Server) serveImpl(tlsConfig *tls.Config, conn net.PacketConn) error {
|
||||
if s.Server == nil {
|
||||
return errors.New("use of h2quic.Server without http.Server")
|
||||
}
|
||||
|
||||
@@ -2,234 +2,142 @@ package integrationtests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/tebeka/selenium"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/onsi/gomega/gexec"
|
||||
)
|
||||
|
||||
const nImgs = 200
|
||||
const imgSize = 40
|
||||
const (
|
||||
nChromeRetries = 8
|
||||
)
|
||||
|
||||
func init() {
|
||||
http.HandleFunc("/tile", func(w http.ResponseWriter, r *http.Request) {
|
||||
// Small 40x40 png
|
||||
w.Write([]byte{
|
||||
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
||||
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28,
|
||||
0x01, 0x03, 0x00, 0x00, 0x00, 0xb6, 0x30, 0x2a, 0x2e, 0x00, 0x00, 0x00,
|
||||
0x03, 0x50, 0x4c, 0x54, 0x45, 0x5a, 0xc3, 0x5a, 0xad, 0x38, 0xaa, 0xdb,
|
||||
0x00, 0x00, 0x00, 0x0b, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0x63, 0x18,
|
||||
0x61, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x01, 0xe2, 0xb8, 0x75, 0x22, 0x00,
|
||||
0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
|
||||
})
|
||||
})
|
||||
func getChromePath() string {
|
||||
if runtime.GOOS == "darwin" {
|
||||
return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
}
|
||||
return "google-chrome"
|
||||
}
|
||||
|
||||
http.HandleFunc("/tiles", func(w http.ResponseWriter, r *http.Request) {
|
||||
io.WriteString(w, "<html><body>")
|
||||
for i := 0; i < nImgs; i++ {
|
||||
fmt.Fprintf(w, `<img src="/tile?cachebust=%d">`, i)
|
||||
func chromeTest(version protocol.VersionNumber, url string, blockUntilDone func()) {
|
||||
// Chrome sometimes starts but doesn't send any HTTP requests for no apparent reason.
|
||||
// Retry starting it a couple of times.
|
||||
for i := 0; i < nChromeRetries; i++ {
|
||||
if chromeTestImpl(version, url, blockUntilDone) {
|
||||
return
|
||||
}
|
||||
io.WriteString(w, "</body></html>")
|
||||
})
|
||||
}
|
||||
Fail("Chrome didn't hit the testing endpoints")
|
||||
}
|
||||
|
||||
func chromeTestImpl(version protocol.VersionNumber, url string, blockUntilDone func()) bool {
|
||||
userDataDir, err := ioutil.TempDir("", "quic-go-test-chrome-dir")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer os.RemoveAll(userDataDir)
|
||||
path := getChromePath()
|
||||
args := []string{
|
||||
"--disable-gpu",
|
||||
"--no-first-run=true",
|
||||
"--no-default-browser-check=true",
|
||||
"--user-data-dir=" + userDataDir,
|
||||
"--enable-quic=true",
|
||||
"--no-proxy-server=true",
|
||||
"--origin-to-force-quic-on=quic.clemente.io:443",
|
||||
fmt.Sprintf(`--host-resolver-rules=MAP quic.clemente.io:443 localhost:%s`, port),
|
||||
fmt.Sprintf("--quic-version=QUIC_VERSION_%d", version),
|
||||
url,
|
||||
}
|
||||
utils.Infof("Running chrome: %s '%s'", getChromePath(), strings.Join(args, "' '"))
|
||||
command := exec.Command(path, args...)
|
||||
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer session.Kill()
|
||||
const pollInterval = 100 * time.Millisecond
|
||||
const pollDuration = 10 * time.Second
|
||||
for i := 0; i < int(pollDuration/pollInterval); i++ {
|
||||
time.Sleep(pollInterval)
|
||||
if testEndpointCalled {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !testEndpointCalled {
|
||||
return false
|
||||
}
|
||||
blockUntilDone()
|
||||
return true
|
||||
}
|
||||
|
||||
var _ = Describe("Chrome tests", func() {
|
||||
// test disabled since it doesn't work with the configurable QUIC version in the server
|
||||
PIt("does not work with mismatching versions", func() {
|
||||
versionForUs := protocol.SupportedVersions[0]
|
||||
versionForChrome := protocol.SupportedVersions[1]
|
||||
|
||||
supportedVersionsBefore := protocol.SupportedVersions
|
||||
protocol.SupportedVersions = []protocol.VersionNumber{versionForUs}
|
||||
wd := getWebdriverForVersion(versionForChrome)
|
||||
|
||||
defer func() {
|
||||
protocol.SupportedVersions = supportedVersionsBefore
|
||||
wd.Close()
|
||||
}()
|
||||
|
||||
err := wd.Get("https://quic.clemente.io/hello")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
source, err := wd.PageSource()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(source).ToNot(ContainSubstring("Hello, World!\n"))
|
||||
})
|
||||
|
||||
for i := range protocol.SupportedVersions {
|
||||
version := protocol.SupportedVersions[i]
|
||||
|
||||
Context(fmt.Sprintf("with quic version %d", version), func() {
|
||||
var (
|
||||
wd selenium.WebDriver
|
||||
supportedVersionsBefore []protocol.VersionNumber
|
||||
)
|
||||
supportedVersionsBefore := protocol.SupportedVersions
|
||||
|
||||
BeforeEach(func() {
|
||||
if version == protocol.Version37 {
|
||||
Skip("Skipping Chrome test with QUIC version 37")
|
||||
}
|
||||
supportedVersionsBefore = protocol.SupportedVersions
|
||||
protocol.SupportedVersions = []protocol.VersionNumber{version}
|
||||
})
|
||||
|
||||
JustBeforeEach(func() {
|
||||
wd = getWebdriverForVersion(version)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
wd.Close()
|
||||
protocol.SupportedVersions = supportedVersionsBefore
|
||||
})
|
||||
|
||||
It("loads a simple hello world page using quic", func(done Done) {
|
||||
err := wd.Get("https://quic.clemente.io/hello")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
source, err := wd.PageSource()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(source).To(ContainSubstring("Hello, World!\n"))
|
||||
close(done)
|
||||
}, 5)
|
||||
|
||||
It("loads a large number of files", func(done Done) {
|
||||
err := wd.Get("https://quic.clemente.io/tiles")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Eventually(func() error {
|
||||
imgs, err := wd.FindElements("tag name", "img")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(imgs) != nImgs {
|
||||
return fmt.Errorf("expected number of images to be %d, got %d", nImgs, len(imgs))
|
||||
}
|
||||
for i, img := range imgs {
|
||||
size, err := img.Size()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if size.Height != imgSize || size.Width != imgSize {
|
||||
return fmt.Errorf("image %d did not have expected size", i)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}, 5).ShouldNot(HaveOccurred())
|
||||
close(done)
|
||||
}, 20)
|
||||
|
||||
It("downloads a small file", func() {
|
||||
dataMan.GenerateData(dataLen)
|
||||
err := wd.Get("https://quic.clemente.io/data")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Eventually(func() int { return getDownloadSize("data") }, 30, 0.1).Should(Equal(dataLen))
|
||||
Expect(getDownloadMD5("data")).To(Equal(dataMan.GetMD5()))
|
||||
}, 60)
|
||||
chromeTest(
|
||||
version,
|
||||
fmt.Sprintf("https://quic.clemente.io/downloadtest?num=1&len=%d", dataLen),
|
||||
waitForDone,
|
||||
)
|
||||
})
|
||||
|
||||
It("downloads a large file", func() {
|
||||
dataMan.GenerateData(dataLongLen)
|
||||
err := wd.Get("https://quic.clemente.io/data")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Eventually(func() int { return getDownloadSize("data") }, 90, 0.5).Should(Equal(dataLongLen))
|
||||
Expect(getDownloadMD5("data")).To(Equal(dataMan.GetMD5()))
|
||||
}, 100)
|
||||
chromeTest(
|
||||
version,
|
||||
fmt.Sprintf("https://quic.clemente.io/downloadtest?num=1&len=%d", dataLongLen),
|
||||
waitForDone,
|
||||
)
|
||||
})
|
||||
|
||||
It("loads a large number of files", func() {
|
||||
chromeTest(
|
||||
version,
|
||||
"https://quic.clemente.io/downloadtest?num=4&len=100",
|
||||
waitForDone,
|
||||
)
|
||||
})
|
||||
|
||||
It("uploads a small file", func() {
|
||||
dataMan.GenerateData(dataLen)
|
||||
data := dataMan.GetData()
|
||||
dir, err := ioutil.TempDir("", "quic-upload-src")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(dir)
|
||||
tmpfn := filepath.Join(dir, "data.dat")
|
||||
err = ioutil.WriteFile(tmpfn, data, 0777)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
copyFileToDocker(tmpfn)
|
||||
|
||||
err = wd.Get("https://quic.clemente.io/uploadform?num=1")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
elem, err := wd.FindElement(selenium.ByCSSSelector, "#upload_0")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = elem.SendKeys("/home/seluser/data.dat")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Eventually(func() error { return elem.Submit() }, 30, 0.1).ShouldNot(HaveOccurred())
|
||||
|
||||
file := filepath.Join(uploadDir, "data.dat")
|
||||
Expect(getFileSize(file)).To(Equal(dataLen))
|
||||
Expect(getFileMD5(file)).To(Equal(dataMan.GetMD5()))
|
||||
chromeTest(
|
||||
version,
|
||||
fmt.Sprintf("https://quic.clemente.io/uploadtest?num=1&len=%d", dataLen),
|
||||
waitForNUploaded(1),
|
||||
)
|
||||
})
|
||||
|
||||
It("uploads a large file", func() {
|
||||
dataMan.GenerateData(dataLongLen)
|
||||
data := dataMan.GetData()
|
||||
dir, err := ioutil.TempDir("", "quic-upload-src")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(dir)
|
||||
tmpfn := filepath.Join(dir, "data.dat")
|
||||
err = ioutil.WriteFile(tmpfn, data, 0777)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
copyFileToDocker(tmpfn)
|
||||
|
||||
err = wd.Get("https://quic.clemente.io/uploadform?num=1")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
elem, err := wd.FindElement(selenium.ByCSSSelector, "#upload_0")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = elem.SendKeys("/home/seluser/data.dat")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Eventually(func() error { return elem.Submit() }, 90, 0.5).ShouldNot(HaveOccurred())
|
||||
|
||||
file := filepath.Join(uploadDir, "data.dat")
|
||||
Expect(getFileSize(file)).To(Equal(dataLongLen))
|
||||
Expect(getFileMD5(file)).To(Equal(dataMan.GetMD5()))
|
||||
chromeTest(
|
||||
version,
|
||||
fmt.Sprintf("https://quic.clemente.io/uploadtest?num=1&len=%d", dataLongLen),
|
||||
waitForNUploaded(1),
|
||||
)
|
||||
})
|
||||
|
||||
// this test takes a long time because it copies every file into the docker container one by one
|
||||
// unfortunately, docker doesn't support copying multiple files at once
|
||||
// see https://github.com/docker/docker/issues/7710
|
||||
It("uploads many small files", func() {
|
||||
num := protocol.MaxStreamsPerConnection + 20
|
||||
|
||||
dir, err := ioutil.TempDir("", "quic-upload-src")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
var md5s [][]byte
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
dataMan.GenerateData(dataLen)
|
||||
data := dataMan.GetData()
|
||||
md5s = append(md5s, dataMan.GetMD5())
|
||||
tmpfn := filepath.Join(dir, "data_"+strconv.Itoa(i)+".dat")
|
||||
err = ioutil.WriteFile(tmpfn, data, 0777)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
copyFileToDocker(tmpfn)
|
||||
}
|
||||
|
||||
err = wd.Get("https://quic.clemente.io/uploadform?num=" + strconv.Itoa(num))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
var elem selenium.WebElement
|
||||
elem, err = wd.FindElement(selenium.ByCSSSelector, "#upload_"+strconv.Itoa(i))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = elem.SendKeys("/home/seluser/data_" + strconv.Itoa(i) + ".dat")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
elem, err := wd.FindElement(selenium.ByCSSSelector, "#form")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Eventually(func() error { return elem.Submit() }, 30, 0.1).ShouldNot(HaveOccurred())
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
file := filepath.Join(uploadDir, "data_"+strconv.Itoa(i)+".dat")
|
||||
Expect(getFileSize(file)).To(Equal(dataLen))
|
||||
Expect(getFileMD5(file)).To(Equal(md5s[i]))
|
||||
}
|
||||
chromeTest(
|
||||
version,
|
||||
fmt.Sprintf("https://quic.clemente.io/uploadtest?num=%d&len=%d", num, dataLen),
|
||||
waitForNUploaded(num),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ var _ = Describe("Drop Proxy", func() {
|
||||
"https://quic.clemente.io/data",
|
||||
)
|
||||
|
||||
session, err := Start(command, GinkgoWriter, GinkgoWriter)
|
||||
session, err := Start(command, nil, GinkgoWriter)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer session.Kill()
|
||||
Eventually(session, 20).Should(Exit(0))
|
||||
|
||||
@@ -34,7 +34,7 @@ var _ = Describe("Integration tests", func() {
|
||||
"--port="+port,
|
||||
"https://quic.clemente.io/hello",
|
||||
)
|
||||
session, err := Start(command, GinkgoWriter, GinkgoWriter)
|
||||
session, err := Start(command, nil, GinkgoWriter)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer session.Kill()
|
||||
Eventually(session, 5).Should(Exit(0))
|
||||
@@ -51,7 +51,7 @@ var _ = Describe("Integration tests", func() {
|
||||
"--body=foo",
|
||||
"https://quic.clemente.io/echo",
|
||||
)
|
||||
session, err := Start(command, GinkgoWriter, GinkgoWriter)
|
||||
session, err := Start(command, nil, GinkgoWriter)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer session.Kill()
|
||||
Eventually(session, 5).Should(Exit(0))
|
||||
|
||||
@@ -2,35 +2,27 @@ package integrationtests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/h2quic"
|
||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/testdata"
|
||||
"github.com/tebeka/selenium"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/onsi/gomega/gbytes"
|
||||
"github.com/onsi/gomega/gexec"
|
||||
|
||||
"testing"
|
||||
)
|
||||
@@ -41,17 +33,17 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
server *h2quic.Server
|
||||
dataMan dataManager
|
||||
port string
|
||||
uploadDir string
|
||||
clientPath string // path of the quic_client
|
||||
serverPath string // path of the quic_server
|
||||
server *h2quic.Server
|
||||
dataMan dataManager
|
||||
port string
|
||||
clientPath string
|
||||
serverPath string
|
||||
nFilesUploaded int32
|
||||
testEndpointCalled bool
|
||||
doneCalled bool
|
||||
|
||||
logFileName string // the log file set in the ginkgo flags
|
||||
logFile *os.File
|
||||
|
||||
docker *gexec.Session
|
||||
)
|
||||
|
||||
func TestIntegration(t *testing.T) {
|
||||
@@ -59,14 +51,7 @@ func TestIntegration(t *testing.T) {
|
||||
RunSpecs(t, "Integration Tests Suite")
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
setupHTTPHandlers()
|
||||
setupSelenium()
|
||||
})
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
stopSelenium()
|
||||
}, 10)
|
||||
var _ = BeforeSuite(setupHTTPHandlers)
|
||||
|
||||
// read the logfile command line flag
|
||||
// to set call ginkgo -- -logfile=log.txt
|
||||
@@ -77,14 +62,6 @@ func init() {
|
||||
var _ = BeforeEach(func() {
|
||||
// set custom time format for logs
|
||||
utils.SetLogTimeFormat("15:04:05.000")
|
||||
|
||||
// create a new uploadDir for every test
|
||||
var err error
|
||||
uploadDir, err = ioutil.TempDir("", "quic-upload-dest")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.MkdirAll(uploadDir, os.ModeDir|0777)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
_, thisfile, _, ok := runtime.Caller(0)
|
||||
if !ok {
|
||||
Fail("Failed to get current path")
|
||||
@@ -93,6 +70,7 @@ var _ = BeforeEach(func() {
|
||||
serverPath = filepath.Join(thisfile, fmt.Sprintf("../../../quic-clients/server-%s-debug", runtime.GOOS))
|
||||
|
||||
if len(logFileName) > 0 {
|
||||
var err error
|
||||
logFile, err = os.Create("./log.txt")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
log.SetOutput(logFile)
|
||||
@@ -105,18 +83,13 @@ var _ = JustBeforeEach(startQuicServer)
|
||||
var _ = AfterEach(func() {
|
||||
stopQuicServer()
|
||||
|
||||
// remove uploadDir
|
||||
if len(uploadDir) < 20 {
|
||||
panic("uploadDir too short")
|
||||
}
|
||||
os.RemoveAll(uploadDir)
|
||||
|
||||
// remove downloaded file in docker container
|
||||
removeDownload("data")
|
||||
|
||||
if len(logFileName) > 0 {
|
||||
_ = logFile.Close()
|
||||
}
|
||||
|
||||
nFilesUploaded = 0
|
||||
doneCalled = false
|
||||
testEndpointCalled = false
|
||||
})
|
||||
|
||||
func setupHTTPHandlers() {
|
||||
@@ -136,6 +109,16 @@ func setupHTTPHandlers() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
http.HandleFunc("/prdata", func(w http.ResponseWriter, r *http.Request) {
|
||||
defer GinkgoRecover()
|
||||
sl := r.URL.Query().Get("len")
|
||||
l, err := strconv.Atoi(sl)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
data := generatePRData(l)
|
||||
_, err = w.Write(data)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
http.HandleFunc("/echo", func(w http.ResponseWriter, r *http.Request) {
|
||||
defer GinkgoRecover()
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
@@ -144,46 +127,45 @@ func setupHTTPHandlers() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
// requires the num GET parameter, e.g. /uploadform?num=2
|
||||
// will create num input fields for uploading files
|
||||
http.HandleFunc("/uploadform", func(w http.ResponseWriter, r *http.Request) {
|
||||
// Requires the len & num GET parameters, e.g. /uploadtest?len=100&num=1
|
||||
http.HandleFunc("/uploadtest", func(w http.ResponseWriter, r *http.Request) {
|
||||
defer GinkgoRecover()
|
||||
num, err := strconv.Atoi(r.URL.Query().Get("num"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
response := "<html><body>\n<form id='form' action='https://quic.clemente.io/uploadhandler' method='post' enctype='multipart/form-data'>"
|
||||
for i := 0; i < num; i++ {
|
||||
response += "<input type='file' id='upload_" + strconv.Itoa(i) + "' name='uploadfile_" + strconv.Itoa(i) + "' />"
|
||||
}
|
||||
response += "</form><body></html>"
|
||||
_, err = io.WriteString(w, response)
|
||||
response := uploadHTML
|
||||
response = strings.Replace(response, "LENGTH", r.URL.Query().Get("len"), -1)
|
||||
response = strings.Replace(response, "NUM", r.URL.Query().Get("num"), -1)
|
||||
_, err := io.WriteString(w, response)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
testEndpointCalled = true
|
||||
})
|
||||
|
||||
// Requires the len & num GET parameters, e.g. /downloadtest?len=100&num=1
|
||||
http.HandleFunc("/downloadtest", func(w http.ResponseWriter, r *http.Request) {
|
||||
defer GinkgoRecover()
|
||||
response := downloadHTML
|
||||
response = strings.Replace(response, "LENGTH", r.URL.Query().Get("len"), -1)
|
||||
response = strings.Replace(response, "NUM", r.URL.Query().Get("num"), -1)
|
||||
_, err := io.WriteString(w, response)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
testEndpointCalled = true
|
||||
})
|
||||
|
||||
http.HandleFunc("/uploadhandler", func(w http.ResponseWriter, r *http.Request) {
|
||||
defer GinkgoRecover()
|
||||
|
||||
err := r.ParseMultipartForm(100 * (1 << 20)) // max. 100 MB
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
count := 0
|
||||
for {
|
||||
var file multipart.File
|
||||
var handler *multipart.FileHeader
|
||||
file, handler, err = r.FormFile("uploadfile_" + strconv.Itoa(count))
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
count++
|
||||
f, err2 := os.OpenFile(path.Join(uploadDir, handler.Filename), os.O_WRONLY|os.O_CREATE, 0666)
|
||||
Expect(err2).ToNot(HaveOccurred())
|
||||
io.Copy(f, file)
|
||||
f.Close()
|
||||
file.Close()
|
||||
}
|
||||
Expect(count).ToNot(BeZero()) // there have been at least one uploaded file in this request
|
||||
|
||||
_, err = io.WriteString(w, "")
|
||||
l, err := strconv.Atoi(r.URL.Query().Get("len"))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
defer r.Body.Close()
|
||||
actual, err := ioutil.ReadAll(r.Body)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(bytes.Equal(actual, generatePRData(l))).To(BeTrue())
|
||||
|
||||
atomic.AddInt32(&nFilesUploaded, 1)
|
||||
})
|
||||
|
||||
http.HandleFunc("/done", func(w http.ResponseWriter, r *http.Request) {
|
||||
doneCalled = true
|
||||
})
|
||||
}
|
||||
|
||||
@@ -210,137 +192,94 @@ func stopQuicServer() {
|
||||
Expect(server.Close()).NotTo(HaveOccurred())
|
||||
}
|
||||
|
||||
func setupSelenium() {
|
||||
var err error
|
||||
pullCmd := exec.Command("docker", "pull", "lclemente/standalone-chrome:dev")
|
||||
pull, err := gexec.Start(pullCmd, GinkgoWriter, GinkgoWriter)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
// Assuming a download at 10 Mbit/s
|
||||
Eventually(pull, 10*time.Minute).Should(gexec.Exit(0))
|
||||
|
||||
dockerCmd := exec.Command(
|
||||
"docker",
|
||||
"run",
|
||||
"-i",
|
||||
"--rm",
|
||||
"-p=4444:4444",
|
||||
"--name", "quic-test-selenium",
|
||||
"lclemente/standalone-chrome:dev",
|
||||
)
|
||||
docker, err = gexec.Start(dockerCmd, GinkgoWriter, GinkgoWriter)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Eventually(docker.Out, 10).Should(gbytes.Say("Selenium Server is up and running"))
|
||||
func waitForDone() {
|
||||
Eventually(func() bool { return doneCalled }, 60).Should(BeTrue())
|
||||
}
|
||||
|
||||
func stopSelenium() {
|
||||
docker.Interrupt().Wait(10)
|
||||
}
|
||||
|
||||
func getWebdriverForVersion(version protocol.VersionNumber) selenium.WebDriver {
|
||||
caps := selenium.Capabilities{
|
||||
"browserName": "chrome",
|
||||
"chromeOptions": map[string]interface{}{
|
||||
"args": []string{
|
||||
"--enable-quic",
|
||||
"--no-proxy-server",
|
||||
"--origin-to-force-quic-on=quic.clemente.io:443",
|
||||
fmt.Sprintf(`--host-resolver-rules=MAP quic.clemente.io:443 %s:%s`, GetLocalIP(), port),
|
||||
fmt.Sprintf(`--quic-version=QUIC_VERSION_%d`, version),
|
||||
},
|
||||
},
|
||||
func waitForNUploaded(expected int) func() {
|
||||
return func() {
|
||||
Eventually(func() int32 {
|
||||
return atomic.LoadInt32(&nFilesUploaded)
|
||||
}, 60).Should(BeEquivalentTo(expected))
|
||||
}
|
||||
wd, err := selenium.NewRemote(caps, "http://localhost:4444/wd/hub")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return wd
|
||||
}
|
||||
|
||||
func GetLocalIP() string {
|
||||
// First, try finding interface docker0
|
||||
i, err := net.InterfaceByName("docker0")
|
||||
if err == nil {
|
||||
var addrs []net.Addr
|
||||
addrs, err = i.Addrs()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return addrs[0].(*net.IPNet).IP.String()
|
||||
}
|
||||
const commonJS = `
|
||||
var buf = new ArrayBuffer(LENGTH);
|
||||
var prng = new Uint8Array(buf);
|
||||
var seed = 1;
|
||||
for (var i = 0; i < LENGTH; i++) {
|
||||
// https://en.wikipedia.org/wiki/Lehmer_random_number_generator
|
||||
seed = seed * 48271 % 2147483647;
|
||||
prng[i] = seed;
|
||||
}
|
||||
`
|
||||
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
for _, address := range addrs {
|
||||
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||
if ipnet.IP.To4() != nil {
|
||||
return ipnet.IP.String()
|
||||
}
|
||||
const uploadHTML = `
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
console.log("Running DL test...");
|
||||
|
||||
` + commonJS + `
|
||||
for (var i = 0; i < NUM; i++) {
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("POST", "/uploadhandler?len=" + LENGTH, true);
|
||||
req.send(buf);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
const downloadHTML = `
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
console.log("Running DL test...");
|
||||
` + commonJS + `
|
||||
|
||||
function verify(data) {
|
||||
if (data.length !== LENGTH) return false;
|
||||
for (var i = 0; i < LENGTH; i++) {
|
||||
if (data[i] !== prng[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
panic("no addr")
|
||||
}
|
||||
|
||||
func removeDownload(filename string) {
|
||||
cmd := exec.Command("docker", "exec", "-i", "quic-test-selenium", "rm", "-f", "/home/seluser/Downloads/"+filename)
|
||||
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Eventually(session, 5).Should(gexec.Exit(0))
|
||||
}
|
||||
|
||||
// getDownloadSize gets the file size of a file in the /home/seluser/Downloads folder in the docker container
|
||||
func getDownloadSize(filename string) int {
|
||||
var out bytes.Buffer
|
||||
cmd := exec.Command("docker", "exec", "-i", "quic-test-selenium", "stat", "--printf=%s", "/home/seluser/Downloads/"+filename)
|
||||
session, err := gexec.Start(cmd, &out, GinkgoWriter)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Eventually(session, 5).Should(gexec.Exit())
|
||||
if session.ExitCode() != 0 {
|
||||
return 0
|
||||
var nOK = 0;
|
||||
for (var i = 0; i < NUM; i++) {
|
||||
let req = new XMLHttpRequest();
|
||||
req.responseType = "arraybuffer";
|
||||
req.open("POST", "/prdata?len=" + LENGTH, true);
|
||||
req.onreadystatechange = function () {
|
||||
if (req.readyState === XMLHttpRequest.DONE && req.status === 200) {
|
||||
if (verify(new Uint8Array(req.response))) {
|
||||
nOK++;
|
||||
if (nOK === NUM) {
|
||||
console.log("Done :)");
|
||||
var reqDone = new XMLHttpRequest();
|
||||
reqDone.open("GET", "/done");
|
||||
reqDone.send();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
req.send();
|
||||
}
|
||||
Expect(out.Bytes()).ToNot(BeEmpty())
|
||||
size, err := strconv.Atoi(string(out.Bytes()))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return size
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
// getFileSize gets the file size of a file on the local file system
|
||||
func getFileSize(filename string) int {
|
||||
file, err := os.Open(filename)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
fi, err := file.Stat()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
return int(fi.Size())
|
||||
}
|
||||
|
||||
// getDownloadMD5 gets the md5 sum file of a file in the /home/seluser/Downloads folder in the docker container
|
||||
func getDownloadMD5(filename string) []byte {
|
||||
var out bytes.Buffer
|
||||
cmd := exec.Command("docker", "exec", "-i", "quic-test-selenium", "md5sum", "/home/seluser/Downloads/"+filename)
|
||||
session, err := gexec.Start(cmd, &out, GinkgoWriter)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Eventually(session, 5).Should(gexec.Exit())
|
||||
if session.ExitCode() != 0 {
|
||||
return nil
|
||||
// Same as in the JS code, see
|
||||
// https://en.wikipedia.org/wiki/Lehmer_random_number_generator
|
||||
func generatePRData(l int) []byte {
|
||||
res := make([]byte, l)
|
||||
seed := uint64(1)
|
||||
for i := 0; i < l; i++ {
|
||||
seed = seed * 48271 % 2147483647
|
||||
res[i] = byte(seed)
|
||||
}
|
||||
Expect(out.Bytes()).ToNot(BeEmpty())
|
||||
res, err := hex.DecodeString(string(out.Bytes()[0:32]))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return res
|
||||
}
|
||||
|
||||
// getFileMD5 gets the md5 sum of a file on the local file system
|
||||
func getFileMD5(filepath string) []byte {
|
||||
var result []byte
|
||||
file, err := os.Open(filepath)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer file.Close()
|
||||
|
||||
hash := md5.New()
|
||||
_, err = io.Copy(hash, file)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
return hash.Sum(result)
|
||||
}
|
||||
|
||||
// copyFileToDocker copies a file from the local file system into the /home/seluser/ directory in the docker container
|
||||
func copyFileToDocker(filepath string) {
|
||||
cmd := exec.Command("docker", "cp", filepath, "quic-test-selenium:/home/seluser/")
|
||||
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Eventually(session, 5).Should(gexec.Exit(0))
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ var _ = Describe("Server tests", func() {
|
||||
"--certificate_file="+filepath.Join(tmpDir, "cert.pem"),
|
||||
"--port="+serverPort,
|
||||
)
|
||||
session, err = Start(command, GinkgoWriter, GinkgoWriter)
|
||||
session, err = Start(command, nil, GinkgoWriter)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user