diff --git a/.circleci/config.yml b/.circleci/config.yml index b6f39a54e..b10fa7952 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,17 +1,19 @@ version: 2.1 -jobs: - build: - parameters: - image: - type: string - default: \"\" - runrace: - type: boolean - default: false +executors: + test: docker: - - image: << parameters.image >> - environment: - GO111MODULE: "on" + - image: "circleci/golang:1.13" + environment: + runrace: true + interop: + docker: + - image: circleci/buildpack-deps:stretch + environment: + IMAGE_NAME: martenseemann/quic-go-interop + +jobs: + test: + executor: test working_directory: /go/src/github.com/lucas-clemente/quic-go steps: - checkout @@ -35,16 +37,52 @@ jobs: - run: name: "Run self integration tests" command: ginkgo -v -randomizeAllSpecs -trace integrationtests/self - - when: - condition: << parameters.runrace >> - steps: - - run: - name: "Run self integration tests with race detector" - command: ginkgo -race -v -randomizeAllSpecs -trace integrationtests/self + - run: + name: "Run self integration tests with race detector" + command: ginkgo -race -v -randomizeAllSpecs -trace integrationtests/self + interop-build: + executor: interop + steps: + - checkout + - setup_remote_docker: + docker_layer_caching: true + - run: + name: Build docker image + command: cd interop && docker build . -t $IMAGE_NAME:latest --pull --build-arg CACHEBUST=$(date +%s) + - run: + name: Archive Docker image + command: docker save -o image.tar $IMAGE_NAME + - persist_to_workspace: + root: . + paths: + - ./image.tar + interop-publish: + executor: interop + steps: + - attach_workspace: + at: /tmp/workspace + - setup_remote_docker + - run: + name: Load archived Docker image + command: docker load -i /tmp/workspace/image.tar + - run: + name: Publish quic-go-interop + command: | + echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin + docker push $IMAGE_NAME:latest + workflows: workflow: jobs: - - build: - name: "Go 1.13" - image: "circleci/golang:1.13" - runrace: true + - test + - interop-build: + filters: + branches: + only: interop + - interop-publish: + requires: + - test + - interop-build + filters: + branches: + only: interop diff --git a/go.mod b/go.mod index 2152150ee..78fb85aeb 100644 --- a/go.mod +++ b/go.mod @@ -14,4 +14,5 @@ require ( github.com/onsi/gomega v1.4.3 golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 + golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f ) diff --git a/interop/Dockerfile b/interop/Dockerfile new file mode 100644 index 000000000..c33c926a9 --- /dev/null +++ b/interop/Dockerfile @@ -0,0 +1,24 @@ +FROM martenseemann/quic-network-simulator-endpoint:latest + +RUN apt-get update && apt-get install -y wget tar git vim python + +RUN wget https://dl.google.com/go/go1.13.3.linux-amd64.tar.gz && \ + tar xfz go1.13.3.linux-amd64.tar.gz && \ + rm go1.13.3.linux-amd64.tar.gz + +ENV PATH="/go/bin:${PATH}" + +# build with --build-arg CACHEBUST=$(date +%s) +ARG CACHEBUST=1 + +RUN git clone https://github.com/lucas-clemente/quic-go && \ + cd quic-go && \ + git checkout interop && \ + go get ./... + +WORKDIR /quic-go + +COPY run_endpoint.sh . +RUN chmod +x run_endpoint.sh + +ENTRYPOINT [ "./run_endpoint.sh" ] diff --git a/interop/client/main.go b/interop/client/main.go new file mode 100644 index 000000000..45ac2f854 --- /dev/null +++ b/interop/client/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "crypto/tls" + "flag" + "fmt" + "io" + "log" + "net/http" + "os" + + "github.com/lucas-clemente/quic-go/http3" + "github.com/lucas-clemente/quic-go/interop/http09" + "golang.org/x/sync/errgroup" +) + +func main() { + logFile, err := os.Create("/logs/log.txt") + if err != nil { + fmt.Printf("Could not create log file: %s\n", err.Error()) + os.Exit(1) + } + defer logFile.Close() + log.SetOutput(logFile) + + flag.Parse() + urls := flag.Args() + + testcase := os.Getenv("TESTCASE") + + var useH3 bool + switch testcase { + case "handshake", "transfer", "retry": + case "http3": + useH3 = true + default: + fmt.Printf("unsupported test case: %s\n", testcase) + os.Exit(127) + } + + var roundTripper http.RoundTripper + if useH3 { + r := &http3.RoundTripper{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + defer r.Close() + roundTripper = r + } else { + r := &http09.RoundTripper{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + defer r.Close() + roundTripper = r + } + + var g errgroup.Group + for _, u := range urls { + url := u + g.Go(func() error { + return downloadFile(roundTripper, url) + }) + } + if err := g.Wait(); err != nil { + fmt.Printf("Downloading files failed: %s\n", err.Error()) + os.Exit(1) + } +} + +func downloadFile(cl http.RoundTripper, url string) error { + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return err + } + rsp, err := cl.RoundTrip(req) + if err != nil { + return err + } + defer rsp.Body.Close() + + file, err := os.Create("/downloads" + req.URL.Path) + if err != nil { + return err + } + defer file.Close() + _, err = io.Copy(file, rsp.Body) + return err +} diff --git a/interop/run_endpoint.sh b/interop/run_endpoint.sh new file mode 100644 index 000000000..f48c62453 --- /dev/null +++ b/interop/run_endpoint.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -e + +# Set up the routing needed for the simulation. +/setup.sh + +if [ "$ROLE" == "client" ]; then + # Wait for the simulator to start up. + /wait-for-it.sh sim:57832 -s -t 10 + echo "Starting QUIC client..." + echo "Client params: $CLIENT_PARAMS" + echo "Test case: $TESTCASE" + QUIC_GO_LOG_LEVEL=debug go run interop/client/main.go $CLIENT_PARAMS $REQUESTS +else + echo "Running QUIC server." + QUIC_GO_LOG_LEVEL=debug go run interop/server/main.go "$@" +fi diff --git a/interop/server/main.go b/interop/server/main.go new file mode 100644 index 000000000..34574527a --- /dev/null +++ b/interop/server/main.go @@ -0,0 +1,74 @@ +package main + +import ( + "fmt" + "log" + "net" + "net/http" + "os" + + "github.com/lucas-clemente/quic-go" + "github.com/lucas-clemente/quic-go/http3" + "github.com/lucas-clemente/quic-go/internal/testdata" + "github.com/lucas-clemente/quic-go/interop/http09" +) + +func main() { + logFile, err := os.Create("/logs/log.txt") + if err != nil { + fmt.Printf("Could not create log file: %s\n", err.Error()) + os.Exit(1) + } + defer logFile.Close() + log.SetOutput(logFile) + + testcase := os.Getenv("TESTCASE") + + // a quic.Config that doesn't do a Retry + quicConf := &quic.Config{ + AcceptToken: func(_ net.Addr, _ *quic.Token) bool { return true }, + } + + switch testcase { + case "handshake", "transfer": + err = runHTTP09Server(quicConf) + case "retry": + // By default, quic-go performs a Retry on every incoming connection. + quicConf.AcceptToken = nil + err = runHTTP09Server(quicConf) + case "http3": + err = runHTTP3Server(quicConf) + default: + fmt.Printf("unsupported test case: %s\n", testcase) + os.Exit(127) + } + + if err != nil { + fmt.Printf("Error running server: %s\n", err.Error()) + os.Exit(1) + } +} + +func runHTTP09Server(quicConf *quic.Config) error { + server := http09.Server{ + Server: &http.Server{ + Addr: "0.0.0.0:443", + TLSConfig: testdata.GetTLSConfig(), + }, + QuicConfig: quicConf, + } + http.DefaultServeMux.Handle("/", http.FileServer(http.Dir("/www"))) + return server.ListenAndServe() +} + +func runHTTP3Server(quicConf *quic.Config) error { + server := http3.Server{ + Server: &http.Server{ + Addr: "0.0.0.0:443", + TLSConfig: testdata.GetTLSConfig(), + }, + QuicConfig: quicConf, + } + http.DefaultServeMux.Handle("/", http.FileServer(http.Dir("/www"))) + return server.ListenAndServe() +}