From c11e3bffd698cfb1e19a21097ff4a65cf10e28b0 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Thu, 3 Oct 2019 16:00:41 -0600 Subject: Add file-server and reverse-proxy subcommands --- modules/caddyhttp/fileserver/command.go | 106 +++++++++++++++++++++++ modules/caddyhttp/reverseproxy/command.go | 138 ++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 modules/caddyhttp/fileserver/command.go create mode 100644 modules/caddyhttp/reverseproxy/command.go (limited to 'modules/caddyhttp') diff --git a/modules/caddyhttp/fileserver/command.go b/modules/caddyhttp/fileserver/command.go new file mode 100644 index 0000000..727d7ba --- /dev/null +++ b/modules/caddyhttp/fileserver/command.go @@ -0,0 +1,106 @@ +// Copyright 2015 Matthew Holt and The Caddy Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fileserver + +import ( + "encoding/json" + "flag" + "log" + + "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig" + caddycmd "github.com/caddyserver/caddy/v2/cmd" + "github.com/caddyserver/caddy/v2/modules/caddyhttp" +) + +func init() { + caddycmd.RegisterCommand(caddycmd.Command{ + Name: "file-server", + Func: cmdFileServer, + Usage: "[--domain ] [--path ] [--listen ] [--browse]", + Short: "Spins up a production-ready file server", + Long: ` +A simple but production-ready file server. Useful for quick deployments, +demos, and development. + +If a qualifying hostname is specified with --domain, the server will use +HTTPS if domain validation succeeds. Ensure A/AAAA records are properly +configured before using this option. + +The listener's socket address can be customized with the --listen flag. + +If --browse is enabled, requests for folders without an index file will +respond with a file listing.`, + Flags: func() *flag.FlagSet { + fs := flag.NewFlagSet("file-server", flag.ExitOnError) + fs.String("domain", "", "Domain name at which to serve the files") + fs.String("root", "", "The path to the root of the site") + fs.String("listen", "", "The address to which to bind the listener") + fs.Bool("browse", false, "Whether to enable directory browsing") + return fs + }(), + }) +} + +func cmdFileServer(fs caddycmd.Flags) (int, error) { + domain := fs.String("domain") + root := fs.String("root") + listen := fs.String("listen") + browse := fs.Bool("browse") + + handler := FileServer{Root: root} + if browse { + handler.Browse = new(Browse) + } + + route := caddyhttp.Route{ + HandlersRaw: []json.RawMessage{ + caddyconfig.JSONModuleObject(handler, "handler", "file_server", nil), + }, + } + if domain != "" { + route.MatcherSetsRaw = []map[string]json.RawMessage{ + map[string]json.RawMessage{ + "host": caddyconfig.JSON(caddyhttp.MatchHost{domain}, nil), + }, + } + } + + server := &caddyhttp.Server{ + Routes: caddyhttp.RouteList{route}, + } + if listen != "" { + server.Listen = []string{listen} + } + + httpApp := caddyhttp.App{ + Servers: map[string]*caddyhttp.Server{"static": server}, + } + + cfg := &caddy.Config{ + AppsRaw: map[string]json.RawMessage{ + "http": caddyconfig.JSON(httpApp, nil), + }, + } + + err := caddy.Run(cfg) + if err != nil { + return caddy.ExitCodeFailedStartup, err + } + + log.Println("Caddy 2 serving static files") + + select {} +} diff --git a/modules/caddyhttp/reverseproxy/command.go b/modules/caddyhttp/reverseproxy/command.go new file mode 100644 index 0000000..0a664ed --- /dev/null +++ b/modules/caddyhttp/reverseproxy/command.go @@ -0,0 +1,138 @@ +// Copyright 2015 Matthew Holt and The Caddy Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package reverseproxy + +import ( + "encoding/json" + "flag" + "log" + "net/http" + "net/url" + "strings" + + "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig" + "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" + caddycmd "github.com/caddyserver/caddy/v2/cmd" + "github.com/caddyserver/caddy/v2/modules/caddyhttp" + "github.com/caddyserver/caddy/v2/modules/caddyhttp/headers" + "github.com/mholt/certmagic" +) + +func init() { + caddycmd.RegisterCommand(caddycmd.Command{ + Name: "reverse-proxy", + Func: cmdReverseProxy, + Usage: "[--from ] [--to ]", + Short: "A quick and production-ready reverse proxy", + Long: ` +A simple but production-ready reverse proxy. Useful for quick deployments, +demos, and development. + +Simply shuttles HTTP traffic from the --from address to the --to address. + +If the --from address has a domain name, Caddy will attempt to serve the +proxy over HTTPS with a certificate. +`, + Flags: func() *flag.FlagSet { + fs := flag.NewFlagSet("file-server", flag.ExitOnError) + fs.String("from", "", "Address to receive traffic on") + fs.String("to", "", "Upstream address to proxy traffic to") + return fs + }(), + }) +} + +func cmdReverseProxy(fs caddycmd.Flags) (int, error) { + from := fs.String("from") + to := fs.String("to") + + if from == "" { + from = "localhost:" + httpcaddyfile.DefaultPort + } + + if !strings.Contains(from, "://") { + from = "http://" + from + } + + fromURL, err := url.Parse(from) + if err != nil { + fromURL.Host = from + } + + toURL, err := url.Parse(to) + if err != nil { + toURL.Host = to + } + + ht := HTTPTransport{} + if toURL.Scheme == "https" { + ht.TLS = new(TLSConfig) + } + + handler := Handler{ + TransportRaw: caddyconfig.JSONModuleObject(ht, "protocol", "http", nil), + Upstreams: UpstreamPool{{Dial: toURL.Host}}, + Headers: &headers.Handler{ + Request: &headers.HeaderOps{ + Set: http.Header{ + "Host": []string{"{http.handlers.reverse_proxy.upstream.host}"}, + }, + }, + }, + } + + route := caddyhttp.Route{ + HandlersRaw: []json.RawMessage{ + caddyconfig.JSONModuleObject(handler, "handler", "reverse_proxy", nil), + }, + } + if fromURL.Hostname() != "" { + route.MatcherSetsRaw = []map[string]json.RawMessage{ + map[string]json.RawMessage{ + "host": caddyconfig.JSON(caddyhttp.MatchHost{fromURL.Hostname()}, nil), + }, + } + } + + listen := ":" + httpcaddyfile.DefaultPort + if certmagic.HostQualifies(fromURL.Hostname()) { + listen = ":443" + } + + server := &caddyhttp.Server{ + Routes: caddyhttp.RouteList{route}, + Listen: []string{listen}, + } + + httpApp := caddyhttp.App{ + Servers: map[string]*caddyhttp.Server{"proxy": server}, + } + + cfg := &caddy.Config{ + AppsRaw: map[string]json.RawMessage{ + "http": caddyconfig.JSON(httpApp, nil), + }, + } + + err = caddy.Run(cfg) + if err != nil { + return caddy.ExitCodeFailedStartup, err + } + + log.Printf("Caddy 2 proxying from %s to %s", from, to) + + select {} +} -- cgit v1.2.3