]> Sergey Matveev's repositories - godlighty.git/commitdiff
Full HTTP Metalink headers
authorSergey Matveev <stargrave@stargrave.org>
Mon, 20 Dec 2021 12:45:01 +0000 (15:45 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Mon, 20 Dec 2021 12:47:37 +0000 (15:47 +0300)
doc/index.texi
handler.go
meta4/parse.go [new file with mode: 0644]
meta4/scheme.go [new file with mode: 0644]

index cfb612ff2710fc187804ae1bafb2a11539280e24..750d7f3f87c25d6b241d37b5ce1e098574bc8e2c 100644 (file)
@@ -48,9 +48,10 @@ read-only @url{https://en.wikipedia.org/wiki/WebDAV, WebDAV} support.
 
 @item Per-domain HTTP basic authorization and TLS client authentication.
 
-@item If corresponding @file{.meta4} files are found, then @code{Link}
-header is generated automatically to that
-@url{http://www.metalinker.org/, Metalink}.
+@item If corresponding @file{.meta4} files are found, it is parsed and
+additional @code{Link}s with @code{Digest}s headers are generated
+automatically, based on that @url{http://www.metalinker.org/, Metalink}
+file.
 
 @item Very friendly to @url{http://cr.yp.to/daemontools.html, daemontools}.
 Can drop (UID, GID, groups) privileges.
index 52d9a1082619c6a191bb362757e2c044f874d4a8..76859813b79349fa1ad6e09f0807f11a14c2df64 100644 (file)
@@ -20,6 +20,7 @@ package godlighty
 import (
        "bytes"
        "compress/gzip"
+       "encoding/base64"
        "errors"
        "fmt"
        "io/ioutil"
@@ -35,13 +36,13 @@ import (
        "time"
 
        "github.com/klauspost/compress/zstd"
+       "go.stargrave.org/godlighty/meta4"
        "golang.org/x/net/webdav"
 )
 
 const (
-       Index    = "index.html"
-       Readme   = "README"
-       Meta4Ext = ".meta4"
+       Index  = "index.html"
+       Readme = "README"
 )
 
 var (
@@ -240,9 +241,35 @@ IndexLookup:
        }
        defer fd.Close()
 
-       if _, err = os.Stat(pth + Meta4Ext); err == nil {
-               w.Header().Set("Link", "<"+path.Base(pth)+Meta4Ext+`>; rel=describedby; type="application/metalink4+xml"`)
+       if meta4fi, err := os.Stat(pth + meta4.Ext); err == nil {
+               if meta4fi.Size() > meta4.MaxSize {
+                       goto SkipMeta4
+               }
+               meta4Raw, err := ioutil.ReadFile(pth + meta4.Ext)
+               if err != nil {
+                       goto SkipMeta4
+               }
+               base := path.Base(pth)
+               forHTTP, err := meta4.Parse(base, meta4Raw)
+               if err != nil {
+                       goto SkipMeta4
+               }
+               w.Header().Add("Link", "<"+base+meta4.Ext+
+                       `>; rel=describedby; type="application/metalink4+xml"`,
+               )
+               for _, u := range forHTTP.URLs {
+                       w.Header().Add("Link", "<"+u+">; rel=duplicate")
+               }
+               if forHTTP.SHA256 != nil {
+                       w.Header().Add("Digest", "SHA-256="+
+                               base64.StdEncoding.EncodeToString(forHTTP.SHA256))
+               }
+               if forHTTP.SHA512 != nil {
+                       w.Header().Add("Digest", "SHA-512="+
+                               base64.StdEncoding.EncodeToString(forHTTP.SHA512))
+               }
        }
+SkipMeta4:
 
        if contentType == "" {
                contentType = mediaType(path.Base(pth), cfg.MIMEs)
diff --git a/meta4/parse.go b/meta4/parse.go
new file mode 100644 (file)
index 0000000..92ff37f
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+godlighty -- highly-customizable HTTP, HTTP/2, HTTPS server
+Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 3 of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package meta4
+
+import (
+       "encoding/hex"
+       "encoding/xml"
+)
+
+type ForHTTP struct {
+       SHA256 []byte
+       SHA512 []byte
+       URLs   []string
+}
+
+func Parse(fn string, data []byte) (*ForHTTP, error) {
+       var meta Metalink
+       err := xml.Unmarshal(data, &meta)
+       if err != nil {
+               return nil, err
+       }
+       for _, f := range meta.Files {
+               if f.Name != fn {
+                       continue
+               }
+               forHTTP := ForHTTP{}
+               for _, h := range f.Hashes {
+                       digest, err := hex.DecodeString(h.Hash)
+                       if err != nil {
+                               return nil, err
+                       }
+                       switch h.Type {
+                       case HashSHA256:
+                               forHTTP.SHA256 = digest
+                       case HashSHA512:
+                               forHTTP.SHA512 = digest
+                       }
+               }
+               for _, u := range f.URLs {
+                       forHTTP.URLs = append(forHTTP.URLs, u.URL)
+               }
+               return &forHTTP, nil
+       }
+       return nil, nil
+}
diff --git a/meta4/scheme.go b/meta4/scheme.go
new file mode 100644 (file)
index 0000000..da46e24
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+godlighty -- highly-customizable HTTP, HTTP/2, HTTPS server
+Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 3 of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package meta4
+
+import (
+       "encoding/xml"
+       "time"
+)
+
+const (
+       HashSHA256 = "sha-256"
+       HashSHA512 = "sha-512"
+       Ext        = ".meta4"
+       MaxSize    = 1 << 16
+)
+
+type Metalink struct {
+       XMLName   xml.Name  `xml:"urn:ietf:params:xml:ns:metalink metalink"`
+       Files     []File    `xml:"file"`
+       Generator string    `xml:"generator,,omitempty"`
+       Published time.Time `xml:"published,,omitempty"`
+}
+
+type File struct {
+       XMLName     xml.Name   `xml:"file"`
+       Name        string     `xml:"name,attr"`
+       Description string     `xml:"description,,omitempty"`
+       Hashes      []Hash     `xml:"hash,,omitempty"`
+       MetaURLs    []MetaURL  `xml:"metaurl,,omitempty"`
+       Signature   *Signature `xml:"signature"`
+       Size        uint64     `xml:"size,,omitempty"`
+       URLs        []URL      `xml:"url,,omitempty"`
+}
+
+type URL struct {
+       XMLName xml.Name `xml:"url"`
+       URL     string   `xml:",chardata"`
+}
+
+type Signature struct {
+       XMLName   xml.Name `xml:"signature"`
+       MediaType string   `xml:"mediatype,attr"`
+       Signature string   `xml:",cdata"`
+}
+
+type Hash struct {
+       XMLName xml.Name `xml:"hash"`
+       Type    string   `xml:"type,attr"`
+       Hash    string   `xml:",chardata"`
+}
+
+type MetaURL struct {
+       XMLName   xml.Name `xml:"metaurl"`
+       MediaType string   `xml:"mediatype,attr"`
+       URL       string   `xml:",chardata"`
+}