diff --git a/.github/workflows/build-docker-images.yml b/.github/workflows/build-docker-images.yml new file mode 100644 index 0000000..0a0571b --- /dev/null +++ b/.github/workflows/build-docker-images.yml @@ -0,0 +1,89 @@ +name: Deploy multi-architecture Docker images for transfer.sh with buildx + +on: + schedule: + - cron: '0 0 * * *' # everyday at midnight UTC + pull_request: + branches: master + push: + branches: master + tags: + - v* + +jobs: + buildx: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Prepare + id: prepare + run: | + DOCKER_IMAGE=dutchcoders/transfer.sh + DOCKER_PLATFORMS=linux/amd64,linux/arm/v7,linux/arm64,linux/386 + VERSION=edge + + if [[ $GITHUB_REF == refs/tags/* ]]; then + VERSION=${GITHUB_REF#refs/tags/v} + fi + + if [ "${{ github.event_name }}" = "schedule" ]; then + VERSION=nightly + fi + + TAGS="--tag ${DOCKER_IMAGE}:${VERSION}" + + if [ $VERSION = edge -o $VERSION = nightly ]; then + TAGS="$TAGS --tag ${DOCKER_IMAGE}:latest" + fi + + echo ::set-output name=docker_image::${DOCKER_IMAGE} + echo ::set-output name=version::${VERSION} + echo ::set-output name=buildx_args::--platform ${DOCKER_PLATFORMS} \ + --build-arg VERSION=${VERSION} \ + --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \ + --build-arg VCS_REF=${GITHUB_SHA::8} \ + ${TAGS} . + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + with: + platforms: all + - + name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + with: + version: latest + - + name: Available platforms + run: echo ${{ steps.buildx.outputs.platforms }} + - + name: Docker Buildx (build) + run: | + docker buildx build --no-cache --pull --output "type=image,push=false" ${{ steps.prepare.outputs.buildx_args }} + - + name: Docker Login + if: success() && github.event_name != 'pull_request' + env: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + run: | + echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin + - + name: Docker Buildx (push) + if: success() && github.event_name != 'pull_request' + run: | + docker buildx build --output "type=image,push=true" ${{ steps.prepare.outputs.buildx_args }} + - + name: Docker Check Manifest + if: always() && github.event_name != 'pull_request' + run: | + docker run --rm mplatform/mquery ${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.version }} + - + name: Clear + if: always() && github.event_name != 'pull_request' + run: | + rm -f ${HOME}/.docker/config.json diff --git a/Dockerfile b/Dockerfile index aeb6d86..0018ba1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ WORKDIR /go/src/github.com/dutchcoders/transfer.sh ENV GO111MODULE=on # build & install server -RUN go get -u ./... && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags -a -tags netgo -ldflags '-w -extldflags "-static"' -o /go/bin/transfersh github.com/dutchcoders/transfer.sh +RUN go get -u ./... && CGO_ENABLED=0 go build -ldflags -a -tags netgo -ldflags '-w -extldflags "-static"' -o /go/bin/transfersh github.com/dutchcoders/transfer.sh FROM scratch AS final LABEL maintainer="Andrea Spacca " diff --git a/README.md b/README.md index 26d5859..1b9a278 100644 --- a/README.md +++ b/README.md @@ -76,42 +76,46 @@ https://transfer.sh/1lDau/test.txt --> https://transfer.sh/inline/1lDau/test.txt Parameter | Description | Value | Env --- | --- | --- | --- -listener | port to use for http (:80) | | -profile-listener | port to use for profiler (:6060)| | -force-https | redirect to https | false | -tls-listener | port to use for https (:443) | | -tls-listener-only | flag to enable tls listener only | | -tls-cert-file | path to tls certificate | | -tls-private-key | path to tls private key | | -http-auth-user | user for basic http auth on upload | | -http-auth-pass | pass for basic http auth on upload | | -ip-whitelist | comma separated list of ips allowed to connect to the service | | -ip-blacklist | comma separated list of ips not allowed to connect to the service | | -temp-path | path to temp folder | system temp | -web-path | path to static web files (for development or custom front end) | | -proxy-path | path prefix when service is run behind a proxy | | -ga-key | google analytics key for the front end | | -uservoice-key | user voice key for the front end | | +listener | port to use for http (:80) | | LISTENER | +profile-listener | port to use for profiler (:6060) | | PROFILE_LISTENER | +force-https | redirect to https | false | FORCE_HTTPS +tls-listener | port to use for https (:443) | | TLS_LISTENER | +tls-listener-only | flag to enable tls listener only | | TLS_LISTENER_ONLY | +tls-cert-file | path to tls certificate | | TLS_CERT_FILE | +tls-private-key | path to tls private key | | TLS_PRIVATE_KEY | +http-auth-user | user for basic http auth on upload | | HTTP_AUTH_USER | +http-auth-pass | pass for basic http auth on upload | | HTTP_AUTH_PASS | +ip-whitelist | comma separated list of ips allowed to connect to the service | | IP_WHITELIST | +ip-blacklist | comma separated list of ips not allowed to connect to the service | | IP_BLACKLIST | +temp-path | path to temp folder | system temp | TEMP_PATH | +web-path | path to static web files (for development or custom front end) | | WEB_PATH | +proxy-path | path prefix when service is run behind a proxy | | PROXY_PATH | +proxy-port | port of the proxy when the service is run behind a proxy | | PROXY_PORT | +ga-key | google analytics key for the front end | | GA_KEY | provider | which storage provider to use | (s3, storj, gdrive or local) | -aws-access-key | aws access key | | AWS_ACCESS_KEY -aws-secret-key | aws access key | | AWS_SECRET_KEY -bucket | aws bucket | | BUCKET -s3-endpoint | Custom S3 endpoint. | | -s3-region | region of the s3 bucket | eu-west-1 | S3_REGION -s3-no-multipart | disables s3 multipart upload | false | | -s3-path-style | Forces path style URLs, required for Minio. | false | | -storj-access | Access for the project | | STORJ_ACCESS -storj-bucket | Bucket to use within the project | | STORJ_BUCKET -basedir | path storage for local/gdrive provider| | -gdrive-client-json-filepath | path to oauth client json config for gdrive provider| | -gdrive-local-config-path | path to store local transfer.sh config cache for gdrive provider| | -gdrive-chunk-size | chunk size for gdrive upload in megabytes, must be lower than available memory (8 MB) | | -lets-encrypt-hosts | hosts to use for lets encrypt certificates (comma seperated) | | -log | path to log file| | +uservoice-key | user voice key for the front end | | USERVOICE_KEY | +aws-access-key | aws access key | | AWS_ACCESS_KEY | +aws-secret-key | aws access key | | AWS_SECRET_KEY | +bucket | aws bucket | | BUCKET | +s3-endpoint | Custom S3 endpoint. | | S3_ENDPOINT | +s3-region | region of the s3 bucket | eu-west-1 | S3_REGION | +s3-no-multipart | disables s3 multipart upload | false | S3_NO_MULTIPART | +s3-path-style | Forces path style URLs, required for Minio. | false | S3_PATH_STYLE | +storj-access | Access for the project | | STORJ_ACCESS | +storj-bucket | Bucket to use within the project | | STORJ_BUCKET | +basedir | path storage for local/gdrive provider | | BASEDIR | +gdrive-client-json-filepath | path to oauth client json config for gdrive provider | | GDRIVE_CLIENT_JSON_FILEPATH | +gdrive-local-config-path | path to store local transfer.sh config cache for gdrive provider| | GDRIVE_LOCAL_CONFIG_PATH | +gdrive-chunk-size | chunk size for gdrive upload in megabytes, must be lower than available memory (8 MB) | | GDRIVE_CHUNK_SIZE | +lets-encrypt-hosts | hosts to use for lets encrypt certificates (comma seperated) | | HOSTS | +log | path to log file| | LOG | +cors-domains | comma separated list of domains for CORS, setting it enable CORS | | CORS_DOMAINS | +clamav-host | host for clamav feature | | CLAMAV_HOST | +rate-limit | request per minute | | RATE_LIMIT | If you want to use TLS using lets encrypt certificates, set lets-encrypt-hosts to your domain, set tls-listener to :443 and enable force-https. -If you want to use TLS using your own certificates, set tls-listener to :443, force-https, tls-cert=file and tls-private-key. +If you want to use TLS using your own certificates, set tls-listener to :443, force-https, tls-cert-file and tls-private-key. ## Development diff --git a/cmd/cmd.go b/cmd/cmd.go index 1533668..5abd46e 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -12,7 +12,7 @@ import ( "google.golang.org/api/googleapi" ) -var Version = "1.1.4" +var Version = "1.1.7" var helpTemplate = `NAME: {{.Name}} - {{.Usage}} @@ -37,6 +37,7 @@ var globalFlags = []cli.Flag{ Name: "listener", Usage: "127.0.0.1:8080", Value: "127.0.0.1:8080", + EnvVar: "LISTENER", }, // redirect to https? // hostnames @@ -44,57 +45,75 @@ var globalFlags = []cli.Flag{ Name: "profile-listener", Usage: "127.0.0.1:6060", Value: "", + EnvVar: "PROFILE_LISTENER", }, cli.BoolFlag{ Name: "force-https", Usage: "", + EnvVar: "FORCE_HTTPS", }, cli.StringFlag{ Name: "tls-listener", Usage: "127.0.0.1:8443", Value: "", + EnvVar: "TLS_LISTENER", }, cli.BoolFlag{ Name: "tls-listener-only", Usage: "", + EnvVar: "TLS_LISTENER_ONLY", }, cli.StringFlag{ Name: "tls-cert-file", Value: "", + EnvVar: "TLS_CERT_FILE", }, cli.StringFlag{ Name: "tls-private-key", Value: "", + EnvVar: "TLS_PRIVATE_KEY", }, cli.StringFlag{ Name: "temp-path", Usage: "path to temp files", Value: os.TempDir(), + EnvVar: "TEMP_PATH", }, cli.StringFlag{ Name: "web-path", Usage: "path to static web files", Value: "", + EnvVar: "WEB_PATH", }, cli.StringFlag{ Name: "proxy-path", Usage: "path prefix when service is run behind a proxy", Value: "", + EnvVar: "PROXY_PATH", + }, + cli.StringFlag{ + Name: "proxy-port", + Usage: "port of the proxy when the service is run behind a proxy", + Value: "", + EnvVar: "PROXY_PORT", }, cli.StringFlag{ Name: "ga-key", Usage: "key for google analytics (front end)", Value: "", + EnvVar: "GA_KEY", }, cli.StringFlag{ Name: "uservoice-key", Usage: "key for user voice (front end)", Value: "", + EnvVar: "USERVOICE_KEY", }, cli.StringFlag{ Name: "provider", Usage: "s3|gdrive|local", Value: "", + EnvVar: "PROVIDER", }, cli.StringFlag{ Name: "s3-endpoint", @@ -129,25 +148,30 @@ var globalFlags = []cli.Flag{ cli.BoolFlag{ Name: "s3-no-multipart", Usage: "Disables S3 Multipart Puts", + EnvVar: "S3_NO_MULTIPART", }, cli.BoolFlag{ Name: "s3-path-style", Usage: "Forces path style URLs, required for Minio.", + EnvVar: "S3_PATH_STYLE", }, cli.StringFlag{ Name: "gdrive-client-json-filepath", Usage: "", Value: "", + EnvVar: "GDRIVE_CLIENT_JSON_FILEPATH", }, cli.StringFlag{ Name: "gdrive-local-config-path", Usage: "", Value: "", + EnvVar: "GDRIVE_LOCAL_CONFIG_PATH", }, cli.IntFlag{ Name: "gdrive-chunk-size", Usage: "", Value: googleapi.DefaultUploadChunkSize / 1024 / 1024, + EnvVar: "GDRIVE_CHUNK_SIZE", }, cli.StringFlag{ Name: "storj-access", @@ -165,7 +189,7 @@ var globalFlags = []cli.Flag{ Name: "rate-limit", Usage: "requests per minute", Value: 0, - EnvVar: "", + EnvVar: "RATE_LIMIT", }, cli.StringFlag{ Name: "lets-encrypt-hosts", @@ -177,11 +201,13 @@ var globalFlags = []cli.Flag{ Name: "log", Usage: "/var/log/transfersh.log", Value: "", + EnvVar: "LOG", }, cli.StringFlag{ Name: "basedir", Usage: "path to storage", Value: "", + EnvVar: "BASEDIR", }, cli.StringFlag{ Name: "clamav-host", @@ -198,26 +224,37 @@ var globalFlags = []cli.Flag{ cli.BoolFlag{ Name: "profiler", Usage: "enable profiling", + EnvVar: "PROFILER", }, cli.StringFlag{ Name: "http-auth-user", Usage: "user for http basic auth", Value: "", + EnvVar: "HTTP_AUTH_USER", }, cli.StringFlag{ Name: "http-auth-pass", Usage: "pass for http basic auth", Value: "", + EnvVar: "HTTP_AUTH_PASS", }, cli.StringFlag{ Name: "ip-whitelist", Usage: "comma separated list of ips allowed to connect to the service", Value: "", + EnvVar: "IP_WHITELIST", }, cli.StringFlag{ Name: "ip-blacklist", Usage: "comma separated list of ips not allowed to connect to the service", Value: "", + EnvVar: "IP_BLACKLIST", + }, + cli.StringFlag{ + Name: "cors-domains", + Usage: "comma separated list of domains allowed for CORS requests", + Value: "", + EnvVar: "CORS_DOMAINS", }, } @@ -226,7 +263,7 @@ type Cmd struct { } func VersionAction(c *cli.Context) { - fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh: Easy file sharing from the command line"))) + fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh %s: Easy file sharing from the command line", Version))) } func New() *Cmd { @@ -257,6 +294,10 @@ func New() *Cmd { options = append(options, server.Listener(v)) } + if v := c.String("cors-domains"); v != "" { + options = append(options, server.CorsDomains(v)) + } + if v := c.String("tls-listener"); v == "" { } else if c.Bool("tls-listener-only") { options = append(options, server.TLSListener(v, true)) @@ -276,6 +317,10 @@ func New() *Cmd { options = append(options, server.ProxyPath(v)) } + if v := c.String("proxy-port"); v != "" { + options = append(options, server.ProxyPort(v)) + } + if v := c.String("ga-key"); v != "" { options = append(options, server.GoogleAnalytics(v)) } diff --git a/go.mod b/go.mod index 58a41eb..1a6a5f4 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/garyburd/redigo v1.6.0 // indirect github.com/golang/gddo v0.0.0-20200310004957-95ce5a452273 github.com/golang/protobuf v1.3.5 // indirect + github.com/gorilla/handlers v1.4.2 github.com/gorilla/mux v1.7.4 github.com/gorilla/securecookie v1.1.1 // indirect github.com/jmespath/go-jmespath v0.3.0 // indirect diff --git a/go.sum b/go.sum index 2e9185f..96dd5a1 100644 --- a/go.sum +++ b/go.sum @@ -136,6 +136,8 @@ github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg= +github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= diff --git a/server/handlers.go b/server/handlers.go index 4f0af3d..067f493 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -158,9 +158,9 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { } relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) - resolvedURL := resolveURL(r, relativeURL) + resolvedURL := resolveURL(r, relativeURL, s.proxyPort) relativeURLGet, _ := url.Parse(path.Join(s.proxyPath, getPathPart, token, filename)) - resolvedURLGet := resolveURL(r, relativeURLGet) + resolvedURLGet := resolveURL(r, relativeURLGet, s.proxyPort) var png []byte png, err = qrcode.Encode(resolvedURL, qrcode.High, 150) if err != nil { @@ -170,8 +170,8 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { qrCode := base64.StdEncoding.EncodeToString(png) - hostname := getURL(r).Host - webAddress := resolveWebAddress(r, s.proxyPath) + hostname := getURL(r, s.proxyPort).Host + webAddress := resolveWebAddress(r, s.proxyPath, s.proxyPort) data := struct { ContentType string @@ -212,8 +212,8 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { func (s *Server) viewHandler(w http.ResponseWriter, r *http.Request) { // vars := mux.Vars(r) - hostname := getURL(r).Host - webAddress := resolveWebAddress(r, s.proxyPath) + hostname := getURL(r, s.proxyPort).Host + webAddress := resolveWebAddress(r, s.proxyPath, s.proxyPort) data := struct { Hostname string @@ -339,7 +339,7 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) { filename = url.PathEscape(filename) relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) - fmt.Fprintln(w, getURL(r).ResolveReference(relativeURL).String()) + fmt.Fprintln(w, getURL(r, s.proxyPort).ResolveReference(relativeURL).String()) cleanTmpFile(file) } @@ -500,15 +500,15 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) { relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) deleteURL, _ := url.Parse(path.Join(s.proxyPath, token, filename, metadata.DeletionToken)) - w.Header().Set("X-Url-Delete", resolveURL(r, deleteURL)) + w.Header().Set("X-Url-Delete", resolveURL(r, deleteURL, s.proxyPort)) - fmt.Fprint(w, resolveURL(r, relativeURL)) + fmt.Fprint(w, resolveURL(r, relativeURL, s.proxyPort)) } -func resolveURL(r *http.Request, u *url.URL) string { +func resolveURL(r *http.Request, u *url.URL, proxyPort string) string { r.URL.Path = "" - return getURL(r).ResolveReference(u).String() + return getURL(r, proxyPort).ResolveReference(u).String() } func resolveKey(key, proxyPath string) string { @@ -525,8 +525,8 @@ func resolveKey(key, proxyPath string) string { return key } -func resolveWebAddress(r *http.Request, proxyPath string) string { - url := getURL(r) +func resolveWebAddress(r *http.Request, proxyPath string, proxyPort string) string { + url := getURL(r, proxyPort) var webAddress string @@ -544,7 +544,7 @@ func resolveWebAddress(r *http.Request, proxyPath string) string { return webAddress } -func getURL(r *http.Request) *url.URL { +func getURL(r *http.Request, proxyPort string) *url.URL { u, _ := url.Parse(r.URL.String()) if r.TLS != nil { @@ -555,16 +555,25 @@ func getURL(r *http.Request) *url.URL { u.Scheme = "http" } - if u.Host != "" { - } else if host, port, err := net.SplitHostPort(r.Host); err != nil { - u.Host = r.Host - } else { - if port == "80" && u.Scheme == "http" { - u.Host = host - } else if port == "443" && u.Scheme == "https" { + if u.Host == "" { + host, port, err := net.SplitHostPort(r.Host) + if err != nil { + host = r.Host + port = "" + } + if len(proxyPort) != 0 { + port = proxyPort + } + if len(port) == 0 { u.Host = host } else { - u.Host = net.JoinHostPort(host, port) + if port == "80" && u.Scheme == "http" { + u.Host = host + } else if port == "443" && u.Scheme == "https" { + u.Host = host + } else { + u.Host = net.JoinHostPort(host, port) + } } } @@ -614,9 +623,7 @@ func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (M var metadata Metadata r, _, err := s.storage.Get(token, fmt.Sprintf("%s.metadata", filename)) - if s.storage.IsNotExist(err) { - return metadata, nil - } else if err != nil { + if err != nil { return metadata, err } @@ -1011,7 +1018,7 @@ func (s *Server) RedirectHandler(h http.Handler) http.HandlerFunc { } else if r.Header.Get("X-Forwarded-Proto") == "https" { } else if r.URL.Scheme == "https" { } else { - u := getURL(r) + u := getURL(r, s.proxyPort) u.Scheme = "https" http.Redirect(w, r, u.String(), http.StatusPermanentRedirect) diff --git a/server/server.go b/server/server.go index 16c3197..3fc0b97 100644 --- a/server/server.go +++ b/server/server.go @@ -26,7 +26,10 @@ package server import ( "errors" + gorillaHandlers "github.com/gorilla/handlers" "log" + crypto_rand "crypto/rand" + "encoding/binary" "math/rand" "mime" "net/http" @@ -85,6 +88,13 @@ func Listener(s string) OptionFn { } +func CorsDomains(s string) OptionFn { + return func(srvr *Server) { + srvr.CorsDomains = s + } + +} + func GoogleAnalytics(gaKey string) OptionFn { return func(srvr *Server) { srvr.gaKey = gaKey @@ -131,6 +141,12 @@ func ProxyPath(s string) OptionFn { } } +func ProxyPort(s string) OptionFn { + return func(srvr *Server) { + srvr.proxyPort = s + } +} + func TempPath(s string) OptionFn { return func(srvr *Server) { if s[len(s)-1:] != "/" { @@ -270,11 +286,13 @@ type Server struct { webPath string proxyPath string + proxyPort string gaKey string userVoiceKey string TLSListenerOnly bool + CorsDomains string ListenerString string TLSListenerString string ProfileListenerString string @@ -297,7 +315,11 @@ func New(options ...OptionFn) (*Server, error) { } func init() { - rand.Seed(time.Now().UTC().UnixNano()) + var seedBytes [8]byte + if _, err := crypto_rand.Read(seedBytes[:]); err != nil { + panic("cannot obtain cryptographically secure seed") + } + rand.Seed(int64(binary.LittleEndian.Uint64(seedBytes[:]))) } func (s *Server) Run() { @@ -413,11 +435,24 @@ func (s *Server) Run() { s.logger.Printf("Transfer.sh server started.\nusing temp folder: %s\nusing storage provider: %s", s.tempPath, s.storage.Type()) + var cors func(http.Handler) http.Handler + if len(s.CorsDomains) > 0 { + cors = gorillaHandlers.CORS( + gorillaHandlers.AllowedHeaders([]string{"*"}), + gorillaHandlers.AllowedOrigins(strings.Split(s.CorsDomains, ",")), + gorillaHandlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"}), + ) + } else { + cors = func(h http.Handler) http.Handler { + return h + } + } + h := handlers.PanicHandler( IPFilterHandler( handlers.LogHandler( LoveHandler( - s.RedirectHandler(r)), + s.RedirectHandler(cors(r))), handlers.NewLogOptions(s.logger.Printf, "_default_"), ), s.ipFilterOptions,