]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: FileServer method check + minimal OPTIONS implementation
authorPascal S. de Kloe <pascal@quies.net>
Tue, 21 Jun 2022 16:50:35 +0000 (18:50 +0200)
committerDavid Chase <drchase@google.com>
Mon, 29 Aug 2022 17:12:01 +0000 (17:12 +0000)
FileServer provides a read-only service. Methods other than GET or HEAD should
be denied with an Allow header.

Fixes #53501

Change-Id: I1d31b405eefd90565ecd474ac3f8d8d6e3b15072
Reviewed-on: https://go-review.googlesource.com/c/go/+/413554
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Damien Neil <dneil@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
src/net/http/fs.go
src/net/http/fs_test.go

index 87caeb7e90f39a1a21dc8c2307241122d1a93116..cf80018b5e1a8475cad2987d88bc31640ea5dc6d 100644 (file)
@@ -843,12 +843,22 @@ func FileServer(root FileSystem) Handler {
 }
 
 func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) {
-       upath := r.URL.Path
-       if !strings.HasPrefix(upath, "/") {
-               upath = "/" + upath
-               r.URL.Path = upath
+       const options = MethodOptions + ", " + MethodGet + ", " + MethodHead
+
+       switch r.Method {
+       case MethodGet, MethodHead:
+               if !strings.HasPrefix(r.URL.Path, "/") {
+                       r.URL.Path = "/" + r.URL.Path
+               }
+               serveFile(w, r, f.root, path.Clean(r.URL.Path), true)
+
+       case MethodOptions:
+               w.Header().Set("Allow", options)
+
+       default:
+               w.Header().Set("Allow", options)
+               Error(w, "read-only", StatusMethodNotAllowed)
        }
-       serveFile(w, r, f.root, path.Clean(upath), true)
 }
 
 // httpRange specifies the byte range to be sent to the client.
index 4be561cdfa1fa7864240a7e5e515db23b1a36454..077c037c853edcf2013dc45ed9f2f99bc97ef3fe 100644 (file)
@@ -24,6 +24,7 @@ import (
        "reflect"
        "regexp"
        "runtime"
+       "sort"
        "strings"
        "testing"
        "time"
@@ -404,6 +405,47 @@ func TestFileServerImplicitLeadingSlash(t *testing.T) {
        }
 }
 
+func TestFileServerMethodOptions(t *testing.T) {
+       defer afterTest(t)
+       const want = "GET, HEAD, OPTIONS"
+       ts := httptest.NewServer(FileServer(Dir(".")))
+       defer ts.Close()
+
+       tests := []struct {
+               method     string
+               wantStatus int
+       }{
+               {MethodOptions, StatusOK},
+
+               {MethodDelete, StatusMethodNotAllowed},
+               {MethodPut, StatusMethodNotAllowed},
+               {MethodPost, StatusMethodNotAllowed},
+       }
+
+       for _, test := range tests {
+               req, err := NewRequest(test.method, ts.URL, nil)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               res, err := ts.Client().Do(req)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               defer res.Body.Close()
+
+               if res.StatusCode != test.wantStatus {
+                       t.Errorf("%s got status %q, want code %d", test.method, res.Status, test.wantStatus)
+               }
+
+               a := strings.Split(res.Header.Get("Allow"), ", ")
+               sort.Strings(a)
+               got := strings.Join(a, ", ")
+               if got != want {
+                       t.Errorf("%s got Allow header %q, want %q", test.method, got, want)
+               }
+       }
+}
+
 func TestDirJoin(t *testing.T) {
        if runtime.GOOS == "windows" {
                t.Skip("skipping test on windows")