edge/pkg/http/handler.go

107 lines
2.2 KiB
Go

package http
import (
"io/ioutil"
"net/http"
"strings"
"sync"
"forge.cadoles.com/arcad/edge/pkg/app"
"forge.cadoles.com/arcad/edge/pkg/bundle"
"forge.cadoles.com/arcad/edge/pkg/bus"
"forge.cadoles.com/arcad/edge/pkg/sdk"
"github.com/igm/sockjs-go/v3/sockjs"
"github.com/pkg/errors"
)
const (
sockJSPathPrefix = "/sock"
clientJSPath = "/client.js"
backendMainScript = "backend/main.js"
)
type Handler struct {
bundle bundle.Bundle
public http.Handler
sockjs http.Handler
bus bus.Bus
sockjsOpts sockjs.Options
backend *app.Backend
backendModuleFactories []app.BackendModuleFactory
mutex sync.RWMutex
}
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.mutex.RLock()
defer h.mutex.RUnlock()
switch {
case r.URL.Path == clientJSPath:
serveFile(w, r, &sdk.FS, "client/dist/client.js")
case r.URL.Path == clientJSPath+".map":
serveFile(w, r, &sdk.FS, "client/dist/client.js.map")
case strings.HasPrefix(r.URL.Path, sockJSPathPrefix):
h.sockjs.ServeHTTP(w, r)
default:
h.public.ServeHTTP(w, r)
}
}
func (h *Handler) Load(bdle bundle.Bundle) error {
h.mutex.Lock()
defer h.mutex.Unlock()
file, _, err := bdle.File(backendMainScript)
if err != nil {
return errors.Wrap(err, "could not open backend main script")
}
mainScript, err := ioutil.ReadAll(file)
if err != nil {
return errors.Wrap(err, "could not read backend main script")
}
backend := app.NewBackend(h.backendModuleFactories...)
if err := backend.Load(backendMainScript, string(mainScript)); err != nil {
return errors.WithStack(err)
}
fs := bundle.NewFileSystem("public", bdle)
public := http.FileServer(fs)
sockjs := sockjs.NewHandler(sockJSPathPrefix, h.sockjsOpts, h.handleSockJSSession)
if h.backend != nil {
h.backend.Stop()
}
if err := backend.Start(); err != nil {
return errors.WithStack(err)
}
h.bundle = bdle
h.backend = backend
h.public = public
h.sockjs = sockjs
return nil
}
func NewHandler(funcs ...HandlerOptionFunc) *Handler {
opts := defaultHandlerOptions()
for _, fn := range funcs {
fn(opts)
}
handler := &Handler{
sockjsOpts: opts.SockJS,
backendModuleFactories: opts.BackendModuleFactories,
bus: opts.Bus,
}
return handler
}