• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

go-spatial / tegola / fa61eff93-PR-1040

19 Mar 2025 08:01AM UTC coverage: 40.442% (-0.3%) from 40.721%
fa61eff93-PR-1040

Pull #1040

github

iwpnd
chore: vendor
Pull Request #1040: refactor: drop zap and stdlogger in favour of slog

21 of 110 new or added lines in 6 files covered. (19.09%)

21 existing lines in 4 files now uncovered.

6588 of 16290 relevant lines covered (40.44%)

216.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.37
/server/server.go
1
// Package server implements the http frontend
2
package server
3

4
import (
5
        "net/http"
6
        "net/url"
7
        "os"
8

9
        "github.com/dimfeld/httptreemux"
10

11
        "github.com/go-spatial/tegola/atlas"
12
        "github.com/go-spatial/tegola/internal/build"
13
        "github.com/go-spatial/tegola/internal/log"
14
        "github.com/go-spatial/tegola/observability"
15
)
16

17
const (
18
        // MaxTileSize is 500k. Currently, just throws a warning when tile
19
        // is larger than MaxTileSize
20
        MaxTileSize = 500000
21

22
        // QueryKeyDebug is a common query string key used throughout the pacakge
23
        // the value should always be a boolean
24
        QueryKeyDebug = "debug"
25
)
26

27
var (
28
        // Version is the version of the software, this should be set by the main program, before starting up.
29
        // It is used by various Middleware to determine the version.
30
        Version string = "version not set"
31

32
        // HostName is the name of the host to use for construction of URLS.
33
        // configurable via the tegola config.toml file (set in main.go)
34
        HostName *url.URL
35

36
        // Port is the port the server is listening on, used for construction of URLS.
37
        // configurable via the tegola config.toml file (set in main.go)
38
        Port string
39

40
        // SSLCert is a filepath to an SSL cert, this will be used to enable https
41
        SSLCert string
42

43
        // SSLKey is a filepath to an SSL key, this will be used to enable https
44
        SSLKey string
45

46
        // Headers is the map of user defined response headers.
47
        // configurable via the tegola config.toml file (set in main.go)
48
        Headers = map[string]string{}
49

50
        // URIPrefix sets a prefix on all server endpoints. This is often used
51
        // when the server sits behind a reverse proxy with a prefix (i.e. /tegola)
52
        URIPrefix = "/"
53

54
        // ProxyProtocol is a custom protocol that will be used to generate the URLs
55
        // included in the capabilities endpoint responses. This is useful when he
56
        // server sits behind a reverse proxy
57
        // (See https://github.com/go-spatial/tegola/pull/967)
58
        ProxyProtocol string
59

60
        // DefaultCORSHeaders define the default CORS response headers added to all requests
61
        DefaultCORSHeaders = map[string]string{
62
                "Access-Control-Allow-Origin":  "*",
63
                "Access-Control-Allow-Methods": "GET, OPTIONS",
64
        }
65
)
66

67
// NewRouter set's up our routes.
68
func NewRouter(a *atlas.Atlas) *httptreemux.TreeMux {
57✔
69
        o := a.Observer()
57✔
70
        r := httptreemux.New()
57✔
71
        group := r.NewGroup(URIPrefix)
57✔
72

57✔
73
        // one handler to respond to all OPTIONS requests for registered routes with our CORS headers
57✔
74
        r.OptionsHandler = corsHandler
57✔
75

57✔
76
        if o != nil && o != observability.NullObserver {
57✔
77
                const (
×
78
                        metricsRoute = "/metrics"
×
79
                )
×
80
                if h := o.Handler(metricsRoute); h != nil {
×
81
                        // Only set up the /metrics endpoint if we have a configured observer
×
82
                        log.Infof("setting up observer: %v", o.Name())
×
83
                        group.UsingContext().Handler(http.MethodGet, metricsRoute, h)
×
84
                }
×
85
        }
86

87
        // capabilities endpoints
88
        group.UsingContext().
57✔
89
                Handler(observability.InstrumentAPIHandler(http.MethodGet, "/capabilities", o, HeadersHandler(HandleCapabilities{})))
57✔
90
        group.UsingContext().
57✔
91
                Handler(observability.InstrumentAPIHandler(http.MethodGet, "/capabilities/:map_name", o, HeadersHandler(HandleMapCapabilities{})))
57✔
92

57✔
93
        // map tiles
57✔
94
        hMapLayerZXY := HandleMapLayerZXY{Atlas: a}
57✔
95
        group.UsingContext().
57✔
96
                Handler(observability.InstrumentAPIHandler(http.MethodGet, "/maps/:map_name/:z/:x/:y", o, HeadersHandler(GZipHandler(TileCacheHandler(a, hMapLayerZXY)))))
57✔
97
        group.UsingContext().
57✔
98
                Handler(observability.InstrumentAPIHandler(http.MethodGet, "/maps/:map_name/:layer_name/:z/:x/:y", o, HeadersHandler(GZipHandler(TileCacheHandler(a, hMapLayerZXY)))))
57✔
99

57✔
100
        // map style
57✔
101
        group.UsingContext().
57✔
102
                Handler(observability.InstrumentAPIHandler(http.MethodGet, "/maps/:map_name/style.json", o, HeadersHandler(HandleMapStyle{})))
57✔
103

57✔
104
        // setup viewer routes, which can be excluded via build flags
57✔
105
        setupViewer(o, group)
57✔
106

57✔
107
        return r
57✔
108
}
109

110
// Start starts the tile server binding to the provided port
111
func Start(a *atlas.Atlas, port string) *http.Server {
1✔
112
        // notify the user the server is starting
1✔
113
        log.Infof("starting tegola server (%v) on port %v", build.Version, port)
1✔
114

1✔
115
        srv := &http.Server{Addr: port, Handler: NewRouter(a)}
1✔
116

1✔
117
        // start our server
1✔
118
        go func() {
2✔
119
                var err error
1✔
120

1✔
121
                if SSLCert+SSLKey != "" {
2✔
122
                        err = srv.ListenAndServeTLS(SSLCert, SSLKey)
1✔
123
                } else {
1✔
124
                        err = srv.ListenAndServe()
×
125
                }
×
126

127
                switch err {
1✔
128
                case nil:
×
129
                        // noop
×
130
                        return
×
131
                case http.ErrServerClosed:
1✔
132
                        log.Info("http server closed")
1✔
133
                        return
1✔
134
                default:
×
NEW
135
                        log.Error(err)
×
NEW
136
                        os.Exit(1)
×
UNCOV
137
                        return
×
138
                }
139
        }()
140

141
        return srv
1✔
142
}
143

144
// hostName determines whether to use an user defined HostName
145
// or the host from the incoming request
146
func hostName(r *http.Request) *url.URL {
54✔
147
        // if the HostName has been configured, don't mutate it
54✔
148
        if HostName != nil {
94✔
149
                return HostName
40✔
150
        }
40✔
151

152
        // favor the r.URL.Host attribute in case tegola is behind a proxy
153
        // https://stackoverflow.com/questions/42921567/what-is-the-difference-between-host-and-url-host-for-golang-http-request
154
        if r.URL != nil && r.URL.Host != "" {
27✔
155
                return r.URL
13✔
156
        }
13✔
157

158
        return &url.URL{
1✔
159
                Host: r.Host,
1✔
160
        }
1✔
161
}
162

163
const (
164
        HeaderXForwardedProto = "X-Forwarded-Proto"
165
)
166

167
// various checks to determine if the request is http or https. the scheme is needed for the TileURLs
168
// r.URL.Scheme can be empty if a relative request is issued from the client. (i.e. GET /foo.html)
169
func scheme(r *http.Request) string {
57✔
170
        if ProxyProtocol != "" {
62✔
171
                return ProxyProtocol
5✔
172
        }
5✔
173

174
        if r.Header.Get(HeaderXForwardedProto) != "" {
54✔
175
                return r.Header.Get(HeaderXForwardedProto)
2✔
176
        }
2✔
177

178
        if r.TLS != nil {
57✔
179
                return "https"
7✔
180
        }
7✔
181

182
        return "http"
43✔
183
}
184

185
// URLRoot builds a string containing the scheme, host and port based on a combination of user defined values,
186
// headers and request parameters. The function is public so it can be overridden for other implementations.
187
var URLRoot = func(r *http.Request) *url.URL {
2✔
188
        return &url.URL{
2✔
189
                Scheme: scheme(r),
2✔
190
                Host:   hostName(r).Host,
2✔
191
        }
2✔
192
}
2✔
193

194
// corsHandler is used to respond to all OPTIONS requests for registered routes
195
func corsHandler(w http.ResponseWriter, _ *http.Request, _ map[string]string) {
10✔
196
        setHeaders(w)
10✔
197
}
10✔
198

199
// setHeaders sets default headers and user defined headers
200
func setHeaders(w http.ResponseWriter) {
62✔
201
        // add our default CORS headers
62✔
202
        for name, val := range DefaultCORSHeaders {
186✔
203
                if val == "" {
124✔
204
                        log.Warnf("default CORS header (%s) has no value", name)
×
205
                }
×
206

207
                w.Header().Set(name, val)
124✔
208
        }
209

210
        // set user defined headers
211
        for name, val := range Headers {
68✔
212
                if val == "" {
6✔
213
                        log.Warnf("header (%s) has no value", name)
×
214
                }
×
215

216
                w.Header().Set(name, val)
6✔
217
        }
218
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc