]> Sergey Matveev's repositories - godlighty.git/blobdiff - handler.go
Seems there is not problems with HEAD
[godlighty.git] / handler.go
index c613d2a6b9a4c61fd39f4e52eac3c0cde47e72f6..52d9a1082619c6a191bb362757e2c044f874d4a8 100644 (file)
@@ -20,10 +20,13 @@ package godlighty
 import (
        "bytes"
        "compress/gzip"
+       "errors"
        "fmt"
        "io/ioutil"
        "log"
+       "net"
        "net/http"
+       "net/url"
        "os"
        "path"
        "strconv"
@@ -42,8 +45,6 @@ const (
 )
 
 var (
-       CompressibleContentTypes = make(map[string]struct{})
-
        gzPool = sync.Pool{
                New: func() interface{} { return gzip.NewWriter(ioutil.Discard) },
        }
@@ -63,43 +64,78 @@ var (
        MainHandler Handler
 )
 
+func PathWithQuery(u *url.URL) string {
+       if u.RawQuery == "" {
+               return u.EscapedPath()
+       }
+       return u.EscapedPath() + "?" + u.RawQuery
+}
+
 type Handler struct{}
 
 func (h Handler) Handle(
        w http.ResponseWriter, r *http.Request,
        host string, cfg *HostCfg,
 ) {
-       if cfg == nil {
-               fmt.Printf("%s %s \"%s %s %s\" %d \"%s\"\n",
-                       r.RemoteAddr, host, r.Method, r.URL.Path, r.Proto,
+       notFound := func() {
+               fmt.Printf("%s %s \"%s %+q %s\" %d \"%s\"\n",
+                       r.RemoteAddr, host, r.Method, PathWithQuery(r.URL), r.Proto,
                        http.StatusNotFound,
                        r.Header.Get("User-Agent"),
                )
                http.NotFound(w, r)
+       }
+       if cfg == nil {
+               notFound()
                return
        }
 
-       for _, hook := range cfg.Hooks {
-               if hook(w, r) {
-                       return
-               }
+       var username string
+       var err error
+       if cfg.Auth != nil {
+               username, err = performAuth(w, r, cfg.Auth)
+       }
+       if username != "" {
+               username = "user:" + username + " "
        }
-
        printErr := func(code int, err error) {
-               fmt.Printf("%s %s \"%s %s %s\" %d \"%s\" \"%s\"\n",
-                       r.RemoteAddr, host, r.Method, r.URL.Path, r.Proto,
+               fmt.Printf("%s %s \"%s %+q %s\" %d \"%s\" %s\"%s\"\n",
+                       r.RemoteAddr, host, r.Method, PathWithQuery(r.URL), r.Proto,
                        code, err.Error(),
-                       r.Header.Get("User-Agent"),
+                       username, r.Header.Get("User-Agent"),
                )
        }
+       switch err {
+       case nil:
+               break
+       case Unauthorized:
+               printErr(http.StatusUnauthorized, err)
+               return
+       default:
+               printErr(http.StatusInternalServerError, err)
+               http.Error(w, "internal error", http.StatusInternalServerError)
+               return
+       }
+
+       if cfg.TLS != nil && len(cfg.TLS.ClientCAs) > 0 {
+               if r.TLS == nil {
+                       err = errors.New("TLS client authentication required")
+                       printErr(http.StatusForbidden, err)
+                       http.Error(w, err.Error(), http.StatusForbidden)
+                       return
+               } else {
+                       username += r.TLS.PeerCertificates[0].Subject.String() + " "
+               }
+       }
+
+       for _, hook := range cfg.Hooks {
+               if done := hook(w, r); done {
+                       return
+               }
+       }
 
        if cfg.Root == "" {
-               fmt.Printf("%s %s \"%s %s %s\" %d \"%s\"\n",
-                       r.RemoteAddr, host, r.Method, r.URL.Path, r.Proto,
-                       http.StatusNotFound,
-                       r.Header.Get("User-Agent"),
-               )
-               http.NotFound(w, r)
+               notFound()
                return
        }
 
@@ -112,19 +148,19 @@ func (h Handler) Handle(
                }
                wc := &CountResponseWriter{ResponseWriter: w}
                dav.ServeHTTP(wc, r)
-               fmt.Printf("%s %s \"WebDAV %s\" %d %d \"%s\"\n",
-                       r.RemoteAddr, host, r.URL.Path,
+               fmt.Printf("%s %s \"WebDAV %+q\" %d %d %s\"%s\"\n",
+                       r.RemoteAddr, host, PathWithQuery(r.URL),
                        wc.Status, wc.Size,
-                       r.Header.Get("User-Agent"),
+                       username, r.Header.Get("User-Agent"),
                )
                return
        }
 
-       if !(r.Method == "" || r.Method == http.MethodGet) {
-               fmt.Printf("%s %s \"%s %s %s\" %d \"%s\"\n",
-                       r.RemoteAddr, host, r.Method, r.URL.Path, r.Proto,
+       if !(r.Method == "" || r.Method == http.MethodGet || r.Method == http.MethodHead) {
+               fmt.Printf("%s %s \"%s %+q %s\" %d %s\"%s\"\n",
+                       r.RemoteAddr, host, r.Method, PathWithQuery(r.URL), r.Proto,
                        http.StatusMethodNotAllowed,
-                       r.Header.Get("User-Agent"),
+                       username, r.Header.Get("User-Agent"),
                )
                http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
                return
@@ -132,16 +168,12 @@ func (h Handler) Handle(
        var fd *os.File
        var contentType string
        var etag string
-       pth := path.Clean(path.Join(cfg.Root, r.URL.Path))
+       pthOrig := path.Clean(path.Join(cfg.Root, r.URL.Path))
+       pth := pthOrig
 IndexLookup:
        fi, err := os.Stat(pth)
        if err != nil {
-               fmt.Printf("%s %s \"%s %s %s\" %d \"%s\"\n",
-                       r.RemoteAddr, host, r.Method, r.URL.Path, r.Proto,
-                       http.StatusNotFound,
-                       r.Header.Get("User-Agent"),
-               )
-               http.NotFound(w, r)
+               notFound()
                return
        }
        if fi.IsDir() {
@@ -166,7 +198,7 @@ IndexLookup:
                                return
                        }
                        var readme []byte
-                       for _, f := range append(cfg.DirListReadmes, Readme) {
+                       for _, f := range append(cfg.Readmes, Readme) {
                                readme, _ = ioutil.ReadFile(path.Join(pth, f))
                                if readme != nil {
                                        break
@@ -180,12 +212,15 @@ IndexLookup:
                        }
                        contentType = "text/html; charset=utf-8"
                } else {
-                       if cfg.Index == "" {
-                               pth = path.Join(pth, Index)
-                       } else {
-                               pth = path.Join(pth, cfg.Index)
+                       for _, index := range append(cfg.Indexes, Index) {
+                               p := path.Join(pth, index)
+                               if _, err := os.Stat(p); err == nil {
+                                       pth = p
+                                       goto IndexLookup
+                               }
                        }
-                       goto IndexLookup
+                       notFound()
+                       return
                }
        }
 
@@ -210,7 +245,7 @@ IndexLookup:
        }
 
        if contentType == "" {
-               contentType = mediaType(path.Base(pth), cfg.MIMEOverride)
+               contentType = mediaType(path.Base(pth), cfg.MIMEs)
        }
        contentTypeBase := strings.SplitN(contentType, ";", 2)[0]
        w.Header().Set("Content-Type", contentType)
@@ -259,18 +294,18 @@ IndexLookup:
                wr := wc.(*gzipResponseWriter)
                w.WriteHeader(wr.status)
                w.Write(bufCompressed.Bytes())
-               fmt.Printf("%s %s \"%s %s %s\" %d %d \"%s\"\n",
-                       r.RemoteAddr, host, r.Method, r.URL.Path, r.Proto,
+               fmt.Printf("%s %s \"%s %+q %s\" %d %d %s\"%s\"\n",
+                       r.RemoteAddr, host, r.Method, PathWithQuery(r.URL), r.Proto,
                        wr.status, size,
-                       r.Header.Get("User-Agent"),
+                       username, r.Header.Get("User-Agent"),
                )
                return
        }
        wr := wc.(*CountResponseWriter)
-       fmt.Printf("%s %s \"%s %s %s\" %d %d \"%s\"\n",
-               r.RemoteAddr, host, r.Method, r.URL.Path, r.Proto,
+       fmt.Printf("%s %s \"%s %+q %s\" %d %d %s\"%s\"\n",
+               r.RemoteAddr, host, r.Method, PathWithQuery(r.URL), r.Proto,
                wr.Status, wr.Size,
-               r.Header.Get("User-Agent"),
+               username, r.Header.Get("User-Agent"),
        )
 }
 
@@ -279,6 +314,9 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
                http.Error(w, "invalid URL path", http.StatusBadRequest)
                return
        }
-       host := strings.SplitN(r.Host, ":", 2)[0]
+       host, _, err := net.SplitHostPort(r.Host)
+       if err != nil {
+               host = r.Host
+       }
        h.Handle(w, r, host, Hosts[host])
 }