Skip to content

Commit

Permalink
Initial implementation of Caddy module (#84)
Browse files Browse the repository at this point in the history
* only supports pmtiles in top level of bucket, mounted at the root route
* does not have any custom credentials logic
  • Loading branch information
bdon authored Sep 24, 2023
1 parent ad2801a commit 1f4c412
Showing 1 changed file with 109 additions and 0 deletions.
109 changes: 109 additions & 0 deletions caddy/pmtiles_proxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package pmtiles_proxy

import (
"fmt"
"io"
"log"
"net/http"
"strconv"

"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/protomaps/go-pmtiles/pmtiles"
"go.uber.org/zap"
"time"
)

func init() {
caddy.RegisterModule(Middleware{})
httpcaddyfile.RegisterHandlerDirective("pmtiles_proxy", parseCaddyfile)
}

type Middleware struct {
Bucket string `json:"bucket"`
CacheSize int `json:"cache_size"`
logger *zap.Logger
server *pmtiles.Server
}

func (Middleware) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.handlers.pmtiles_proxy",
New: func() caddy.Module { return new(Middleware) },
}
}

func (m *Middleware) Provision(ctx caddy.Context) error {
m.logger = ctx.Logger()
logger := log.New(io.Discard, "", log.Ldate)
prefix := "." // serve only the root of the bucket for now, at the root route of Caddyfile
server, err := pmtiles.NewServer(m.Bucket, prefix, logger, 64, "", "https://example.com")
if err != nil {
return err
}
m.server = server
server.Start()
return nil
}

func (m *Middleware) Validate() error {
if m.Bucket == "" {
return fmt.Errorf("no bucket")
}
if m.CacheSize <= 0 {
m.CacheSize = 64
}
return nil
}

func (m Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
start := time.Now()
status_code, headers, body := m.server.Get(r.Context(), r.URL.Path)
for k, v := range headers {
w.Header().Set(k, v)
}
w.WriteHeader(status_code)
w.Write(body)
m.logger.Info("response", zap.Int("status", status_code), zap.String("path", r.URL.Path), zap.Duration("duration", time.Since(start)))

return next.ServeHTTP(w, r)
}

func (m *Middleware) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "bucket":
if !d.Args(&m.Bucket) {
return d.ArgErr()
}
case "cache_size":
var cacheSize string
if !d.Args(&cacheSize) {
return d.ArgErr()
}
num, err := strconv.Atoi(cacheSize)
if err != nil {
return d.ArgErr()
}
m.CacheSize = num
}
}
}
return nil
}

func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
var m Middleware
err := m.UnmarshalCaddyfile(h.Dispenser)
return m, err
}

var (
_ caddy.Provisioner = (*Middleware)(nil)
_ caddy.Validator = (*Middleware)(nil)
_ caddyhttp.MiddlewareHandler = (*Middleware)(nil)
_ caddyfile.Unmarshaler = (*Middleware)(nil)
)

0 comments on commit 1f4c412

Please sign in to comment.