summaryrefslogtreecommitdiff
path: root/gopher/serve.go
diff options
context:
space:
mode:
authortjpcc <tjp@ctrl-c.club>2023-01-28 14:52:35 -0700
committertjpcc <tjp@ctrl-c.club>2023-01-28 15:01:41 -0700
commit66a1b1f39a1e1d5499b548b36d18c8daa872d7da (patch)
tree96471dbd5486ede1a908790ac23e0c55b226dfad /gopher/serve.go
parenta27b879accb191b6a6c6e76a6251ed751967f73a (diff)
gopher support.
Some of the contrib packages were originally built gemini-specific and had to be refactored into generic core functionality and thin protocol-specific wrappers for each of gemini and gopher.
Diffstat (limited to 'gopher/serve.go')
-rw-r--r--gopher/serve.go72
1 files changed, 72 insertions, 0 deletions
diff --git a/gopher/serve.go b/gopher/serve.go
new file mode 100644
index 0000000..84745d7
--- /dev/null
+++ b/gopher/serve.go
@@ -0,0 +1,72 @@
+package gopher
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "strings"
+
+ "tildegit.org/tjp/gus"
+ "tildegit.org/tjp/gus/internal"
+ "tildegit.org/tjp/gus/logging"
+)
+
+type gopherServer struct {
+ internal.Server
+ handler gus.Handler
+}
+
+func (gs gopherServer) Protocol() string { return "GOPHER" }
+
+// NewServer builds a gopher server.
+func NewServer(
+ ctx context.Context,
+ hostname string,
+ network string,
+ address string,
+ handler gus.Handler,
+ errLog logging.Logger,
+) (gus.Server, error) {
+ gs := &gopherServer{handler: handler}
+
+ if strings.IndexByte(hostname, ':') < 0 {
+ hostname = net.JoinHostPort(hostname, "70")
+ }
+
+ var err error
+ gs.Server, err = internal.NewServer(ctx, hostname, network, address, errLog, gs.handleConn)
+ if err != nil {
+ return nil, err
+ }
+
+ return gs, nil
+}
+
+func (gs *gopherServer) handleConn(conn net.Conn) {
+ var response *gus.Response
+ request, err := ParseRequest(conn)
+ if err != nil {
+ response = Error(errors.New("Malformed request.")).Response()
+ } else {
+ request.Server = gs
+ request.RemoteAddr = conn.RemoteAddr()
+
+ defer func() {
+ if r := recover(); r != nil {
+ err := fmt.Errorf("%s", r)
+ _ = gs.LogError("msg", "panic in handler", "err", err)
+ rdr := NewResponseReader(Error(errors.New("Server error.")).Response())
+ _, _ = io.Copy(conn, rdr)
+ }
+ }()
+ response = gs.handler(gs.Ctx, request)
+ if response == nil {
+ response = Error(errors.New("Resource does not exist.")).Response()
+ }
+ }
+
+ defer response.Close()
+ _, _ = io.Copy(conn, NewResponseReader(response))
+}