Read host and protocol information from request for links
When constructing links we want to read the required host and protocol information in a dynamic manner from the request for constructing ACME links such as the directory information. This way, if the server is running behind a proxy, and we don't know what the exposed URL should be at runtime, we can construct the required information from the host, tls and X-Forwarded-Proto fields in the HTTP request. Inspired by the LetsEncrypt Boulder project (web/relative.go).pull/249/head
parent
f126962f3f
commit
639993bd09
@ -0,0 +1,28 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// baseURLFromRequest determines the base URL which should be used for constructing link URLs in e.g. the ACME directory
|
||||
// result by taking the request Host, TLS and Header[X-Forwarded-Proto] values into consideration.
|
||||
// If the Request.Host is an empty string, we return an empty string, to indicate that the configured
|
||||
// URL values should be used instead.
|
||||
// If this function returns a non-empty result, then this should be used in constructing ACME link URLs.
|
||||
func baseURLFromRequest(r *http.Request) string {
|
||||
// TODO: I semantically copied the functionality of determining the protol from boulder web/relative.go
|
||||
// which allows HTTP. Previously this was always forced to be HTTPS for absolute URLs. Should this be
|
||||
// changed to also always force HTTPS protocol?
|
||||
proto := "http"
|
||||
if specifiedProto := r.Header.Get("X-Forwarded-Proto"); specifiedProto != "" {
|
||||
proto = specifiedProto
|
||||
} else if r.TLS != nil {
|
||||
proto += "s"
|
||||
}
|
||||
|
||||
host := r.Host
|
||||
if host == "" {
|
||||
return ""
|
||||
}
|
||||
return proto + "://" + host
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetBaseUrl(t *testing.T) {
|
||||
tests := []struct {
|
||||
testFailedDescription string
|
||||
targetURL string
|
||||
expectedResult string
|
||||
requestPreparer func(*http.Request)
|
||||
}{
|
||||
{
|
||||
"HTTP host pass-through failed.",
|
||||
"http://my.dummy.host",
|
||||
"http://my.dummy.host",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"HTTPS host pass-through failed.",
|
||||
"https://my.dummy.host",
|
||||
"https://my.dummy.host",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"Port pass-through failed",
|
||||
"http://host.with.port:8080",
|
||||
"http://host.with.port:8080",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"Explicit host from Request.Host was not used.",
|
||||
"http://some.target.host:8080",
|
||||
"http://proxied.host",
|
||||
func(r *http.Request) {
|
||||
r.Host = "proxied.host"
|
||||
},
|
||||
},
|
||||
{
|
||||
"Explicit forwarded protocol from request header X-Forwarded-Proto was not used.",
|
||||
"http://some.host",
|
||||
"ssl://some.host",
|
||||
func(r *http.Request) {
|
||||
r.Header.Add("X-Forwarded-Proto", "ssl")
|
||||
},
|
||||
},
|
||||
{
|
||||
"Missing Request.Host value did not result in empty string result.",
|
||||
"http://some.host",
|
||||
"",
|
||||
func(r *http.Request) {
|
||||
r.Host = ""
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
request := httptest.NewRequest("GET", test.targetURL, nil)
|
||||
if test.requestPreparer != nil {
|
||||
test.requestPreparer(request)
|
||||
}
|
||||
result := baseURLFromRequest(request)
|
||||
if result != test.expectedResult {
|
||||
t.Errorf("Expected %q, but got %q", test.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue