diff --git a/.github/workflows/continuous_delivery.yaml b/.github/workflows/continuous_delivery.yaml deleted file mode 100644 index 01e35dc1483775568f9e0b116a897183de37f851..0000000000000000000000000000000000000000 --- a/.github/workflows/continuous_delivery.yaml +++ /dev/null @@ -1,105 +0,0 @@ -# This workflow implements continuous delivery with automated testing and fully -# autonomous deploys to production on merge. -name: CD - -# Translated: "Execute this workflow on pushes to main OR on pull requests -# opened against main" -# -# See this question and answer for what we're solving here: -# https://github.community/t5/GitHub-Actions/How-to-trigger-an-action-on-push-or-pull-request-but-not-both/m-p/36155#M2460 -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - test: - name: Test - runs-on: ubuntu-latest - steps: - - name: Setup - uses: actions/setup-go@v2 - with: - go-version: '1.18' - - - name: Checkout - uses: actions/checkout@v2 - - - name: Build - run: make build - - - name: Build docker image - run: make image - - - name: Test - run: git show --stat && make testci - - - name: Code coverage - uses: codecov/codecov-action@v1 - with: - file: ./coverage.txt - - regression_test: - name: Regression Tests - runs-on: ubuntu-latest - strategy: - matrix: - go_version: - - '1.17' - - '1.16' - - steps: - - name: Setup - uses: actions/setup-go@v2 - with: - go-version: ${{matrix.go_version}} - - - name: Checkout - uses: actions/checkout@v2 - - - name: Build - run: make build - - - name: Test - run: make test - - production_deploy: - name: Production Deploy - if: github.ref == 'refs/heads/main' - runs-on: ubuntu-latest - needs: [test] - - permissions: - deployments: write - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Setup flyctl - uses: superfly/flyctl-actions/setup-flyctl@master - - - name: Notify start - id: deployment - uses: bobheadxi/deployments@v1 - with: - step: start - token: ${{ secrets.GITHUB_TOKEN }} - env: production - - - name: Deploy - run: "flyctl deploy --strategy rolling" - env: - DOCKER_BUILDKIT: 1 - FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} - - - name: Notify finish - uses: bobheadxi/deployments@v1 - with: - step: finish - deployment_id: ${{ steps.deployment.outputs.deployment_id }} - token: ${{ secrets.GITHUB_TOKEN }} - env: production - status: ${{ job.status }} - env_url: 'https://httpbingo.org' diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 0b42e002138505342d0521d5ead4e1822f59d141..b1e57069ca2c1466b3a46c20d5707a01a5dedc67 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -15,7 +15,6 @@ jobs: - uses: actions/setup-go@v2 with: go-version: '1.18' - - name: golangci-lint - uses: golangci/golangci-lint-action@v3.1.0 + - uses: golangci/golangci-lint-action@v3.1.0 with: version: v1.45.2 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a528f37398153dbcdce7ba7c659f06be0655710a --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,51 @@ +name: Test + +# Translated: "Execute this workflow on pushes to main OR on pull requests +# opened against main" +# +# See this question and answer for what we're solving here: +# https://github.community/t5/GitHub-Actions/How-to-trigger-an-action-on-push-or-pull-request-but-not-both/m-p/36155#M2460 +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + name: Test + runs-on: ubuntu-latest + + steps: + - uses: actions/setup-go@v2 + with: + go-version: '1.18' + + - uses: actions/checkout@v2 + + - run: make testci + + - uses: codecov/codecov-action@v1 + with: + file: ./coverage.txt + + - run: make image + + regression_tests: + name: Regression Tests + runs-on: ubuntu-latest + + strategy: + matrix: + go_version: + - '1.17' + - '1.16' + + steps: + - uses: actions/setup-go@v2 + with: + go-version: ${{matrix.go_version}} + + - uses: actions/checkout@v2 + + - run: make test diff --git a/Makefile b/Makefile index 1c11590595517d87aca45ff664cb85d27a5151c1..e5e0d135d2a11b393d6a6e7f2fd83c1dcd719ed8 100644 --- a/Makefile +++ b/Makefile @@ -1,24 +1,7 @@ -.PHONY: clean deploy deps gcloud-auth image imagepush lint run stagedeploy test testci testcover - # The version that will be used in docker tags (e.g. to push a # go-httpbin:latest image use `make imagepush VERSION=latest)` -VERSION ?= $(shell git rev-parse --short HEAD) - -# Override these values to deploy to a different Cloud Run project -GCLOUD_PROJECT ?= httpbingo -GCLOUD_ACCOUNT ?= mccutchen@gmail.com -GCLOUD_REGION ?= us-central1 - -# The version tag for the Cloud Run deployment (override this to adjust -# pre-production URLs) -GCLOUD_TAG ?= "v-$(VERSION)" - -# Run gcloud in a container to avoid needing to install the SDK locally -GCLOUD_COMMAND ?= ./bin/gcloud - -# We push docker images to both docker hub and gcr.io -DOCKER_TAG_DOCKERHUB ?= mccutchen/go-httpbin:$(VERSION) -DOCKER_TAG_GCLOUD ?= gcr.io/$(GCLOUD_PROJECT)/go-httpbin:$(VERSION) +VERSION ?= $(shell git rev-parse --short HEAD) +DOCKER_TAG ?= mccutchen/go-httpbin:$(VERSION) # Built binaries will be placed here DIST_PATH ?= dist @@ -41,15 +24,18 @@ GO_SOURCES = $(wildcard **/*.go) # ============================================================================= build: $(DIST_PATH)/go-httpbin +buildtests: $(DIST_PATH)/go-httpbin.test + $(DIST_PATH)/go-httpbin: $(GO_SOURCES) mkdir -p $(DIST_PATH) CGO_ENABLED=0 go build -ldflags="-s -w" -o $(DIST_PATH)/go-httpbin ./cmd/go-httpbin -buildtests: +$(DIST_PATH)/go-httpbin.test: $(GO_SOURCES) CGO_ENABLED=0 go test -ldflags="-s -w" -v -c -o $(DIST_PATH)/go-httpbin.test ./httpbin clean: rm -rf $(DIST_PATH) $(COVERAGE_PATH) +.PHONY: clean # ============================================================================= @@ -57,6 +43,7 @@ clean: # ============================================================================= test: go test $(TEST_ARGS) ./... +.PHONY: test # Test command to run for continuous integration, which includes code coverage @@ -65,15 +52,18 @@ test: testci: build go test $(TEST_ARGS) $(COVERAGE_ARGS) ./... git diff --exit-code +.PHONY: testci testcover: testci go tool cover -html=$(COVERAGE_PATH) +.PHONY: testcover lint: $(TOOL_GOLINT) $(TOOL_STATICCHECK) test -z "$$(gofmt -d -s -e .)" || (echo "Error: gofmt failed"; gofmt -d -s -e . ; exit 1) go vet ./... $(TOOL_GOLINT) -set_exit_status ./... $(TOOL_STATICCHECK) ./... +.PHONY: lint # ============================================================================= @@ -81,61 +71,26 @@ lint: $(TOOL_GOLINT) $(TOOL_STATICCHECK) # ============================================================================= run: build $(DIST_PATH)/go-httpbin +.PHONY: run watch: $(TOOL_REFLEX) reflex -s -r '\.(go|html)$$' make run - - -# ============================================================================= -# deploy to fly.io -# ============================================================================= -deploy: - flyctl deploy --strategy=rolling - - -# ============================================================================= -# deploy to google cloud run -# ============================================================================= -deploy-cloud-run: gcloud-auth imagepush - $(GCLOUD_COMMAND) beta run deploy \ - $(GCLOUD_PROJECT) \ - --image=$(DOCKER_TAG_GCLOUD) \ - --revision-suffix=$(VERSION) \ - --tag=$(GCLOUD_TAG) \ - --project=$(GCLOUD_PROJECT) \ - --region=$(GCLOUD_REGION) \ - --allow-unauthenticated \ - --platform=managed - $(GCLOUD_COMMAND) run services update-traffic --to-latest - -stagedeploy-cloud-run: gcloud-auth imagepush - $(GCLOUD_COMMAND) beta run deploy \ - $(GCLOUD_PROJECT) \ - --image=$(DOCKER_TAG_GCLOUD) \ - --revision-suffix=$(VERSION) \ - --tag=$(GCLOUD_TAG) \ - --project=$(GCLOUD_PROJECT) \ - --region=$(GCLOUD_REGION) \ - --allow-unauthenticated \ - --platform=managed \ - --no-traffic - -gcloud-auth: - @$(GCLOUD_COMMAND) auth list | grep '^\*' | grep -q $(GCLOUD_ACCOUNT) || $(GCLOUD_COMMAND) auth login $(GCLOUD_ACCOUNT) - @$(GCLOUD_COMMAND) auth print-access-token | docker login -u oauth2accesstoken --password-stdin https://gcr.io +.PHONY: watch # ============================================================================= # docker images # ============================================================================= image: - DOCKER_BUILDKIT=1 docker build -t $(DOCKER_TAG_DOCKERHUB) . + DOCKER_BUILDKIT=1 docker build -t $(DOCKER_TAG) . +.PHONY: image imagepush: docker buildx create --name httpbin docker buildx use httpbin - docker buildx build --push --platform linux/amd64,linux/arm64 -t $(DOCKER_TAG_DOCKERHUB) . + docker buildx build --push --platform linux/amd64,linux/arm64 -t $(DOCKER_TAG) . docker buildx rm httpbin +.PHONY: imagepush # ============================================================================= diff --git a/app.yaml b/app.yaml deleted file mode 100644 index 0b4e957ae267e4734d382bc034418ec4d0ac42ca..0000000000000000000000000000000000000000 --- a/app.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -runtime: go113 - -main: ./cmd/go_httpbin - -handlers: - # Always redirect index requests to https - - url: / - script: auto - secure: always - redirect_http_response_code: 301 - - # Allow requests for any other resources via either http or https - - url: /.+ - script: auto - secure: optional diff --git a/bin/gcloud b/bin/gcloud deleted file mode 100755 index 9f1ec9f500d14454c45cc7d248fea3bd65fb52f1..0000000000000000000000000000000000000000 --- a/bin/gcloud +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# A wrapper that executes the gcloud CLI in a docker container, to avoid -# requiring a local installation. -# -# Adapted from this helpful blog post: -# https://blog.scottlowe.org/2018/09/13/running-gcloud-cli-in-a-docker-container/ - -GCLOUD_SDK_TAG="312.0.0" - -exec docker run \ - --rm -it \ - --workdir /code \ - -v $PWD:/code \ - -v $HOME/.config/gcloud:/root/.config/gcloud \ - google/cloud-sdk:$GCLOUD_SDK_TAG \ - gcloud $* diff --git a/cmd/README.md b/cmd/README.md deleted file mode 100644 index 267f207348ea6249cd75e4f0fe44b06001d663e4..0000000000000000000000000000000000000000 --- a/cmd/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# What is going on here? - -## TL;DR - - * `cmd/maincmd` package exposes all of this app's command line functionality in a `Main()` - - * `cmd/go-httpbin` and `cmd/go_httpbin` build identical binaries using the - `maincmd` package for backwards compatibility reasons explained below - -## Why tho - -Originally, this project exposed only one command: - - cmd/go-httpbin/main.go - -But the dash in that path was incompatible with Google App Engine's naming -restrictions, so in [moving httpbingo.org onto Google App Engine][pr17], that -path was (carelessly) renamed to - - cmd/go_httpbin/main.go - -_That_ change had a number of unintended consequences: - - * It broke existing workflows built around `go get github.com/mccutchen/go-httpbin/cmd/go-httpbin`, - as suggested in the README - - * It broke the Makefile, which was still looking for `cmd/go-httpbin` - - * It broke the absolute aesthetic truth that CLI binaries should use dashes - instead of underscores for word separators - -So, to restore the former behavior while maintaining support for deploying to -App Engine, the actual main functionality was extracted into the `cmd/maincmd` -package here and shared between the other two. - -(This is pretty dumb, I know, but it seems to work.) - -[pr17]: https://github.com/mccutchen/go-httpbin/pull/17 diff --git a/cmd/go-httpbin/main.go b/cmd/go-httpbin/main.go index 830cd9e1ee16a8da0f76f85cbcc1c07c77d54aa1..0743a117f37e6dcf49e3d79df18a2d58c91329b8 100644 --- a/cmd/go-httpbin/main.go +++ b/cmd/go-httpbin/main.go @@ -1,7 +1,154 @@ package main -import "github.com/mccutchen/go-httpbin/v2/cmd/maincmd" +import ( + "context" + "flag" + "fmt" + "log" + "net" + "net/http" + "os" + "os/signal" + "strconv" + "syscall" + "time" + + "github.com/mccutchen/go-httpbin/v2/httpbin" +) + +const ( + defaultHost = "0.0.0.0" + defaultPort = 8080 +) + +var ( + host string + port int + maxBodySize int64 + maxDuration time.Duration + httpsCertFile string + httpsKeyFile string +) func main() { - maincmd.Main() + flag.StringVar(&host, "host", defaultHost, "Host to listen on") + flag.IntVar(&port, "port", defaultPort, "Port to listen on") + flag.StringVar(&httpsCertFile, "https-cert-file", "", "HTTPS Server certificate file") + flag.StringVar(&httpsKeyFile, "https-key-file", "", "HTTPS Server private key file") + flag.Int64Var(&maxBodySize, "max-body-size", httpbin.DefaultMaxBodySize, "Maximum size of request or response, in bytes") + flag.DurationVar(&maxDuration, "max-duration", httpbin.DefaultMaxDuration, "Maximum duration a response may take") + flag.Parse() + + // Command line flags take precedence over environment vars, so we only + // check for environment vars if we have default values for our command + // line flags. + var err error + if maxBodySize == httpbin.DefaultMaxBodySize && os.Getenv("MAX_BODY_SIZE") != "" { + maxBodySize, err = strconv.ParseInt(os.Getenv("MAX_BODY_SIZE"), 10, 64) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: invalid value %#v for env var MAX_BODY_SIZE: %s\n\n", os.Getenv("MAX_BODY_SIZE"), err) + flag.Usage() + os.Exit(1) + } + } + if maxDuration == httpbin.DefaultMaxDuration && os.Getenv("MAX_DURATION") != "" { + maxDuration, err = time.ParseDuration(os.Getenv("MAX_DURATION")) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: invalid value %#v for env var MAX_DURATION: %s\n\n", os.Getenv("MAX_DURATION"), err) + flag.Usage() + os.Exit(1) + } + } + if host == defaultHost && os.Getenv("HOST") != "" { + host = os.Getenv("HOST") + } + if port == defaultPort && os.Getenv("PORT") != "" { + port, err = strconv.Atoi(os.Getenv("PORT")) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: invalid value %#v for env var PORT: %s\n\n", os.Getenv("PORT"), err) + flag.Usage() + os.Exit(1) + } + } + + if httpsCertFile == "" && os.Getenv("HTTPS_CERT_FILE") != "" { + httpsCertFile = os.Getenv("HTTPS_CERT_FILE") + } + if httpsKeyFile == "" && os.Getenv("HTTPS_KEY_FILE") != "" { + httpsKeyFile = os.Getenv("HTTPS_KEY_FILE") + } + + var serveTLS bool + if httpsCertFile != "" || httpsKeyFile != "" { + serveTLS = true + if httpsCertFile == "" || httpsKeyFile == "" { + fmt.Fprintf(os.Stderr, "Error: https cert and key must both be provided\n\n") + flag.Usage() + os.Exit(1) + } + } + + logger := log.New(os.Stderr, "", 0) + + // A hacky log helper function to ensure that shutdown messages are + // formatted the same as other messages. See StdLogObserver in + // httpbin/middleware.go for the format we're matching here. + serverLog := func(msg string, args ...interface{}) { + const ( + logFmt = "time=%q msg=%q" + dateFmt = "2006-01-02T15:04:05.9999" + ) + logger.Printf(logFmt, time.Now().Format(dateFmt), fmt.Sprintf(msg, args...)) + } + + h := httpbin.New( + httpbin.WithMaxBodySize(maxBodySize), + httpbin.WithMaxDuration(maxDuration), + httpbin.WithObserver(httpbin.StdLogObserver(logger)), + ) + + listenAddr := net.JoinHostPort(host, strconv.Itoa(port)) + + server := &http.Server{ + Addr: listenAddr, + Handler: h.Handler(), + } + + // shutdownCh triggers graceful shutdown on SIGINT or SIGTERM + shutdownCh := make(chan os.Signal, 1) + signal.Notify(shutdownCh, syscall.SIGINT, syscall.SIGTERM) + + // exitCh will be closed when it is safe to exit, after graceful shutdown + exitCh := make(chan struct{}) + + go func() { + sig := <-shutdownCh + serverLog("shutdown started by signal: %s", sig) + + shutdownTimeout := maxDuration + 1*time.Second + ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout) + defer cancel() + + server.SetKeepAlivesEnabled(false) + if err := server.Shutdown(ctx); err != nil { + serverLog("shutdown error: %s", err) + } + + close(exitCh) + }() + + var listenErr error + if serveTLS { + serverLog("go-httpbin listening on https://%s", listenAddr) + listenErr = server.ListenAndServeTLS(httpsCertFile, httpsKeyFile) + } else { + serverLog("go-httpbin listening on http://%s", listenAddr) + listenErr = server.ListenAndServe() + } + if listenErr != nil && listenErr != http.ErrServerClosed { + logger.Fatalf("failed to listen: %s", listenErr) + } + + <-exitCh + serverLog("shutdown finished") } diff --git a/cmd/go_httpbin/main.go b/cmd/go_httpbin/main.go deleted file mode 100644 index 830cd9e1ee16a8da0f76f85cbcc1c07c77d54aa1..0000000000000000000000000000000000000000 --- a/cmd/go_httpbin/main.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -import "github.com/mccutchen/go-httpbin/v2/cmd/maincmd" - -func main() { - maincmd.Main() -} diff --git a/cmd/maincmd/main.go b/cmd/maincmd/main.go deleted file mode 100644 index 0778d5c5feba5d30e2ff01c0359a080475c6b2d1..0000000000000000000000000000000000000000 --- a/cmd/maincmd/main.go +++ /dev/null @@ -1,153 +0,0 @@ -package maincmd - -import ( - "context" - "flag" - "fmt" - "log" - "net" - "net/http" - "os" - "os/signal" - "strconv" - "syscall" - "time" - - "github.com/mccutchen/go-httpbin/v2/httpbin" -) - -const defaultHost = "0.0.0.0" -const defaultPort = 8080 - -var ( - host string - port int - maxBodySize int64 - maxDuration time.Duration - httpsCertFile string - httpsKeyFile string -) - -// Main implements the go-httpbin CLI's main() function in a reusable way -func Main() { - flag.StringVar(&host, "host", defaultHost, "Host to listen on") - flag.IntVar(&port, "port", defaultPort, "Port to listen on") - flag.StringVar(&httpsCertFile, "https-cert-file", "", "HTTPS Server certificate file") - flag.StringVar(&httpsKeyFile, "https-key-file", "", "HTTPS Server private key file") - flag.Int64Var(&maxBodySize, "max-body-size", httpbin.DefaultMaxBodySize, "Maximum size of request or response, in bytes") - flag.DurationVar(&maxDuration, "max-duration", httpbin.DefaultMaxDuration, "Maximum duration a response may take") - flag.Parse() - - // Command line flags take precedence over environment vars, so we only - // check for environment vars if we have default values for our command - // line flags. - var err error - if maxBodySize == httpbin.DefaultMaxBodySize && os.Getenv("MAX_BODY_SIZE") != "" { - maxBodySize, err = strconv.ParseInt(os.Getenv("MAX_BODY_SIZE"), 10, 64) - if err != nil { - fmt.Fprintf(os.Stderr, "Error: invalid value %#v for env var MAX_BODY_SIZE: %s\n\n", os.Getenv("MAX_BODY_SIZE"), err) - flag.Usage() - os.Exit(1) - } - } - if maxDuration == httpbin.DefaultMaxDuration && os.Getenv("MAX_DURATION") != "" { - maxDuration, err = time.ParseDuration(os.Getenv("MAX_DURATION")) - if err != nil { - fmt.Fprintf(os.Stderr, "Error: invalid value %#v for env var MAX_DURATION: %s\n\n", os.Getenv("MAX_DURATION"), err) - flag.Usage() - os.Exit(1) - } - } - if host == defaultHost && os.Getenv("HOST") != "" { - host = os.Getenv("HOST") - } - if port == defaultPort && os.Getenv("PORT") != "" { - port, err = strconv.Atoi(os.Getenv("PORT")) - if err != nil { - fmt.Fprintf(os.Stderr, "Error: invalid value %#v for env var PORT: %s\n\n", os.Getenv("PORT"), err) - flag.Usage() - os.Exit(1) - } - } - - if httpsCertFile == "" && os.Getenv("HTTPS_CERT_FILE") != "" { - httpsCertFile = os.Getenv("HTTPS_CERT_FILE") - } - if httpsKeyFile == "" && os.Getenv("HTTPS_KEY_FILE") != "" { - httpsKeyFile = os.Getenv("HTTPS_KEY_FILE") - } - - var serveTLS bool - if httpsCertFile != "" || httpsKeyFile != "" { - serveTLS = true - if httpsCertFile == "" || httpsKeyFile == "" { - fmt.Fprintf(os.Stderr, "Error: https cert and key must both be provided\n\n") - flag.Usage() - os.Exit(1) - } - } - - logger := log.New(os.Stderr, "", 0) - - // A hacky log helper function to ensure that shutdown messages are - // formatted the same as other messages. See StdLogObserver in - // httpbin/middleware.go for the format we're matching here. - serverLog := func(msg string, args ...interface{}) { - const ( - logFmt = "time=%q msg=%q" - dateFmt = "2006-01-02T15:04:05.9999" - ) - logger.Printf(logFmt, time.Now().Format(dateFmt), fmt.Sprintf(msg, args...)) - } - - h := httpbin.New( - httpbin.WithMaxBodySize(maxBodySize), - httpbin.WithMaxDuration(maxDuration), - httpbin.WithObserver(httpbin.StdLogObserver(logger)), - ) - - listenAddr := net.JoinHostPort(host, strconv.Itoa(port)) - - server := &http.Server{ - Addr: listenAddr, - Handler: h.Handler(), - } - - // shutdownCh triggers graceful shutdown on SIGINT or SIGTERM - shutdownCh := make(chan os.Signal, 1) - signal.Notify(shutdownCh, syscall.SIGINT, syscall.SIGTERM) - - // exitCh will be closed when it is safe to exit, after graceful shutdown - exitCh := make(chan struct{}) - - go func() { - sig := <-shutdownCh - serverLog("shutdown started by signal: %s", sig) - - shutdownTimeout := maxDuration + 1*time.Second - ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout) - defer cancel() - - server.SetKeepAlivesEnabled(false) - if err := server.Shutdown(ctx); err != nil { - serverLog("shutdown error: %s", err) - } - - close(exitCh) - }() - - var listenErr error - if serveTLS { - serverLog("go-httpbin listening on https://%s", listenAddr) - listenErr = server.ListenAndServeTLS(httpsCertFile, httpsKeyFile) - } else { - serverLog("go-httpbin listening on http://%s", listenAddr) - listenErr = server.ListenAndServe() - } - if listenErr != nil && listenErr != http.ErrServerClosed { - logger.Fatalf("failed to listen: %s", listenErr) - } - - <-exitCh - serverLog("shutdown finished") -} diff --git a/fly.toml b/fly.toml deleted file mode 100644 index b798550d545462d576f04c59aadc695555800996..0000000000000000000000000000000000000000 --- a/fly.toml +++ /dev/null @@ -1,21 +0,0 @@ -app = "httpbingo" - -[[services]] - internal_port = 8080 - protocol = "tcp" - - [services.concurrency] - hard_limit = 25 - soft_limit = 20 - - [[services.ports]] - handlers = ["http"] - port = "80" - - [[services.ports]] - handlers = ["tls", "http"] - port = "443" - - [[services.tcp_checks]] - interval = 10000 - timeout = 2000