Skip to content

Commit b561f49

Browse files
authored
refactor: create re-usable Shadowsocks service (#206)
* refactor: create re-usable service that can be re-used by Caddy * Remove need to return errors in opt functions. * Move the service into `shadowsocks.go`. * Move initialization of handlers to the constructor. * Pass a `list.List` instead of a `CipherList`. * Rename `SSServer` to `OutlineServer`. * refactor: make connection metrics optional * Revert "Pass a `list.List` instead of a `CipherList`." This reverts commit 1259af8. * Create noop metrics if nil. * Revert some more changes. * Use a noop metrics struct if no metrics provided. * Add noop implementation for `ShadowsocksConnMetrics`. * Resolve nil metrics.
1 parent 4463b38 commit b561f49

File tree

7 files changed

+1023
-775
lines changed

7 files changed

+1023
-775
lines changed

cmd/outline-ss-server/main.go

+49-77
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ package main
1616

1717
import (
1818
"container/list"
19-
"context"
2019
"flag"
2120
"fmt"
2221
"log/slog"
@@ -29,9 +28,9 @@ import (
2928
"syscall"
3029
"time"
3130

32-
"github.com/Jigsaw-Code/outline-sdk/transport"
3331
"github.com/Jigsaw-Code/outline-sdk/transport/shadowsocks"
3432
"github.com/Jigsaw-Code/outline-ss-server/ipinfo"
33+
outline_prometheus "github.com/Jigsaw-Code/outline-ss-server/prometheus"
3534
"github.com/Jigsaw-Code/outline-ss-server/service"
3635
"github.com/lmittmann/tint"
3736
"github.com/prometheus/client_golang/prometheus"
@@ -58,15 +57,16 @@ func init() {
5857
)
5958
}
6059

61-
type SSServer struct {
62-
stopConfig func() error
63-
lnManager service.ListenerManager
64-
natTimeout time.Duration
65-
m *outlineMetrics
66-
replayCache service.ReplayCache
60+
type OutlineServer struct {
61+
stopConfig func() error
62+
lnManager service.ListenerManager
63+
natTimeout time.Duration
64+
serverMetrics *serverMetrics
65+
serviceMetrics service.ServiceMetrics
66+
replayCache service.ReplayCache
6767
}
6868

69-
func (s *SSServer) loadConfig(filename string) error {
69+
func (s *OutlineServer) loadConfig(filename string) error {
7070
configData, err := os.ReadFile(filename)
7171
if err != nil {
7272
return fmt.Errorf("failed to read config file %s: %w", filename, err)
@@ -120,32 +120,6 @@ func newCipherListFromConfig(config ServiceConfig) (service.CipherList, error) {
120120
return ciphers, nil
121121
}
122122

123-
func (s *SSServer) NewShadowsocksStreamHandler(ciphers service.CipherList) service.StreamHandler {
124-
authFunc := service.NewShadowsocksStreamAuthenticator(ciphers, &s.replayCache, s.m.tcpServiceMetrics)
125-
// TODO: Register initial data metrics at zero.
126-
return service.NewStreamHandler(authFunc, tcpReadTimeout)
127-
}
128-
129-
func (s *SSServer) NewShadowsocksPacketHandler(ciphers service.CipherList) service.PacketHandler {
130-
return service.NewPacketHandler(s.natTimeout, ciphers, s.m, s.m.udpServiceMetrics)
131-
}
132-
133-
func (s *SSServer) NewShadowsocksStreamHandlerFromConfig(config ServiceConfig) (service.StreamHandler, error) {
134-
ciphers, err := newCipherListFromConfig(config)
135-
if err != nil {
136-
return nil, err
137-
}
138-
return s.NewShadowsocksStreamHandler(ciphers), nil
139-
}
140-
141-
func (s *SSServer) NewShadowsocksPacketHandlerFromConfig(config ServiceConfig) (service.PacketHandler, error) {
142-
ciphers, err := newCipherListFromConfig(config)
143-
if err != nil {
144-
return nil, err
145-
}
146-
return s.NewShadowsocksPacketHandler(ciphers), nil
147-
}
148-
149123
type listenerSet struct {
150124
manager service.ListenerManager
151125
listenerCloseFuncs map[string]func() error
@@ -207,7 +181,7 @@ func (ls *listenerSet) Len() int {
207181
return len(ls.listenerCloseFuncs)
208182
}
209183

210-
func (s *SSServer) runConfig(config Config) (func() error, error) {
184+
func (s *OutlineServer) runConfig(config Config) (func() error, error) {
211185
startErrCh := make(chan error)
212186
stopErrCh := make(chan error)
213187
stopCh := make(chan struct{})
@@ -243,31 +217,41 @@ func (s *SSServer) runConfig(config Config) (func() error, error) {
243217
ciphers := service.NewCipherList()
244218
ciphers.Update(cipherList)
245219

246-
sh := s.NewShadowsocksStreamHandler(ciphers)
220+
ssService, err := service.NewShadowsocksService(
221+
service.WithCiphers(ciphers),
222+
service.WithNatTimeout(s.natTimeout),
223+
service.WithMetrics(s.serviceMetrics),
224+
service.WithReplayCache(&s.replayCache),
225+
)
247226
ln, err := lnSet.ListenStream(addr)
248227
if err != nil {
249228
return err
250229
}
251230
slog.Info("TCP service started.", "address", ln.Addr().String())
252-
go service.StreamServe(ln.AcceptStream, func(ctx context.Context, conn transport.StreamConn) {
253-
connMetrics := s.m.AddOpenTCPConnection(conn)
254-
sh.Handle(ctx, conn, connMetrics)
255-
})
231+
go service.StreamServe(ln.AcceptStream, ssService.HandleStream)
256232

257233
pc, err := lnSet.ListenPacket(addr)
258234
if err != nil {
259235
return err
260236
}
261237
slog.Info("UDP service started.", "address", pc.LocalAddr().String())
262-
ph := s.NewShadowsocksPacketHandler(ciphers)
263-
go ph.Handle(pc)
238+
go ssService.HandlePacket(pc)
264239
}
265240

266241
for _, serviceConfig := range config.Services {
267-
var (
268-
sh service.StreamHandler
269-
ph service.PacketHandler
242+
ciphers, err := newCipherListFromConfig(serviceConfig)
243+
if err != nil {
244+
return fmt.Errorf("failed to create cipher list from config: %v", err)
245+
}
246+
ssService, err := service.NewShadowsocksService(
247+
service.WithCiphers(ciphers),
248+
service.WithNatTimeout(s.natTimeout),
249+
service.WithMetrics(s.serviceMetrics),
250+
service.WithReplayCache(&s.replayCache),
270251
)
252+
if err != nil {
253+
return err
254+
}
271255
for _, lnConfig := range serviceConfig.Listeners {
272256
switch lnConfig.Type {
273257
case listenerTypeTCP:
@@ -276,36 +260,21 @@ func (s *SSServer) runConfig(config Config) (func() error, error) {
276260
return err
277261
}
278262
slog.Info("TCP service started.", "address", ln.Addr().String())
279-
if sh == nil {
280-
sh, err = s.NewShadowsocksStreamHandlerFromConfig(serviceConfig)
281-
if err != nil {
282-
return err
283-
}
284-
}
285-
go service.StreamServe(ln.AcceptStream, func(ctx context.Context, conn transport.StreamConn) {
286-
connMetrics := s.m.AddOpenTCPConnection(conn)
287-
sh.Handle(ctx, conn, connMetrics)
288-
})
263+
go service.StreamServe(ln.AcceptStream, ssService.HandleStream)
289264
case listenerTypeUDP:
290265
pc, err := lnSet.ListenPacket(lnConfig.Address)
291266
if err != nil {
292267
return err
293268
}
294269
slog.Info("UDP service started.", "address", pc.LocalAddr().String())
295-
if ph == nil {
296-
ph, err = s.NewShadowsocksPacketHandlerFromConfig(serviceConfig)
297-
if err != nil {
298-
return err
299-
}
300-
}
301-
go ph.Handle(pc)
270+
go ssService.HandlePacket(pc)
302271
}
303272
}
304273
totalCipherCount += len(serviceConfig.Keys)
305274
}
306275

307276
slog.Info("Loaded config.", "access_keys", totalCipherCount, "listeners", lnSet.Len())
308-
s.m.SetNumAccessKeys(totalCipherCount, lnSet.Len())
277+
s.serverMetrics.SetNumAccessKeys(totalCipherCount, lnSet.Len())
309278
return nil
310279
}()
311280

@@ -327,7 +296,7 @@ func (s *SSServer) runConfig(config Config) (func() error, error) {
327296
}
328297

329298
// Stop stops serving the current config.
330-
func (s *SSServer) Stop() error {
299+
func (s *OutlineServer) Stop() error {
331300
stopFunc := s.stopConfig
332301
if stopFunc == nil {
333302
return nil
@@ -340,13 +309,14 @@ func (s *SSServer) Stop() error {
340309
return nil
341310
}
342311

343-
// RunSSServer starts a shadowsocks server running, and returns the server or an error.
344-
func RunSSServer(filename string, natTimeout time.Duration, sm *outlineMetrics, replayHistory int) (*SSServer, error) {
345-
server := &SSServer{
346-
lnManager: service.NewListenerManager(),
347-
natTimeout: natTimeout,
348-
m: sm,
349-
replayCache: service.NewReplayCache(replayHistory),
312+
// RunOutlineServer starts an Outline server running, and returns the server or an error.
313+
func RunOutlineServer(filename string, natTimeout time.Duration, serverMetrics *serverMetrics, serviceMetrics service.ServiceMetrics, replayHistory int) (*OutlineServer, error) {
314+
server := &OutlineServer{
315+
lnManager: service.NewListenerManager(),
316+
natTimeout: natTimeout,
317+
serverMetrics: serverMetrics,
318+
serviceMetrics: serviceMetrics,
319+
replayCache: service.NewReplayCache(replayHistory),
350320
}
351321
err := server.loadConfig(filename)
352322
if err != nil {
@@ -424,14 +394,16 @@ func main() {
424394
}
425395
defer ip2info.Close()
426396

427-
metrics, err := newPrometheusOutlineMetrics(ip2info)
397+
serverMetrics := newPrometheusServerMetrics()
398+
serverMetrics.SetVersion(version)
399+
serviceMetrics, err := outline_prometheus.NewServiceMetrics(ip2info)
428400
if err != nil {
429-
slog.Error("Failed to create Outline Prometheus metrics. Aborting.", "err", err)
401+
slog.Error("Failed to create Outline Prometheus service metrics. Aborting.", "err", err)
430402
}
431-
metrics.SetBuildInfo(version)
432403
r := prometheus.WrapRegistererWithPrefix("shadowsocks_", prometheus.DefaultRegisterer)
433-
r.MustRegister(metrics)
434-
_, err = RunSSServer(flags.ConfigFile, flags.natTimeout, metrics, flags.replayHistory)
404+
r.MustRegister(serverMetrics, serviceMetrics)
405+
406+
_, err = RunOutlineServer(flags.ConfigFile, flags.natTimeout, serverMetrics, serviceMetrics, flags.replayHistory)
435407
if err != nil {
436408
slog.Error("Server failed to start. Aborting.", "err", err)
437409
}

0 commit comments

Comments
 (0)