1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
package gemini
import (
"bufio"
"context"
"crypto/tls"
"fmt"
"io"
"net"
"tildegit.org/tjp/sliderule/internal"
"tildegit.org/tjp/sliderule/internal/types"
"tildegit.org/tjp/sliderule/logging"
)
type server struct {
internal.Server
handler types.Handler
}
func (s server) Protocol() string { return "GEMINI" }
// NewServer builds a gemini server.
func NewServer(
ctx context.Context,
hostname string,
network string,
address string,
handler types.Handler,
baseLog logging.Logger,
tlsConfig *tls.Config,
) (types.Server, error) {
s := &server{handler: handler}
hostname = internal.JoinDefaultPort(hostname, "1965")
address = internal.JoinDefaultPort(address, "1965")
internalServer, err := internal.NewServer(ctx, hostname, network, address, baseLog, s.handleConn)
if err != nil {
return nil, err
}
s.Server = internalServer
s.Listener = tls.NewListener(s.Listener, tlsConfig)
return s, nil
}
func (s *server) handleConn(conn net.Conn) {
buf := bufio.NewReader(conn)
var response *types.Response
request, err := ParseRequest(buf)
if err != nil {
response = BadRequest(err.Error())
} else {
if request.Path == "" {
request.Path = "/"
}
request.Server = s
request.RemoteAddr = conn.RemoteAddr()
if tlsconn, ok := conn.(*tls.Conn); ok {
state := tlsconn.ConnectionState()
request.TLSState = &state
}
defer func() {
if r := recover(); r != nil {
err := fmt.Errorf("%s", r)
_ = s.LogError("msg", "panic in handler", "err", err)
_, _ = io.Copy(conn, NewResponseReader(Failure(err)))
}
}()
response = s.handler.Handle(s.Ctx, request)
if response == nil {
response = NotFound("Resource does not exist.")
}
}
defer response.Close()
_, _ = io.Copy(conn, NewResponseReader(response))
}
// GeminiOnly filters requests down to just those on the gemini:// protocol.
//
// Optionally, it will also allow through titan:// requests.
//
// Filtered requests will be turned away with a 53 response "proxy request refused".
func GeminiOnly(allowTitan bool) types.Middleware {
return func(inner types.Handler) types.Handler {
return types.HandlerFunc(func(ctx context.Context, request *types.Request) *types.Response {
if request.Scheme == "gemini" || (allowTitan && request.Scheme == "titan") {
return inner.Handle(ctx, request)
}
return RefuseProxy("Non-gemini protocol requests are not supported.")
})
}
}
|