summaryrefslogtreecommitdiff
path: root/spartan/serve.go
diff options
context:
space:
mode:
authortjpcc <tjp@ctrl-c.club>2023-04-29 16:24:38 -0600
committertjpcc <tjp@ctrl-c.club>2023-04-29 16:24:38 -0600
commit039c58c9d00a4a5886fa99d7c7d472e6d02d6a67 (patch)
tree2b19ed023e956617ee3206a4528ad62317316942 /spartan/serve.go
parentaa6bdb0649c2f2a63b4deae8c4984a660cd0400a (diff)
initial spartan server support
Diffstat (limited to 'spartan/serve.go')
-rw-r--r--spartan/serve.go95
1 files changed, 95 insertions, 0 deletions
diff --git a/spartan/serve.go b/spartan/serve.go
new file mode 100644
index 0000000..677d76c
--- /dev/null
+++ b/spartan/serve.go
@@ -0,0 +1,95 @@
+package spartan
+
+import (
+ "bufio"
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "strings"
+
+ "tildegit.org/tjp/gus"
+ "tildegit.org/tjp/gus/internal"
+ "tildegit.org/tjp/gus/logging"
+)
+
+type spartanRequestBodyKey struct{}
+type spartanRequestBodyLenKey struct{}
+
+// SpartanRequestBody is the key set in a handler's context for spartan request bodies.
+//
+// The corresponding value is a *bufio.Reader from which the request body can be read.
+var SpartanRequestBody = spartanRequestBodyKey{}
+
+// SpartanRequestBodyLen is the key set in a handler's context for the content-length of the request.
+//
+// The corresponding value is an int.
+var SpartanRequestBodyLen = spartanRequestBodyLenKey{}
+
+type spartanServer struct {
+ internal.Server
+ handler gus.Handler
+}
+
+func (ss spartanServer) Protocol() string { return "SPARTAN" }
+
+// NewServer builds a spartan server.
+func NewServer(
+ ctx context.Context,
+ hostname string,
+ network string,
+ address string,
+ handler gus.Handler,
+ errLog logging.Logger,
+) (gus.Server, error) {
+ ss := &spartanServer{handler: handler}
+
+ if strings.IndexByte(hostname, ':') < 0 {
+ hostname = net.JoinHostPort(hostname, "300")
+ }
+
+ var err error
+ ss.Server, err = internal.NewServer(ctx, hostname, network, address, errLog, ss.handleConn)
+ if err != nil {
+ return nil, err
+ }
+
+ return ss, nil
+}
+
+func (ss *spartanServer) handleConn(conn net.Conn) {
+ buf := bufio.NewReader(conn)
+
+ var response *gus.Response
+ request, clen, err := ParseRequest(buf)
+ if err != nil {
+ response = ClientError(err)
+ } else {
+ request.Server = ss
+ request.RemoteAddr = conn.RemoteAddr()
+
+ var body *bufio.Reader = nil
+ if clen > 0 {
+ body = bufio.NewReader(io.LimitReader(buf, int64(clen)))
+ }
+ ctx := context.WithValue(ss.Ctx, SpartanRequestBody, body)
+ ctx = context.WithValue(ctx, SpartanRequestBodyLen, clen)
+
+ defer func() {
+ if r := recover(); r != nil {
+ err := fmt.Errorf("%s", r)
+ _ = ss.LogError("msg", "panic in handler", "err", err)
+ rdr := NewResponseReader(ServerError(errors.New("Server error")))
+ _, _ = io.Copy(conn, rdr)
+ }
+ }()
+ response = ss.handler.Handle(ctx, request)
+ if response == nil {
+ response = ClientError(errors.New("Resource does not exist."))
+ }
+ }
+
+ defer response.Close()
+ _, _ = io.Copy(conn, NewResponseReader(response))
+}