@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.
import (
"bytes"
"compress/gzip"
+ "encoding/base64"
"errors"
"fmt"
"io/ioutil"
"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 (
}
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)
--- /dev/null
+/*
+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
+}
--- /dev/null
+/*
+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"`
+}