contentserver/server/server.go
2022-05-26 15:28:22 +02:00

143 lines
3.5 KiB
Go

package server
import (
"errors"
"fmt"
"net"
"net/http"
"os"
"time"
. "github.com/foomo/contentserver/logger"
"github.com/foomo/contentserver/repo"
jsoniter "github.com/json-iterator/go"
"go.uber.org/zap"
)
var (
json = jsoniter.ConfigCompatibleWithStandardLibrary
)
// Handler type
type Handler string
const (
// HandlerGetURIs get uris, many at once, to keep it fast
HandlerGetURIs Handler = "getURIs"
// HandlerGetContent get (site) content
HandlerGetContent = "getContent"
// HandlerGetNodes get nodes
HandlerGetNodes = "getNodes"
// HandlerUpdate update repo
HandlerUpdate = "update"
// HandlerGetRepo get the whole repo
HandlerGetRepo = "getRepo"
// DefaultRepositoryTimeout for the HTTP client towards the repo
DefaultRepositoryTimeout = 2 * time.Minute
)
// Run - let it run and enjoy on a socket near you
func Run(server string, address string, varDir string, pollUpdates bool) error {
return RunServerSocketAndWebServer(server, address, "", "", varDir, DefaultRepositoryTimeout, pollUpdates)
}
func RunServerSocketAndWebServer(
server string,
address string,
webserverAddress string,
webserverPath string,
varDir string,
repositoryTimeout time.Duration,
pollForUpdates bool,
) error {
if address == "" && webserverAddress == "" {
return errors.New("one of the addresses needs to be set")
}
Log.Info("building repo with content", zap.String("server", server))
r := repo.NewRepo(server, varDir, repositoryTimeout, pollForUpdates)
// start initial update and handle error
if !pollForUpdates {
go func() {
resp := r.Update()
if !resp.Success {
Log.Error("failed to update",
zap.String("error", resp.ErrorMessage),
zap.Int("NumberOfNodes", resp.Stats.NumberOfNodes),
zap.Int("NumberOfURIs", resp.Stats.NumberOfURIs),
zap.Float64("OwnRuntime", resp.Stats.OwnRuntime),
zap.Float64("RepoRuntime", resp.Stats.RepoRuntime),
)
//Exit only if it hasn't recovered
if !r.Recovered() {
os.Exit(1)
}
}
}()
}
// update can run in bg
chanErr := make(chan error)
if address != "" {
Log.Info("starting socketserver", zap.String("address", address))
go runSocketServer(r, address, chanErr)
}
if webserverAddress != "" {
Log.Info("starting webserver", zap.String("webserverAddress", webserverAddress))
go runWebserver(r, webserverAddress, webserverPath, chanErr)
}
return <-chanErr
}
func runWebserver(
r *repo.Repo,
address string,
path string,
chanErr chan error,
) {
chanErr <- http.ListenAndServe(address, NewWebServer(path, r))
}
func runSocketServer(
repo *repo.Repo,
address string,
chanErr chan error,
) {
// create socket server
s := newSocketServer(repo)
// listen on socket
ln, errListen := net.Listen("tcp", address)
if errListen != nil {
Log.Error("runSocketServer: could not start",
zap.String("address", address),
zap.Error(errListen),
)
chanErr <- errors.New("runSocketServer: could not start the on \"" + address + "\" - error: " + fmt.Sprint(errListen))
return
}
Log.Info("runSocketServer: started listening", zap.String("address", address))
for {
// this blocks until connection or error
conn, err := ln.Accept()
if err != nil {
Log.Error("runSocketServer: could not accept connection", zap.Error(err))
continue
}
// a goroutine handles conn so that the loop can accept other connections
go func() {
Log.Debug("accepted connection", zap.String("source", conn.RemoteAddr().String()))
s.handleConnection(conn)
conn.Close()
// log.Debug("connection closed")
}()
}
}