mirror of
https://github.com/safing/portbase
synced 2025-09-01 10:09:50 +00:00
Wait for modules to be ready before calling http handlers
This commit is contained in:
parent
734c49a88a
commit
2b165c149a
5 changed files with 75 additions and 10 deletions
|
@ -161,7 +161,7 @@ func callAPI(ebr *EndpointBridgeRequest) (record.Record, error) {
|
|||
}
|
||||
|
||||
response := &EndpointBridgeResponse{
|
||||
MimeType: w.Result().Header.Get("Content-Type"),
|
||||
MimeType: w.Header().Get("Content-Type"),
|
||||
Body: w.Body.String(),
|
||||
}
|
||||
response.SetKey(apiDatabaseName + ":" + ebr.Path)
|
||||
|
|
|
@ -15,16 +15,18 @@ import (
|
|||
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
)
|
||||
|
||||
// Endpoint describes an API Endpoint.
|
||||
// Path and at least one permission are required.
|
||||
// As is exactly one function.
|
||||
type Endpoint struct {
|
||||
Path string
|
||||
MimeType string
|
||||
Read Permission
|
||||
Write Permission
|
||||
Path string
|
||||
MimeType string
|
||||
Read Permission
|
||||
Write Permission
|
||||
BelongsTo *modules.Module
|
||||
|
||||
// ActionFunc is for simple actions with a return message for the user.
|
||||
ActionFunc ActionFunc `json:"-"`
|
||||
|
@ -268,6 +270,12 @@ func (e *Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// Wait for the owning module to be ready.
|
||||
if !moduleIsReady(e.BelongsTo) {
|
||||
http.Error(w, "The API endpoint not ready yet. Please try again later.", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodHead:
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
|
48
api/modules.go
Normal file
48
api/modules.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/modules"
|
||||
)
|
||||
|
||||
type ModuleHandler interface {
|
||||
BelongsTo() *modules.Module
|
||||
}
|
||||
|
||||
const (
|
||||
moduleCheckMaxWait = 10 * time.Second
|
||||
moduleCheckTickDuration = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
// moduleIsReady checks if the given module is online and http requests can be
|
||||
// sent its way. If the module is not online already, it will wait for a short
|
||||
// duration for it to come online.
|
||||
func moduleIsReady(m *modules.Module) (ok bool) {
|
||||
// Check if we are given a module.
|
||||
if m == nil {
|
||||
// If no module is given, we assume that the handler has not been linked to
|
||||
// a module, and we can safely continue with the request.
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if the module is online.
|
||||
if m.Online() {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if the module will come online.
|
||||
if m.OnlineSoon() {
|
||||
var i time.Duration
|
||||
for i = 0; i < moduleCheckMaxWait; i += moduleCheckTickDuration {
|
||||
// Wait a little.
|
||||
time.Sleep(moduleCheckTickDuration)
|
||||
// Check if module is now online.
|
||||
if m.Online() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
|
@ -86,12 +86,12 @@ func (mh *mainHandler) handle(w http.ResponseWriter, r *http.Request) error {
|
|||
r = r.WithContext(ctx)
|
||||
lrw := NewLoggingResponseWriter(w, r)
|
||||
|
||||
tracer.Tracef("api request: %s ___ %s", r.RemoteAddr, r.RequestURI)
|
||||
tracer.Tracef("api request: %s ___ %s %s", r.RemoteAddr, lrw.Request.Method, r.RequestURI)
|
||||
defer func() {
|
||||
// Log request status.
|
||||
if lrw.Status != 0 {
|
||||
// If lrw.Status is 0, the request may have been hijacked.
|
||||
tracer.Debugf("api request: %s %d %s", lrw.Request.RemoteAddr, lrw.Status, lrw.Request.RequestURI)
|
||||
tracer.Debugf("api request: %s %d %s %s", lrw.Request.RemoteAddr, lrw.Status, lrw.Request.Method, lrw.Request.RequestURI)
|
||||
}
|
||||
tracer.Submit()
|
||||
}()
|
||||
|
@ -130,6 +130,14 @@ func (mh *mainHandler) handle(w http.ResponseWriter, r *http.Request) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Wait for the owning module to be ready.
|
||||
if moduleHandler, ok := handler.(ModuleHandler); ok {
|
||||
if !moduleIsReady(moduleHandler.BelongsTo()) {
|
||||
http.Error(lrw, "The API endpoint not ready yet. Please try again later.", http.StatusServiceUnavailable)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Add security headers.
|
||||
if !devMode() {
|
||||
w.Header().Set(
|
||||
|
|
|
@ -19,9 +19,10 @@ func registerAPI() error {
|
|||
api.RegisterHandler("/metrics", &metricsAPI{})
|
||||
|
||||
return api.RegisterEndpoint(api.Endpoint{
|
||||
Path: "metrics/list",
|
||||
Read: api.PermitAnyone,
|
||||
MimeType: api.MimeTypeJSON,
|
||||
Path: "metrics/list",
|
||||
Read: api.PermitAnyone,
|
||||
MimeType: api.MimeTypeJSON,
|
||||
BelongsTo: module,
|
||||
DataFunc: func(*api.Request) ([]byte, error) {
|
||||
registryLock.RLock()
|
||||
defer registryLock.RUnlock()
|
||||
|
|
Loading…
Add table
Reference in a new issue