mirror of
https://github.com/foomo/contentserver.git
synced 2025-10-16 12:25:44 +00:00
made webserver path configurable
This commit is contained in:
parent
37147120e2
commit
f96a5669a8
@ -15,7 +15,7 @@ import (
|
||||
"github.com/foomo/contentserver/server"
|
||||
)
|
||||
|
||||
var testServerIsRunning = false
|
||||
const pathContentserver = "/contentserver"
|
||||
|
||||
func dump(t *testing.T, v interface{}) {
|
||||
jsonBytes, err := json.MarshalIndent(v, "", " ")
|
||||
@ -51,7 +51,13 @@ func initTestServer(t testing.TB) (socketAddr, webserverAddr string) {
|
||||
webserverAddr = getAvailableAddr()
|
||||
testServer, varDir := mock.GetMockData(t)
|
||||
log.SelectedLevel = log.LevelError
|
||||
go server.RunServerSocketAndWebServer(testServer.URL+"/repo-two-dimensions.json", socketAddr, webserverAddr, varDir)
|
||||
go server.RunServerSocketAndWebServer(
|
||||
testServer.URL+"/repo-two-dimensions.json",
|
||||
socketAddr,
|
||||
webserverAddr,
|
||||
pathContentserver,
|
||||
varDir,
|
||||
)
|
||||
socketClient, errClient := NewClient(socketAddr, 1, time.Duration(time.Millisecond*100))
|
||||
if errClient != nil {
|
||||
panic(errClient)
|
||||
@ -80,12 +86,12 @@ func getTestClients(t testing.TB) (socketClient *Client, httpClient *Client) {
|
||||
testServerSocketAddr = socketAddr
|
||||
testServerWebserverAddr = webserverAddr
|
||||
}
|
||||
socketClient, errClient := NewClient(testServerSocketAddr, 30, time.Duration(time.Millisecond*100))
|
||||
socketClient, errClient := NewClient(testServerSocketAddr, 25, time.Duration(time.Millisecond*100))
|
||||
if errClient != nil {
|
||||
t.Log(errClient)
|
||||
t.Fail()
|
||||
}
|
||||
httpClient, errHTTPClient := NewHTTPClient("http://" + testServerWebserverAddr + server.PathContentserver)
|
||||
httpClient, errHTTPClient := NewHTTPClient("http://" + testServerWebserverAddr + pathContentserver)
|
||||
if errHTTPClient != nil {
|
||||
t.Log(errHTTPClient)
|
||||
t.Fail()
|
||||
|
||||
@ -21,7 +21,9 @@ const (
|
||||
var (
|
||||
uniqushPushVersion = "content-server 1.4.0"
|
||||
showVersionFlag = flag.Bool("version", false, "version info")
|
||||
address = flag.String("address", "127.0.0.1:8081", "address to bind host:port")
|
||||
address = flag.String("address", "127.0.0.1:8081", "address to bind socket server host:port")
|
||||
webserverAddress = flag.String("webserver-address", "", "address to bind web server host:port, when empty no webserver will be spawned")
|
||||
webserverPath = flag.String("webserver-path", "/contentserver", "path to export the webserver on - useful when behind a proxy")
|
||||
varDir = flag.String("var-dir", "/var/lib/contentserver", "where to put my data")
|
||||
logLevelOptions = []string{
|
||||
logLevelError,
|
||||
@ -41,7 +43,7 @@ var (
|
||||
)
|
||||
|
||||
func exitUsage(code int) {
|
||||
fmt.Printf("Usage: %s http(s)://your-content-server/path/to/content.json\n", os.Args[0])
|
||||
fmt.Println("Usage:", os.Args[0], "http(s)://your-content-server/path/to/content.json")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(code)
|
||||
}
|
||||
@ -68,7 +70,7 @@ func main() {
|
||||
level = log.LevelDebug
|
||||
}
|
||||
log.SelectedLevel = level
|
||||
err := server.Run(flag.Arg(0), *address, *varDir)
|
||||
err := server.RunServerSocketAndWebServer(flag.Arg(0), *address, *webserverAddress, *webserverPath, *varDir)
|
||||
if err != nil {
|
||||
fmt.Println("exiting with error", err)
|
||||
os.Exit(1)
|
||||
|
||||
158
server/server.go
158
server/server.go
@ -5,12 +5,9 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/foomo/contentserver/log"
|
||||
"github.com/foomo/contentserver/repo"
|
||||
"github.com/foomo/contentserver/responses"
|
||||
)
|
||||
|
||||
// Handler type
|
||||
@ -29,158 +26,16 @@ const (
|
||||
HandlerGetRepo = "getRepo"
|
||||
)
|
||||
|
||||
// simple internal request counter
|
||||
type stats struct {
|
||||
requests int64
|
||||
chanCount chan int
|
||||
}
|
||||
|
||||
func newStats() *stats {
|
||||
s := &stats{
|
||||
requests: 0,
|
||||
chanCount: make(chan int),
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-s.chanCount:
|
||||
s.requests++
|
||||
s.chanCount <- 1
|
||||
}
|
||||
}
|
||||
}()
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *stats) countRequest() {
|
||||
s.chanCount <- 1
|
||||
<-s.chanCount
|
||||
}
|
||||
|
||||
type socketServer struct {
|
||||
stats *stats
|
||||
repo *repo.Repo
|
||||
}
|
||||
|
||||
func extractHandlerAndJSONLentgh(header string) (handler Handler, jsonLength int, err error) {
|
||||
headerParts := strings.Split(header, ":")
|
||||
if len(headerParts) != 2 {
|
||||
return "", 0, errors.New("invalid header")
|
||||
}
|
||||
jsonLength, err = strconv.Atoi(headerParts[1])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("could not parse length in header: %q", header)
|
||||
}
|
||||
return Handler(headerParts[0]), jsonLength, err
|
||||
}
|
||||
|
||||
func (s *socketServer) execute(handler Handler, jsonBytes []byte) (reply []byte) {
|
||||
s.stats.countRequest()
|
||||
log.Notice("socketServer.execute: ", s.stats.requests, ", ", handler)
|
||||
if log.SelectedLevel == log.LevelDebug {
|
||||
log.Debug(" incoming json buffer:", string(jsonBytes))
|
||||
}
|
||||
reply, handlingError := handleRequest(s.repo, handler, jsonBytes)
|
||||
if handlingError != nil {
|
||||
log.Error("socketServer.execute handlingError :", handlingError)
|
||||
}
|
||||
return reply
|
||||
}
|
||||
|
||||
func (s *socketServer) writeResponse(conn net.Conn, reply []byte) {
|
||||
headerBytes := []byte(strconv.Itoa(len(reply)))
|
||||
reply = append(headerBytes, reply...)
|
||||
log.Debug(" replying: " + string(reply))
|
||||
n, writeError := conn.Write(reply)
|
||||
if writeError != nil {
|
||||
log.Error("socketServer.writeResponse: could not write my reply: " + fmt.Sprint(writeError))
|
||||
return
|
||||
}
|
||||
if n < len(reply) {
|
||||
log.Error(fmt.Sprintf("socketServer.writeResponse: write too short %q instead of %q", n, len(reply)))
|
||||
return
|
||||
}
|
||||
log.Debug(" replied. waiting for next request on open connection")
|
||||
|
||||
}
|
||||
|
||||
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 {
|
||||
log.Debug(" looks like the client closed the connection: ", readErr)
|
||||
return
|
||||
}
|
||||
// read next byte
|
||||
current := headerBuffer[0:]
|
||||
if string(current) == "{" {
|
||||
// json has started
|
||||
handler, jsonLength, headerErr := extractHandlerAndJSONLentgh(header)
|
||||
// reset header
|
||||
header = ""
|
||||
if headerErr != nil {
|
||||
log.Error("invalid request could not read header", headerErr)
|
||||
encodedErr, encodingErr := encodeReply(responses.NewError(4, "invalid header "+headerErr.Error()))
|
||||
if encodingErr == nil {
|
||||
s.writeResponse(conn, encodedErr)
|
||||
} else {
|
||||
log.Error("could not respond to invalid request", encodingErr)
|
||||
}
|
||||
return
|
||||
}
|
||||
log.Debug(fmt.Sprintf(" found json with %d bytes", jsonLength))
|
||||
if jsonLength > 0 {
|
||||
// let us try to read some json
|
||||
jsonBytes := make([]byte, jsonLength)
|
||||
// that is "{"
|
||||
jsonBytes[0] = 123
|
||||
jsonLengthCurrent := 1
|
||||
readRound := 0
|
||||
for jsonLengthCurrent < jsonLength {
|
||||
readRound++
|
||||
readLength, jsonReadErr := conn.Read(jsonBytes[jsonLengthCurrent:jsonLength])
|
||||
if jsonReadErr != nil {
|
||||
//@fixme we need to force a read timeout (SetReadDeadline?), if expected jsonLength is lower than really sent bytes (e.g. if client implements protocol wrong)
|
||||
//@todo should we check for io.EOF here
|
||||
log.Error(" could not read json - giving up with this client connection" + fmt.Sprint(jsonReadErr))
|
||||
return
|
||||
}
|
||||
jsonLengthCurrent += readLength
|
||||
log.Debug(fmt.Sprintf(" read so far %d of %d bytes in read cycle %d", jsonLengthCurrent, jsonLength, readRound))
|
||||
}
|
||||
|
||||
if log.SelectedLevel == log.LevelDebug {
|
||||
log.Debug(" read json: " + string(jsonBytes))
|
||||
}
|
||||
s.writeResponse(conn, s.execute(handler, jsonBytes))
|
||||
// note: connection remains open
|
||||
continue
|
||||
}
|
||||
log.Error("can not read empty json")
|
||||
return
|
||||
}
|
||||
// adding to header byte by byte
|
||||
header += string(headerBuffer[0:])
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
return RunServerSocketAndWebServer(server, address, "", "", varDir)
|
||||
}
|
||||
|
||||
func RunServerSocketAndWebServer(
|
||||
server string,
|
||||
address string,
|
||||
webserverAdresss string,
|
||||
webserverPath string,
|
||||
varDir string,
|
||||
) error {
|
||||
if address == "" && webserverAdresss == "" {
|
||||
@ -195,7 +50,7 @@ func RunServerSocketAndWebServer(
|
||||
go runSocketServer(r, address, chanErr)
|
||||
}
|
||||
if webserverAdresss != "" {
|
||||
go runWebserver(r, webserverAdresss, chanErr)
|
||||
go runWebserver(r, webserverAdresss, webserverPath, chanErr)
|
||||
}
|
||||
return <-chanErr
|
||||
}
|
||||
@ -203,10 +58,13 @@ func RunServerSocketAndWebServer(
|
||||
func runWebserver(
|
||||
r *repo.Repo,
|
||||
address string,
|
||||
path string,
|
||||
chanErr chan error,
|
||||
) {
|
||||
s := &webServer{
|
||||
r: r,
|
||||
s, errNew := NewWebServer(path, r)
|
||||
if errNew != nil {
|
||||
chanErr <- errNew
|
||||
return
|
||||
}
|
||||
chanErr <- http.ListenAndServe(address, s)
|
||||
}
|
||||
|
||||
155
server/socketserver.go
Normal file
155
server/socketserver.go
Normal file
@ -0,0 +1,155 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/foomo/contentserver/log"
|
||||
"github.com/foomo/contentserver/repo"
|
||||
"github.com/foomo/contentserver/responses"
|
||||
)
|
||||
|
||||
// simple internal request counter
|
||||
type stats struct {
|
||||
requests int64
|
||||
chanCount chan int
|
||||
}
|
||||
|
||||
func newStats() *stats {
|
||||
s := &stats{
|
||||
requests: 0,
|
||||
chanCount: make(chan int),
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-s.chanCount:
|
||||
s.requests++
|
||||
s.chanCount <- 1
|
||||
}
|
||||
}
|
||||
}()
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *stats) countRequest() {
|
||||
s.chanCount <- 1
|
||||
<-s.chanCount
|
||||
}
|
||||
|
||||
type socketServer struct {
|
||||
stats *stats
|
||||
repo *repo.Repo
|
||||
}
|
||||
|
||||
func extractHandlerAndJSONLentgh(header string) (handler Handler, jsonLength int, err error) {
|
||||
headerParts := strings.Split(header, ":")
|
||||
if len(headerParts) != 2 {
|
||||
return "", 0, errors.New("invalid header")
|
||||
}
|
||||
jsonLength, err = strconv.Atoi(headerParts[1])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("could not parse length in header: %q", header)
|
||||
}
|
||||
return Handler(headerParts[0]), jsonLength, err
|
||||
}
|
||||
|
||||
func (s *socketServer) execute(handler Handler, jsonBytes []byte) (reply []byte) {
|
||||
s.stats.countRequest()
|
||||
log.Notice("socketServer.execute: ", s.stats.requests, ", ", handler)
|
||||
if log.SelectedLevel == log.LevelDebug {
|
||||
log.Debug(" incoming json buffer:", string(jsonBytes))
|
||||
}
|
||||
reply, handlingError := handleRequest(s.repo, handler, jsonBytes)
|
||||
if handlingError != nil {
|
||||
log.Error("socketServer.execute handlingError :", handlingError)
|
||||
}
|
||||
return reply
|
||||
}
|
||||
|
||||
func (s *socketServer) writeResponse(conn net.Conn, reply []byte) {
|
||||
headerBytes := []byte(strconv.Itoa(len(reply)))
|
||||
reply = append(headerBytes, reply...)
|
||||
log.Debug(" replying: " + string(reply))
|
||||
n, writeError := conn.Write(reply)
|
||||
if writeError != nil {
|
||||
log.Error("socketServer.writeResponse: could not write my reply: " + fmt.Sprint(writeError))
|
||||
return
|
||||
}
|
||||
if n < len(reply) {
|
||||
log.Error(fmt.Sprintf("socketServer.writeResponse: write too short %q instead of %q", n, len(reply)))
|
||||
return
|
||||
}
|
||||
log.Debug(" replied. waiting for next request on open connection")
|
||||
|
||||
}
|
||||
|
||||
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 {
|
||||
log.Debug(" looks like the client closed the connection: ", readErr)
|
||||
return
|
||||
}
|
||||
// read next byte
|
||||
current := headerBuffer[0:]
|
||||
if string(current) == "{" {
|
||||
// json has started
|
||||
handler, jsonLength, headerErr := extractHandlerAndJSONLentgh(header)
|
||||
// reset header
|
||||
header = ""
|
||||
if headerErr != nil {
|
||||
log.Error("invalid request could not read header", headerErr)
|
||||
encodedErr, encodingErr := encodeReply(responses.NewError(4, "invalid header "+headerErr.Error()))
|
||||
if encodingErr == nil {
|
||||
s.writeResponse(conn, encodedErr)
|
||||
} else {
|
||||
log.Error("could not respond to invalid request", encodingErr)
|
||||
}
|
||||
return
|
||||
}
|
||||
log.Debug(fmt.Sprintf(" found json with %d bytes", jsonLength))
|
||||
if jsonLength > 0 {
|
||||
// let us try to read some json
|
||||
jsonBytes := make([]byte, jsonLength)
|
||||
// that is "{"
|
||||
jsonBytes[0] = 123
|
||||
jsonLengthCurrent := 1
|
||||
readRound := 0
|
||||
for jsonLengthCurrent < jsonLength {
|
||||
readRound++
|
||||
readLength, jsonReadErr := conn.Read(jsonBytes[jsonLengthCurrent:jsonLength])
|
||||
if jsonReadErr != nil {
|
||||
//@fixme we need to force a read timeout (SetReadDeadline?), if expected jsonLength is lower than really sent bytes (e.g. if client implements protocol wrong)
|
||||
//@todo should we check for io.EOF here
|
||||
log.Error(" could not read json - giving up with this client connection" + fmt.Sprint(jsonReadErr))
|
||||
return
|
||||
}
|
||||
jsonLengthCurrent += readLength
|
||||
log.Debug(fmt.Sprintf(" read so far %d of %d bytes in read cycle %d", jsonLengthCurrent, jsonLength, readRound))
|
||||
}
|
||||
|
||||
if log.SelectedLevel == log.LevelDebug {
|
||||
log.Debug(" read json: " + string(jsonBytes))
|
||||
}
|
||||
s.writeResponse(conn, s.execute(handler, jsonBytes))
|
||||
// note: connection remains open
|
||||
continue
|
||||
}
|
||||
log.Error("can not read empty json")
|
||||
return
|
||||
}
|
||||
// adding to header byte by byte
|
||||
header += string(headerBuffer[0:])
|
||||
}
|
||||
}
|
||||
@ -8,14 +8,17 @@ import (
|
||||
"github.com/foomo/contentserver/repo"
|
||||
)
|
||||
|
||||
const PathContentserver = "/contentserver"
|
||||
|
||||
type webServer struct {
|
||||
r *repo.Repo
|
||||
r *repo.Repo
|
||||
path string
|
||||
}
|
||||
|
||||
func newWebServer() (s *webServer, err error) {
|
||||
s = &webServer{}
|
||||
// NewWebServer returns a shiny new web server
|
||||
func NewWebServer(path string, r *repo.Repo) (s http.Handler, err error) {
|
||||
s = &webServer{
|
||||
r: r,
|
||||
path: path,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -30,7 +33,7 @@ func (s *webServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "failed to read incoming request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
reply, errReply := handleRequest(s.r, Handler(strings.TrimPrefix(r.URL.Path, PathContentserver+"/")), jsonBytes)
|
||||
reply, errReply := handleRequest(s.r, Handler(strings.TrimPrefix(r.URL.Path, s.path+"/")), jsonBytes)
|
||||
if errReply != nil {
|
||||
http.Error(w, errReply.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
||||
Loading…
Reference in New Issue
Block a user