ENH: Add support for returning missing images as HTTP 404 instead of blank PNGs (#177)

This commit is contained in:
Brendan Ward 2024-01-17 09:51:41 -08:00 committed by GitHub
parent 8403335a48
commit 04d08f1258
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 91 additions and 59 deletions

View File

@ -2,6 +2,10 @@
## 0.11.0 (in development)
- support returning missing image tiles as HTTP 404 instead of blank tiles using
the `--missing-image-tile-404` option (#177).
## 0.10.0
- supports GCC11 on Ubuntu 22.04 (#166)

View File

@ -53,32 +53,36 @@ From within the repository root ($GOPATH/bin needs to be in your $PATH):
```
$ mbtileserver --help
Serve tiles from mbtiles files.
Serve tiles from mbtiles files
Usage:
mbtileserver [flags]
Flags:
-c, --cert string X.509 TLS certificate filename. If present, will be used to enable SSL on the server.
-d, --dir string Directory containing mbtiles files. Directory containing mbtiles files. Can be a comma-delimited list of directories. (default "./tilesets")
--disable-preview Disable map preview for each tileset (enabled by default)
--disable-svc-list Disable services list endpoint (enabled by default)
--disable-tilejson Disable TileJSON endpoint for each tileset (enabled by default)
--domain string Domain name of this server. NOTE: only used for AutoTLS.
--dsn string Sentry DSN
--enable-arcgis Enable ArcGIS Mapserver endpoints
--enable-fs-watch Enable reloading of tilesets by watching filesystem
--enable-reload-signal Enable graceful reload using HUP signal to the server process
--generate-ids Automatically generate tileset IDs instead of using relative path
-h, --help help for mbtileserver
-k, --key string TLS private key
-p, --port int Server port. Default is 443 if --cert or --tls options are used, otherwise 8000. (default -1)
-r, --redirect Redirect HTTP to HTTPS
--root-url string Root URL of services endpoint (default "/services")
-s, --secret-key string Shared secret key used for HMAC request authentication
--tiles-only Only enable tile endpoints (shortcut for --disable-svc-list --disable-tilejson --disable-preview)
-t, --tls Auto TLS via Let's Encrypt
-v, --verbose Verbose logging
--basemap-style-url string Basemap style URL for preview endpoint (can include authorization token parameter if required by host)
--basemap-tiles-url string Basemap raster tiles URL pattern for preview endpoint (can include authorization token parameter if required by host): https://some.host/{z}/{x}/{y}.png
-c, --cert string X.509 TLS certificate filename. If present, will be used to enable SSL on the server.
-d, --dir string Directory containing mbtiles files. Can be a comma-delimited list of directories. (default "./tilesets")
--disable-preview Disable map preview for each tileset (enabled by default)
--disable-svc-list Disable services list endpoint (enabled by default)
--disable-tilejson Disable TileJSON endpoint for each tileset (enabled by default)
--domain string Domain name of this server. NOTE: only used for AutoTLS.
--dsn string Sentry DSN
--enable-arcgis Enable ArcGIS Mapserver endpoints
--enable-fs-watch Enable reloading of tilesets by watching filesystem
--enable-reload-signal Enable graceful reload using HUP signal to the server process
--generate-ids Automatically generate tileset IDs instead of using relative path
-h, --help help for mbtileserver
--host string IP address to listen on. Default is all interfaces. (default "0.0.0.0")
-k, --key string TLS private key
--missing-image-tile-404 Return HTTP 404 error code when image tile is misssing instead of default behavior to return blank PNG
-p, --port int Server port. Default is 443 if --cert or --tls options are used, otherwise 8000. (default -1)
-r, --redirect Redirect HTTP to HTTPS
--root-url string Root URL of services endpoint (default "/services")
-s, --secret-key string Shared secret key used for HMAC request authentication
--tiles-only Only enable tile endpoints (shortcut for --disable-svc-list --disable-tilejson --disable-preview)
-t, --tls Auto TLS via Let's Encrypt
-v, --verbose Verbose logging
```
So hosting tiles is as easy as putting your mbtiles files in the `tilesets`
@ -306,6 +310,18 @@ These are provided at:
where `<format>` is one of `png`, `jpg`, `webp`, `pbf` depending on the type of data in the tileset.
### Missing tiles
Missing vector tiles are always returned as HTTP 204.
Missing image tiles are returned as blank PNGs with the same dimensions as the tileset to give seamless display of
these tiles in interactive maps.
When serving image tiles that encode data (e.g., terrain) instead of purely for display, this can cause issues. In
this case, you can use the `--missing-image-tile-404` option. This behavior will be applied to all image tilesets.
## TileJSON API
`mbtileserver` automatically creates a TileJSON endpoint for each service at `/services/<tileset_id>`.

View File

@ -13,14 +13,15 @@ import (
// ServiceSetConfig provides configuration options for a ServiceSet
type ServiceSetConfig struct {
EnableServiceList bool
EnableTileJSON bool
EnablePreview bool
EnableArcGIS bool
BasemapStyleURL string
BasemapTilesURL string
RootURL *url.URL
ErrorWriter io.Writer
EnableServiceList bool
EnableTileJSON bool
EnablePreview bool
EnableArcGIS bool
BasemapStyleURL string
BasemapTilesURL string
ReturnMissingImageTile404 bool
RootURL *url.URL
ErrorWriter io.Writer
}
// ServiceSet is a group of tilesets plus configuration options.
@ -28,12 +29,13 @@ type ServiceSetConfig struct {
type ServiceSet struct {
tilesets map[string]*Tileset
enableServiceList bool
enableTileJSON bool
enablePreview bool
enableArcGIS bool
basemapStyleURL string
basemapTilesURL string
enableServiceList bool
enableTileJSON bool
enablePreview bool
enableArcGIS bool
basemapStyleURL string
basemapTilesURL string
returnMissingImageTile404 bool
rootURL *url.URL
errorWriter io.Writer
@ -48,15 +50,16 @@ func New(cfg *ServiceSetConfig) (*ServiceSet, error) {
}
s := &ServiceSet{
tilesets: make(map[string]*Tileset),
enableServiceList: cfg.EnableServiceList,
enableTileJSON: cfg.EnableTileJSON,
enablePreview: cfg.EnablePreview,
enableArcGIS: cfg.EnableArcGIS,
basemapStyleURL: cfg.BasemapStyleURL,
basemapTilesURL: cfg.BasemapTilesURL,
rootURL: cfg.RootURL,
errorWriter: cfg.ErrorWriter,
tilesets: make(map[string]*Tileset),
enableServiceList: cfg.EnableServiceList,
enableTileJSON: cfg.EnableTileJSON,
enablePreview: cfg.EnablePreview,
enableArcGIS: cfg.EnableArcGIS,
basemapStyleURL: cfg.BasemapStyleURL,
basemapTilesURL: cfg.BasemapTilesURL,
returnMissingImageTile404: cfg.ReturnMissingImageTile404,
rootURL: cfg.RootURL,
errorWriter: cfg.ErrorWriter,
}
return s, nil

View File

@ -219,7 +219,7 @@ func (ts *Tileset) tileHandler(w http.ResponseWriter, r *http.Request) {
if ts == nil || !ts.published {
// In order to not break any requests from when this tileset was published
// return the appropriate not found handler for the original tile format.
tileNotFoundHandler(w, r, ts.tileformat, ts.tilesize)
tileNotFoundHandler(w, r, ts.tileformat, ts.tilesize, ts.svc.returnMissingImageTile404)
return
}
@ -255,7 +255,7 @@ func (ts *Tileset) tileHandler(w http.ResponseWriter, r *http.Request) {
return
}
if data == nil || len(data) <= 1 {
tileNotFoundHandler(w, r, ts.tileformat, ts.tilesize)
tileNotFoundHandler(w, r, ts.tileformat, ts.tilesize, ts.svc.returnMissingImageTile404)
return
}
@ -327,13 +327,18 @@ func (ts *Tileset) previewHandler(w http.ResponseWriter, r *http.Request) {
// tileNotFoundHandler is an http.HandlerFunc that writes the default response
// for a non-existing tile of type f to w
func tileNotFoundHandler(w http.ResponseWriter, r *http.Request, f mbtiles.TileFormat, tilesize uint32) {
func tileNotFoundHandler(w http.ResponseWriter, r *http.Request, f mbtiles.TileFormat, tilesize uint32, returnMissingImageTile404 bool) {
switch f {
case mbtiles.PNG, mbtiles.JPG, mbtiles.WEBP:
// Return blank PNG for all image types
w.Header().Set("Content-Type", "image/png")
w.WriteHeader(http.StatusOK)
w.Write(BlankPNG(tilesize))
if returnMissingImageTile404 {
// Return 404
w.WriteHeader(http.StatusNotFound)
} else {
// Return blank PNG for all image types
w.Header().Set("Content-Type", "image/png")
w.WriteHeader(http.StatusOK)
w.Write(BlankPNG(tilesize))
}
case mbtiles.PBF:
// Return 204
w.WriteHeader(http.StatusNoContent)

20
main.go
View File

@ -82,6 +82,7 @@ var (
tilesOnly bool
basemapStyleURL string
basemapTilesURL string
missingImageTile404 bool
)
func init() {
@ -112,6 +113,8 @@ func init() {
flags.StringVar(&basemapStyleURL, "basemap-style-url", "", "Basemap style URL for preview endpoint (can include authorization token parameter if required by host)")
flags.StringVar(&basemapTilesURL, "basemap-tiles-url", "", "Basemap raster tiles URL pattern for preview endpoint (can include authorization token parameter if required by host): https://some.host/{z}/{x}/{y}.png")
flags.BoolVarP(&missingImageTile404, "missing-image-tile-404", "", false, "Return HTTP 404 error code when image tile is misssing instead of default behavior to return blank PNG")
flags.BoolVarP(&verbose, "verbose", "v", false, "Verbose logging")
if env := os.Getenv("HOST"); env != "" {
@ -292,14 +295,15 @@ func serve() {
}
svcSet, err := handlers.New(&handlers.ServiceSetConfig{
RootURL: rootURL,
ErrorWriter: &errorLogger{log: log.New()},
EnableServiceList: !disableServiceList,
EnableTileJSON: !disableTileJSON,
EnablePreview: !disablePreview,
EnableArcGIS: enableArcGIS,
BasemapStyleURL: basemapStyleURL,
BasemapTilesURL: basemapTilesURL,
RootURL: rootURL,
ErrorWriter: &errorLogger{log: log.New()},
EnableServiceList: !disableServiceList,
EnableTileJSON: !disableTileJSON,
EnablePreview: !disablePreview,
EnableArcGIS: enableArcGIS,
BasemapStyleURL: basemapStyleURL,
BasemapTilesURL: basemapTilesURL,
ReturnMissingImageTile404: missingImageTile404,
})
if err != nil {
log.Fatalln("Could not construct ServiceSet")