mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-23 22:27:38 -05:00
Merge branch 'master' into getcertificate
This commit is contained in:
commit
d25a3e95e4
16 changed files with 343 additions and 24 deletions
|
@ -69,6 +69,23 @@ var directiveOrder = []directive{
|
|||
{"browse", setup.Browse},
|
||||
}
|
||||
|
||||
// RegisterDirective adds the given directive to caddy's list of directives.
|
||||
// Pass the name of a directive you want it to be placed after,
|
||||
// otherwise it will be placed at the bottom of the stack.
|
||||
func RegisterDirective(name string, setup SetupFunc, after string) {
|
||||
dir := directive{name: name, setup: setup}
|
||||
idx := len(directiveOrder)
|
||||
for i := range directiveOrder {
|
||||
if directiveOrder[i].name == after {
|
||||
idx = i + 1
|
||||
break
|
||||
}
|
||||
}
|
||||
newDirectives := append(directiveOrder[:idx], append([]directive{dir}, directiveOrder[idx:]...)...)
|
||||
directiveOrder = newDirectives
|
||||
parse.ValidDirectives[name] = struct{}{}
|
||||
}
|
||||
|
||||
// directive ties together a directive name with its setup function.
|
||||
type directive struct {
|
||||
name string
|
||||
|
|
31
caddy/directives_test.go
Normal file
31
caddy/directives_test.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package caddy
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRegister(t *testing.T) {
|
||||
directives := []directive{
|
||||
{"dummy", nil},
|
||||
{"dummy2", nil},
|
||||
}
|
||||
directiveOrder = directives
|
||||
RegisterDirective("foo", nil, "dummy")
|
||||
if len(directiveOrder) != 3 {
|
||||
t.Fatal("Should have 3 directives now")
|
||||
}
|
||||
getNames := func() (s []string) {
|
||||
for _, d := range directiveOrder {
|
||||
s = append(s, d.name)
|
||||
}
|
||||
return s
|
||||
}
|
||||
if !reflect.DeepEqual(getNames(), []string{"dummy", "foo", "dummy2"}) {
|
||||
t.Fatalf("directive order doesn't match: %s", getNames())
|
||||
}
|
||||
RegisterDirective("bar", nil, "ASDASD")
|
||||
if !reflect.DeepEqual(getNames(), []string{"dummy", "foo", "dummy2", "bar"}) {
|
||||
t.Fatalf("directive order doesn't match: %s", getNames())
|
||||
}
|
||||
}
|
|
@ -23,9 +23,9 @@ func RequestCallback(w http.ResponseWriter, r *http.Request) bool {
|
|||
scheme = "https"
|
||||
}
|
||||
|
||||
hostname, _, err := net.SplitHostPort(r.URL.Host)
|
||||
hostname, _, err := net.SplitHostPort(r.Host)
|
||||
if err != nil {
|
||||
hostname = r.URL.Host
|
||||
hostname = r.Host
|
||||
}
|
||||
|
||||
upstream, err := url.Parse(scheme + "://" + hostname + ":" + AlternatePort)
|
||||
|
|
8
dist/README.txt
vendored
8
dist/README.txt
vendored
|
@ -8,9 +8,11 @@ Source Code
|
|||
https://github.com/mholt/caddy
|
||||
|
||||
|
||||
For instructions on using Caddy, please see the user guide on the website. For a list of what's new in this version, see CHANGES.txt.
|
||||
For instructions on using Caddy, please see the user guide on the website.
|
||||
For a list of what's new in this version, see CHANGES.txt.
|
||||
|
||||
If you have a question, bug report, or would like to contribute, please open an issue or submit a pull request on GitHub. Your contributions do not go unnoticed!
|
||||
If you have a question, bug report, or would like to contribute, please open an
|
||||
issue or submit a pull request on GitHub. Your contributions do not go unnoticed!
|
||||
|
||||
For a good time, follow @mholt6 on Twitter.
|
||||
|
||||
|
@ -18,4 +20,4 @@ And thanks - you're awesome!
|
|||
|
||||
|
||||
---
|
||||
(c) 2015 Matthew Holt
|
||||
(c) 2015 - 2016 Matthew Holt
|
||||
|
|
|
@ -139,7 +139,7 @@ md5:$apr1$l42y8rex$pOA2VJ0x/0TwaFeAF9nX61`
|
|||
if rule.Password, err = GetHtpasswdMatcher(filename, rule.Username, siteRoot); err != nil {
|
||||
t.Fatalf("GetHtpasswdMatcher(%q, %q): %v", htfh.Name(), rule.Username, err)
|
||||
}
|
||||
t.Logf("%d. username=%q password=%v", i, rule.Username, rule.Password)
|
||||
t.Logf("%d. username=%q", i, rule.Username)
|
||||
if !rule.Password(htpasswdPasswd) || rule.Password(htpasswdPasswd+"!") {
|
||||
t.Errorf("%d (%s) password does not match.", i, rule.Username)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import (
|
|||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/russross/blackfriday"
|
||||
)
|
||||
|
||||
// This file contains the context and functions available for
|
||||
|
@ -190,3 +192,17 @@ func (c Context) StripExt(path string) string {
|
|||
func (c Context) Replace(input, find, replacement string) string {
|
||||
return strings.Replace(input, find, replacement, -1)
|
||||
}
|
||||
|
||||
// Markdown returns the HTML contents of the markdown contained in filename
|
||||
// (relative to the site root).
|
||||
func (c Context) Markdown(filename string) (string, error) {
|
||||
body, err := c.Include(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
renderer := blackfriday.HtmlRenderer(0, "", "")
|
||||
extns := blackfriday.EXTENSION_TABLES | blackfriday.EXTENSION_FENCED_CODE | blackfriday.EXTENSION_STRIKETHROUGH | blackfriday.EXTENSION_DEFINITION_LISTS
|
||||
markdown := blackfriday.Markdown([]byte(body), renderer, extns)
|
||||
|
||||
return string(markdown), nil
|
||||
}
|
||||
|
|
|
@ -92,6 +92,45 @@ func TestIncludeNotExisting(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestMarkdown(t *testing.T) {
|
||||
context := getContextOrFail(t)
|
||||
|
||||
inputFilename := "test_file"
|
||||
absInFilePath := filepath.Join(fmt.Sprintf("%s", context.Root), inputFilename)
|
||||
defer func() {
|
||||
err := os.Remove(absInFilePath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
t.Fatalf("Failed to clean test file!")
|
||||
}
|
||||
}()
|
||||
|
||||
tests := []struct {
|
||||
fileContent string
|
||||
expectedContent string
|
||||
}{
|
||||
// Test 0 - test parsing of markdown
|
||||
{
|
||||
fileContent: "* str1\n* str2\n",
|
||||
expectedContent: "<ul>\n<li>str1</li>\n<li>str2</li>\n</ul>\n",
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
testPrefix := getTestPrefix(i)
|
||||
|
||||
// WriteFile truncates the contentt
|
||||
err := ioutil.WriteFile(absInFilePath, []byte(test.fileContent), os.ModePerm)
|
||||
if err != nil {
|
||||
t.Fatal(testPrefix+"Failed to create test file. Error was: %v", err)
|
||||
}
|
||||
|
||||
content, _ := context.Markdown(inputFilename)
|
||||
if content != test.expectedContent {
|
||||
t.Errorf(testPrefix+"Expected content [%s] but found [%s]. Input file was: %s", test.expectedContent, content, inputFilename)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCookie(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
|
|
|
@ -70,7 +70,8 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
|
|||
}
|
||||
|
||||
// Connect to FastCGI gateway
|
||||
fcgi, err := getClient(&rule)
|
||||
network, address := rule.parseAddress()
|
||||
fcgi, err := Dial(network, address)
|
||||
if err != nil {
|
||||
return http.StatusBadGateway, err
|
||||
}
|
||||
|
@ -128,15 +129,28 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
|
|||
return h.Next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func getClient(r *Rule) (*FCGIClient, error) {
|
||||
// check if unix socket or TCP
|
||||
// parseAddress returns the network and address of r.
|
||||
// The first string is the network, "tcp" or "unix", implied from the scheme and address.
|
||||
// The second string is r.Address, with scheme prefixes removed.
|
||||
// The two returned strings can be used as parameters to the Dial() function.
|
||||
func (r Rule) parseAddress() (string, string) {
|
||||
// check if address has tcp scheme explicitly set
|
||||
if strings.HasPrefix(r.Address, "tcp://") {
|
||||
return "tcp", r.Address[len("tcp://"):]
|
||||
}
|
||||
// check if address has fastcgi scheme explicity set
|
||||
if strings.HasPrefix(r.Address, "fastcgi://") {
|
||||
return "tcp", r.Address[len("fastcgi://"):]
|
||||
}
|
||||
// check if unix socket
|
||||
if trim := strings.HasPrefix(r.Address, "unix"); strings.HasPrefix(r.Address, "/") || trim {
|
||||
if trim {
|
||||
r.Address = r.Address[len("unix:"):]
|
||||
return "unix", r.Address[len("unix:"):]
|
||||
}
|
||||
return Dial("unix", r.Address)
|
||||
return "unix", r.Address
|
||||
}
|
||||
return Dial("tcp", r.Address)
|
||||
// default case, a plain tcp address with no scheme
|
||||
return "tcp", r.Address
|
||||
}
|
||||
|
||||
func writeHeader(w http.ResponseWriter, r *http.Response) {
|
||||
|
@ -168,7 +182,7 @@ func (h Handler) buildEnv(r *http.Request, rule Rule, fpath string) (map[string]
|
|||
|
||||
// Separate remote IP and port; more lenient than net.SplitHostPort
|
||||
var ip, port string
|
||||
if idx := strings.Index(r.RemoteAddr, ":"); idx > -1 {
|
||||
if idx := strings.LastIndex(r.RemoteAddr, ":"); idx > -1 {
|
||||
ip = r.RemoteAddr[:idx]
|
||||
port = r.RemoteAddr[idx+1:]
|
||||
} else {
|
||||
|
|
95
middleware/fastcgi/fastcgi_test.go
Normal file
95
middleware/fastcgi/fastcgi_test.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
package fastcgi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRuleParseAddress(t *testing.T) {
|
||||
|
||||
getClientTestTable := []struct {
|
||||
rule *Rule
|
||||
expectednetwork string
|
||||
expectedaddress string
|
||||
}{
|
||||
{&Rule{Address: "tcp://172.17.0.1:9000"}, "tcp", "172.17.0.1:9000"},
|
||||
{&Rule{Address: "fastcgi://localhost:9000"}, "tcp", "localhost:9000"},
|
||||
{&Rule{Address: "172.17.0.15"}, "tcp", "172.17.0.15"},
|
||||
{&Rule{Address: "/my/unix/socket"}, "unix", "/my/unix/socket"},
|
||||
{&Rule{Address: "unix:/second/unix/socket"}, "unix", "/second/unix/socket"},
|
||||
}
|
||||
|
||||
for _, entry := range getClientTestTable {
|
||||
if actualnetwork, _ := entry.rule.parseAddress(); actualnetwork != entry.expectednetwork {
|
||||
t.Errorf("Unexpected network for address string %v. Got %v, expected %v", entry.rule.Address, actualnetwork, entry.expectednetwork)
|
||||
}
|
||||
if _, actualaddress := entry.rule.parseAddress(); actualaddress != entry.expectedaddress {
|
||||
t.Errorf("Unexpected parsed address for address string %v. Got %v, expected %v", entry.rule.Address, actualaddress, entry.expectedaddress)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestBuildEnv(t *testing.T) {
|
||||
|
||||
buildEnvSingle := func(r *http.Request, rule Rule, fpath string, envExpected map[string]string, t *testing.T) {
|
||||
|
||||
h := Handler{}
|
||||
|
||||
env, err := h.buildEnv(r, rule, fpath)
|
||||
if err != nil {
|
||||
t.Error("Unexpected error:", err.Error())
|
||||
}
|
||||
|
||||
for k, v := range envExpected {
|
||||
if env[k] != v {
|
||||
t.Errorf("Unexpected %v. Got %v, expected %v", k, env[k], v)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
rule := Rule{}
|
||||
url, err := url.Parse("http://localhost:2015/fgci_test.php?test=blabla")
|
||||
if err != nil {
|
||||
t.Error("Unexpected error:", err.Error())
|
||||
}
|
||||
|
||||
r := http.Request{
|
||||
Method: "GET",
|
||||
URL: url,
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Host: "localhost:2015",
|
||||
RemoteAddr: "[2b02:1810:4f2d:9400:70ab:f822:be8a:9093]:51688",
|
||||
RequestURI: "/fgci_test.php",
|
||||
}
|
||||
|
||||
fpath := "/fgci_test.php"
|
||||
|
||||
var envExpected = map[string]string{
|
||||
"REMOTE_ADDR": "[2b02:1810:4f2d:9400:70ab:f822:be8a:9093]",
|
||||
"REMOTE_PORT": "51688",
|
||||
"SERVER_PROTOCOL": "HTTP/1.1",
|
||||
"QUERY_STRING": "test=blabla",
|
||||
"REQUEST_METHOD": "GET",
|
||||
"HTTP_HOST": "localhost:2015",
|
||||
}
|
||||
|
||||
// 1. Test for full canonical IPv6 address
|
||||
buildEnvSingle(&r, rule, fpath, envExpected, t)
|
||||
|
||||
// 2. Test for shorthand notation of IPv6 address
|
||||
r.RemoteAddr = "[::1]:51688"
|
||||
envExpected["REMOTE_ADDR"] = "[::1]"
|
||||
buildEnvSingle(&r, rule, fpath, envExpected, t)
|
||||
|
||||
// 3. Test for IPv4 address
|
||||
r.RemoteAddr = "192.168.0.10:51688"
|
||||
envExpected["REMOTE_ADDR"] = "192.168.0.10"
|
||||
buildEnvSingle(&r, rule, fpath, envExpected, t)
|
||||
|
||||
}
|
|
@ -169,12 +169,11 @@ type FCGIClient struct {
|
|||
reqID uint16
|
||||
}
|
||||
|
||||
// Dial connects to the fcgi responder at the specified network address.
|
||||
// DialWithDialer connects to the fcgi responder at the specified network address, using custom net.Dialer.
|
||||
// See func net.Dial for a description of the network and address parameters.
|
||||
func Dial(network, address string) (fcgi *FCGIClient, err error) {
|
||||
func DialWithDialer(network, address string, dialer net.Dialer) (fcgi *FCGIClient, err error) {
|
||||
var conn net.Conn
|
||||
|
||||
conn, err = net.Dial(network, address)
|
||||
conn, err = dialer.Dial(network, address)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -188,6 +187,12 @@ func Dial(network, address string) (fcgi *FCGIClient, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Dial connects to the fcgi responder at the specified network address, using default net.Dialer.
|
||||
// See func net.Dial for a description of the network and address parameters.
|
||||
func Dial(network, address string) (fcgi *FCGIClient, err error) {
|
||||
return DialWithDialer(network, address, net.Dialer{})
|
||||
}
|
||||
|
||||
// Close closes fcgi connnection
|
||||
func (c *FCGIClient) Close() {
|
||||
c.rwc.Close()
|
||||
|
|
|
@ -68,7 +68,7 @@ func (md Markdown) Process(c *Config, requestPath string, b []byte, ctx middlewa
|
|||
}
|
||||
|
||||
// process markdown
|
||||
extns := blackfriday.EXTENSION_TABLES | blackfriday.EXTENSION_FENCED_CODE | blackfriday.EXTENSION_STRIKETHROUGH
|
||||
extns := blackfriday.EXTENSION_TABLES | blackfriday.EXTENSION_FENCED_CODE | blackfriday.EXTENSION_STRIKETHROUGH | blackfriday.EXTENSION_DEFINITION_LISTS
|
||||
markdown = blackfriday.Markdown(markdown, c.Renderer, extns)
|
||||
|
||||
// set it as body for template
|
||||
|
|
|
@ -63,7 +63,6 @@ var tryDuration = 60 * time.Second
|
|||
|
||||
// ServeHTTP satisfies the middleware.Handler interface.
|
||||
func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
|
||||
for _, upstream := range p.Upstreams {
|
||||
if middleware.Path(r.URL.Path).Matches(upstream.From()) && upstream.IsAllowedPath(r.URL.Path) {
|
||||
var replacer middleware.Replacer
|
||||
|
|
|
@ -3,6 +3,7 @@ package proxy
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
@ -13,7 +14,9 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"runtime"
|
||||
"time"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
@ -160,6 +163,69 @@ func TestWebSocketReverseProxyFromWSClient(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestUnixSocketProxy(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
return
|
||||
}
|
||||
|
||||
trialMsg := "Is it working?"
|
||||
|
||||
var proxySuccess bool
|
||||
|
||||
// This is our fake "application" we want to proxy to
|
||||
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Request was proxied when this is called
|
||||
proxySuccess = true
|
||||
|
||||
fmt.Fprint(w, trialMsg)
|
||||
}))
|
||||
|
||||
// Get absolute path for unix: socket
|
||||
socketPath, err := filepath.Abs("./test_socket")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to get absolute path: %v", err)
|
||||
}
|
||||
|
||||
// Change httptest.Server listener to listen to unix: socket
|
||||
ln, err := net.Listen("unix", socketPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to listen: %v", err)
|
||||
}
|
||||
ts.Listener = ln
|
||||
|
||||
ts.Start()
|
||||
defer ts.Close()
|
||||
|
||||
url := strings.Replace(ts.URL, "http://", "unix:", 1)
|
||||
p := newWebSocketTestProxy(url)
|
||||
|
||||
echoProxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
p.ServeHTTP(w, r)
|
||||
}))
|
||||
defer echoProxy.Close()
|
||||
|
||||
res, err := http.Get(echoProxy.URL)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to GET: %v", err)
|
||||
}
|
||||
|
||||
greeting, err := ioutil.ReadAll(res.Body)
|
||||
res.Body.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to GET: %v", err)
|
||||
}
|
||||
|
||||
actualMsg := fmt.Sprintf("%s", greeting)
|
||||
|
||||
if !proxySuccess {
|
||||
t.Errorf("Expected request to be proxied, but it wasn't")
|
||||
}
|
||||
|
||||
if actualMsg != trialMsg {
|
||||
t.Errorf("Expected '%s' but got '%s' instead", trialMsg, actualMsg)
|
||||
}
|
||||
}
|
||||
|
||||
func newFakeUpstream(name string, insecure bool) *fakeUpstream {
|
||||
uri, _ := url.Parse(name)
|
||||
u := &fakeUpstream{
|
||||
|
|
|
@ -59,6 +59,18 @@ func singleJoiningSlash(a, b string) string {
|
|||
return a + b
|
||||
}
|
||||
|
||||
// Though the relevant directive prefix is just "unix:", url.Parse
|
||||
// will - assuming the regular URL scheme - add additional slashes
|
||||
// as if "unix" was a request protocol.
|
||||
// What we need is just the path, so if "unix:/var/run/www.socket"
|
||||
// was the proxy directive, the parsed hostName would be
|
||||
// "unix:///var/run/www.socket", hence the ambiguous trimming.
|
||||
func socketDial(hostName string) func(network, addr string) (conn net.Conn, err error) {
|
||||
return func(network, addr string) (conn net.Conn, err error) {
|
||||
return net.Dial("unix", hostName[len("unix://"):])
|
||||
}
|
||||
}
|
||||
|
||||
// NewSingleHostReverseProxy returns a new ReverseProxy that rewrites
|
||||
// URLs to the scheme, host, and base path provided in target. If the
|
||||
// target's path is "/base" and the incoming request was for "/dir",
|
||||
|
@ -68,8 +80,15 @@ func singleJoiningSlash(a, b string) string {
|
|||
func NewSingleHostReverseProxy(target *url.URL, without string) *ReverseProxy {
|
||||
targetQuery := target.RawQuery
|
||||
director := func(req *http.Request) {
|
||||
req.URL.Scheme = target.Scheme
|
||||
req.URL.Host = target.Host
|
||||
if target.Scheme == "unix" {
|
||||
// to make Dial work with unix URL,
|
||||
// scheme and host have to be faked
|
||||
req.URL.Scheme = "http"
|
||||
req.URL.Host = "socket"
|
||||
} else {
|
||||
req.URL.Scheme = target.Scheme
|
||||
req.URL.Host = target.Host
|
||||
}
|
||||
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
|
||||
if targetQuery == "" || req.URL.RawQuery == "" {
|
||||
req.URL.RawQuery = targetQuery + req.URL.RawQuery
|
||||
|
@ -80,7 +99,13 @@ func NewSingleHostReverseProxy(target *url.URL, without string) *ReverseProxy {
|
|||
req.URL.Path = strings.TrimPrefix(req.URL.Path, without)
|
||||
}
|
||||
}
|
||||
return &ReverseProxy{Director: director}
|
||||
rp := &ReverseProxy{Director: director}
|
||||
if target.Scheme == "unix" {
|
||||
rp.Transport = &http.Transport{
|
||||
Dial: socketDial(target.String()),
|
||||
}
|
||||
}
|
||||
return rp
|
||||
}
|
||||
|
||||
func copyHeader(dst, src http.Header) {
|
||||
|
@ -104,6 +129,9 @@ var hopHeaders = []string{
|
|||
"Upgrade",
|
||||
}
|
||||
|
||||
// InsecureTransport is used to facilitate HTTPS proxying
|
||||
// when it is OK for upstream to be using a bad certificate,
|
||||
// since this transport skips verification.
|
||||
var InsecureTransport http.RoundTripper = &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
|
|
|
@ -65,7 +65,8 @@ func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) {
|
|||
|
||||
upstream.Hosts = make([]*UpstreamHost, len(to))
|
||||
for i, host := range to {
|
||||
if !strings.HasPrefix(host, "http") {
|
||||
if !strings.HasPrefix(host, "http") &&
|
||||
!strings.HasPrefix(host, "unix:") {
|
||||
host = "http://" + host
|
||||
}
|
||||
uh := &UpstreamHost{
|
||||
|
|
|
@ -329,9 +329,15 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
DefaultErrorFunc(w, r, status)
|
||||
}
|
||||
} else {
|
||||
// Get the remote host
|
||||
remoteHost, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
remoteHost = r.RemoteAddr
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprintf(w, "No such host at %s", s.Server.Addr)
|
||||
log.Printf("[INFO] %s - No such host at %s", host, s.Server.Addr)
|
||||
log.Printf("[INFO] %s - No such host at %s (requested by %s)", host, s.Server.Addr, remoteHost)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue