added webserver

This commit is contained in:
Jan Halfar 2018-11-27 12:53:12 +01:00
parent bbded7c9db
commit 2c5492ebe4
3 changed files with 176 additions and 81 deletions

83
server/handlerequest.go Normal file
View File

@ -0,0 +1,83 @@
package server
import (
"encoding/json"
"errors"
"fmt"
"github.com/foomo/contentserver/log"
"github.com/foomo/contentserver/repo"
"github.com/foomo/contentserver/requests"
"github.com/foomo/contentserver/responses"
)
func handleRequest(r *repo.Repo, handler Handler, jsonBytes []byte) (replyBytes []byte, err error) {
var reply interface{}
var apiErr error
var jsonErr error
processIfJSONIsOk := func(err error, processingFunc func()) {
if err != nil {
jsonErr = err
return
}
processingFunc()
}
switch handler {
case HandlerGetURIs:
getURIRequest := &requests.URIs{}
processIfJSONIsOk(json.Unmarshal(jsonBytes, &getURIRequest), func() {
reply = r.GetURIs(getURIRequest.Dimension, getURIRequest.IDs)
})
case HandlerGetContent:
contentRequest := &requests.Content{}
processIfJSONIsOk(json.Unmarshal(jsonBytes, &contentRequest), func() {
reply, apiErr = r.GetContent(contentRequest)
})
case HandlerGetNodes:
nodesRequest := &requests.Nodes{}
processIfJSONIsOk(json.Unmarshal(jsonBytes, &nodesRequest), func() {
reply = r.GetNodes(nodesRequest)
})
case HandlerUpdate:
updateRequest := &requests.Update{}
processIfJSONIsOk(json.Unmarshal(jsonBytes, &updateRequest), func() {
reply = r.Update()
})
case HandlerGetRepo:
repoRequest := &requests.Repo{}
processIfJSONIsOk(json.Unmarshal(jsonBytes, &repoRequest), func() {
reply = r.GetRepo()
})
default:
err = errors.New(log.Error(" can not handle this one " + handler))
errorResponse := responses.NewError(1, "unknown handler")
reply = errorResponse
}
if jsonErr != nil {
err = jsonErr
log.Error(" could not read incoming json:", jsonErr)
errorResponse := responses.NewError(2, "could not read incoming json "+jsonErr.Error())
reply = errorResponse
} else if apiErr != nil {
log.Error(" an API error occured:", apiErr)
err = apiErr
reply = responses.NewError(3, "internal error "+apiErr.Error())
}
return encodeReply(reply)
}
func encodeReply(reply interface{}) (replyBytes []byte, err error) {
encodedBytes, jsonReplyErr := json.MarshalIndent(map[string]interface{}{
"reply": reply,
}, "", " ")
if jsonReplyErr != nil {
err = jsonReplyErr
log.Error(" could not encode reply " + fmt.Sprint(jsonReplyErr))
} else {
replyBytes = encodedBytes
}
return replyBytes, err
}

View File

@ -1,16 +1,15 @@
package server
import (
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
"strconv"
"strings"
"github.com/foomo/contentserver/log"
"github.com/foomo/contentserver/repo"
"github.com/foomo/contentserver/requests"
"github.com/foomo/contentserver/responses"
)
@ -63,77 +62,6 @@ type socketServer struct {
repo *repo.Repo
}
func (s *socketServer) handle(handler Handler, jsonBytes []byte) (replyBytes []byte, err error) {
var reply interface{}
var apiErr error
var jsonErr error
processIfJSONIsOk := func(err error, processingFunc func()) {
if err != nil {
jsonErr = err
return
}
processingFunc()
}
switch handler {
case HandlerGetURIs:
getURIRequest := &requests.URIs{}
processIfJSONIsOk(json.Unmarshal(jsonBytes, &getURIRequest), func() {
reply = s.repo.GetURIs(getURIRequest.Dimension, getURIRequest.IDs)
})
case HandlerGetContent:
contentRequest := &requests.Content{}
processIfJSONIsOk(json.Unmarshal(jsonBytes, &contentRequest), func() {
reply, apiErr = s.repo.GetContent(contentRequest)
})
case HandlerGetNodes:
nodesRequest := &requests.Nodes{}
processIfJSONIsOk(json.Unmarshal(jsonBytes, &nodesRequest), func() {
reply = s.repo.GetNodes(nodesRequest)
})
case HandlerUpdate:
updateRequest := &requests.Update{}
processIfJSONIsOk(json.Unmarshal(jsonBytes, &updateRequest), func() {
reply = s.repo.Update()
})
case HandlerGetRepo:
repoRequest := &requests.Repo{}
processIfJSONIsOk(json.Unmarshal(jsonBytes, &repoRequest), func() {
reply = s.repo.GetRepo()
})
default:
err = errors.New(log.Error(" can not handle this one " + handler))
errorResponse := responses.NewError(1, "unknown handler")
reply = errorResponse
}
if jsonErr != nil {
err = jsonErr
log.Error(" could not read incoming json:", jsonErr)
errorResponse := responses.NewError(2, "could not read incoming json "+jsonErr.Error())
reply = errorResponse
} else if apiErr != nil {
log.Error(" an API error occured:", apiErr)
err = apiErr
reply = responses.NewError(3, "internal error "+apiErr.Error())
}
return s.encodeReply(reply)
}
func (s *socketServer) encodeReply(reply interface{}) (replyBytes []byte, err error) {
encodedBytes, jsonReplyErr := json.MarshalIndent(map[string]interface{}{
"reply": reply,
}, "", " ")
if jsonReplyErr != nil {
err = jsonReplyErr
log.Error(" could not encode reply " + fmt.Sprint(jsonReplyErr))
} else {
replyBytes = encodedBytes
}
return replyBytes, err
}
func extractHandlerAndJSONLentgh(header string) (handler Handler, jsonLength int, err error) {
headerParts := strings.Split(header, ":")
if len(headerParts) != 2 {
@ -148,11 +76,11 @@ func extractHandlerAndJSONLentgh(header string) (handler Handler, jsonLength int
func (s *socketServer) execute(handler Handler, jsonBytes []byte) (reply []byte) {
s.stats.countRequest()
log.Record("socketServer.execute(%d): %s", s.stats.requests, handler)
log.Notice("socketServer.execute: ", s.stats.requests, ", ", handler)
if log.SelectedLevel == log.LevelDebug {
log.Debug(" incoming json buffer:", string(jsonBytes))
}
reply, handlingError := s.handle(handler, jsonBytes)
reply, handlingError := handleRequest(s.repo, handler, jsonBytes)
if handlingError != nil {
log.Error("socketServer.execute handlingError :", handlingError)
}
@ -180,7 +108,10 @@ func (s *socketServer) handleConnection(conn net.Conn) {
log.Debug("socketServer.handleConnection")
var headerBuffer [1]byte
header := ""
i := 0
for {
i++
// fmt.Println("---->", i)
// let us read with 1 byte steps on conn until we find "{"
_, readErr := conn.Read(headerBuffer[0:])
if readErr != nil {
@ -196,7 +127,7 @@ func (s *socketServer) handleConnection(conn net.Conn) {
header = ""
if headerErr != nil {
log.Error("invalid request could not read header", headerErr)
encodedErr, encodingErr := s.encodeReply(responses.NewError(4, "invalid header "+headerErr.Error()))
encodedErr, encodingErr := encodeReply(responses.NewError(4, "invalid header "+headerErr.Error()))
if encodingErr == nil {
s.writeResponse(conn, encodedErr)
} else {
@ -241,23 +172,64 @@ func (s *socketServer) handleConnection(conn net.Conn) {
}
// Run - let it run and enjoy on a socket near you
func Run(server string, address string, varDir string) error {
return RunServerSocketAndWebServer(server, address, "", varDir)
}
func RunServerSocketAndWebServer(
server string,
address string,
webserverAdresss string,
varDir string,
) error {
if address == "" && webserverAdresss == "" {
return errors.New("one of the addresses needs to be set")
}
log.Record("building repo with content from " + server)
r := repo.NewRepo(server, varDir)
go r.Update()
// update can run in bg
chanErr := make(chan error)
if address != "" {
go runSocketServer(r, address, chanErr)
}
if webserverAdresss != "" {
go runWebserver(r, webserverAdresss, chanErr)
}
return <-chanErr
}
func runWebserver(
r *repo.Repo,
address string,
chanErr chan error,
) {
s := &webServer{
r: r,
}
chanErr <- http.ListenAndServe(address, s)
}
func runSocketServer(
repo *repo.Repo,
address string,
chanErr chan error,
) {
s := &socketServer{
stats: newStats(),
repo: repo.NewRepo(server, varDir),
repo: repo,
}
ln, err := net.Listen("tcp", address)
if err != nil {
err = errors.New("RunSocketServer: could not start the on \"" + address + "\" - error: " + fmt.Sprint(err))
// failed to create socket
log.Error(err)
return err
chanErr <- err
return
}
// there we go
log.Record("RunSocketServer: started to listen on " + address)
// update can run in bg
go s.repo.Update()
for {
// this blocks until connection or error
conn, err := ln.Accept()
@ -265,12 +237,13 @@ func Run(server string, address string, varDir string) error {
log.Error("RunSocketServer: could not accept connection" + fmt.Sprint(err))
continue
}
log.Debug("new connection")
// a goroutine handles conn so that the loop can accept other connections
go func() {
log.Debug("accepted connection")
s.handleConnection(conn)
conn.Close()
log.Debug("connection closed")
// log.Debug("connection closed")
}()
}
}

39
server/webserver.go Normal file
View File

@ -0,0 +1,39 @@
package server
import (
"io/ioutil"
"net/http"
"strings"
"github.com/foomo/contentserver/repo"
)
const PathContentserver = "/contentserver"
type webServer struct {
r *repo.Repo
}
func newWebServer() (s *webServer, err error) {
s = &webServer{}
return
}
func (s *webServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Body == nil {
http.Error(w, "no body", http.StatusBadRequest)
return
}
jsonBytes, readErr := ioutil.ReadAll(r.Body)
r.Body.Close()
if readErr != nil {
http.Error(w, "failed to read incoming request", http.StatusBadRequest)
return
}
reply, errReply := handleRequest(s.r, Handler(strings.TrimPrefix(r.URL.Path, PathContentserver+"/")), jsonBytes)
if errReply != nil {
http.Error(w, errReply.Error(), http.StatusInternalServerError)
return
}
w.Write(reply)
}