diff --git a/docs/source/concepts/resource_tree.rst b/docs/source/concepts/resource_tree.rst index b02bc58..2179abe 100644 --- a/docs/source/concepts/resource_tree.rst +++ b/docs/source/concepts/resource_tree.rst @@ -12,8 +12,6 @@ to compose the response. We access the resource tree easily with the ``kapow set`` and ``kapow get`` subcommands. -.. // DOING #85: /request/remote - .. // DOING #10: /route/id .. // DOING #113: /request/ssl/client/i/dn @@ -30,6 +28,7 @@ Overview │ ├──── host Host part of the URL │ ├──── version HTTP version of the request │ ├──── path Complete URL path (URL-unquoted) + │ ├──── remote IP address of client │ ├──── matches │ │ └──── Previously matched URL path parts │ ├──── params diff --git a/internal/server/data/resource.go b/internal/server/data/resource.go index 53982fd..e21c40f 100644 --- a/internal/server/data/resource.go +++ b/internal/server/data/resource.go @@ -68,6 +68,11 @@ func getRequestPath(w http.ResponseWriter, r *http.Request, h *model.Handler) { _, _ = w.Write([]byte(h.Request.URL.Path)) } +func getRequestRemote(w http.ResponseWriter, r *http.Request, h *model.Handler) { + w.Header().Add("Content-Type", "application/octet-stream") + _, _ = w.Write([]byte(r.RemoteAddr)) +} + func getRequestMatches(w http.ResponseWriter, r *http.Request, h *model.Handler) { w.Header().Add("Content-Type", "application/octet-stream") name := mux.Vars(r)["name"] diff --git a/internal/server/data/resource_test.go b/internal/server/data/resource_test.go index 2303ee2..5a7032b 100644 --- a/internal/server/data/resource_test.go +++ b/internal/server/data/resource_test.go @@ -26,6 +26,7 @@ import ( "net/http/httptest" "net/url" "reflect" + "regexp" "strings" "testing" @@ -286,8 +287,6 @@ func TestGetRequestVersionReturnsTheCorrectHttpVersion(t *testing.T) { } } -// DOING #85: /request/remote - func TestGetRequestPath200sOnHappyPath(t *testing.T) { h := model.Handler{ Request: httptest.NewRequest("POST", "/", nil), @@ -352,6 +351,57 @@ func TestGetRequestPathDoesntReturnQueryStringParams(t *testing.T) { } } +func TestGetRequestRemote200sOnHappyPath(t *testing.T) { + h := model.Handler{ + Request: httptest.NewRequest("POST", "/", nil), + Writer: httptest.NewRecorder(), + } + r := httptest.NewRequest("GET", "/not-important-here", nil) + w := httptest.NewRecorder() + + getRequestRemote(w, r, &h) + + res := w.Result() + if res.StatusCode != http.StatusOK { + t.Errorf("Status code mismatch. Expected: %d, got: %d", http.StatusOK, res.StatusCode) + } +} + +func TestGetRequestRemoteSetsOctectStreamContentType(t *testing.T) { + h := model.Handler{ + Request: httptest.NewRequest("POST", "/", nil), + Writer: httptest.NewRecorder(), + } + r := httptest.NewRequest("GET", "/not-important-here", nil) + w := httptest.NewRecorder() + + getRequestRemote(w, r, &h) + + res := w.Result() + if ct := res.Header.Get("Content-Type"); ct != "application/octet-stream" { + t.Errorf("Content Type mismatch. Expected: %v, got: %v", "application/octet-stream", ct) + } +} + +func TestGetRequestRemoteReturnsTheCorrectRemote(t *testing.T) { + h := model.Handler{ + Request: httptest.NewRequest("POST", "http://www.foo.bar:8080/", nil), + Writer: httptest.NewRecorder(), + } + r := httptest.NewRequest("GET", "/not-important-here", nil) + w := httptest.NewRecorder() + + getRequestRemote(w, r, &h) + + res := w.Result() + body, _ := ioutil.ReadAll(res.Body) + rem := body[:bytes.Index(body, []byte(":"))] + re, _ := regexp.Compile(`^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$`) + if found := re.Match(rem); !found { + t.Errorf("Version mismatch. Expected %v, got %v", `^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$`, string(rem)) + } +} + // DOING #113: /request/ssl/client/i/dn func createMuxRequest(pattern, url, method string, content io.Reader) (req *http.Request) {