Skip to content

Commit c4f554e

Browse files
committed
Create connection handlers that can be shared between WebSocket and layer4 connections.
1 parent 8697a50 commit c4f554e

File tree

12 files changed

+461
-313
lines changed

12 files changed

+461
-313
lines changed

caddy/app.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,19 @@ import (
2828
"github.com/prometheus/client_golang/prometheus"
2929
)
3030

31-
const outlineModuleName = "outline"
31+
const (
32+
outlineModuleName = "outline"
33+
replayCacheCtxKey = "outline_replay_cache"
34+
metricsCtxKey = "outline_metrics"
35+
)
3236

3337
func init() {
3438
replayCache := outline.NewReplayCache(0)
3539
caddy.RegisterModule(ModuleRegistration{
3640
ID: outlineModuleName,
3741
New: func() caddy.Module {
3842
app := new(OutlineApp)
39-
app.ReplayCache = replayCache
43+
app.replayCache = replayCache
4044
return app
4145
},
4246
})
@@ -48,10 +52,11 @@ type ShadowsocksConfig struct {
4852

4953
type OutlineApp struct {
5054
ShadowsocksConfig *ShadowsocksConfig `json:"shadowsocks,omitempty"`
55+
Handlers ConnectionHandlers `json:"connection_handlers,omitempty"`
5156

52-
ReplayCache outline.ReplayCache
5357
logger *slog.Logger
54-
Metrics outline.ServiceMetrics
58+
replayCache outline.ReplayCache
59+
metrics outline.ServiceMetrics
5560
buildInfo *prometheus.GaugeVec
5661
}
5762

@@ -71,7 +76,7 @@ func (app *OutlineApp) Provision(ctx caddy.Context) error {
7176
app.logger.Info("provisioning app instance")
7277

7378
if app.ShadowsocksConfig != nil {
74-
if err := app.ReplayCache.Resize(app.ShadowsocksConfig.ReplayHistory); err != nil {
79+
if err := app.replayCache.Resize(app.ShadowsocksConfig.ReplayHistory); err != nil {
7580
return fmt.Errorf("failed to configure replay history with capacity %d: %v", app.ShadowsocksConfig.ReplayHistory, err)
7681
}
7782
}
@@ -83,6 +88,14 @@ func (app *OutlineApp) Provision(ctx caddy.Context) error {
8388
app.buildInfo.WithLabelValues("dev").Set(1)
8489
// TODO: Add replacement metrics for `shadowsocks_keys` and `shadowsocks_ports`.
8590

91+
ctx = ctx.WithValue(replayCacheCtxKey, app.replayCache)
92+
ctx = ctx.WithValue(metricsCtxKey, app.metrics)
93+
94+
err := app.Handlers.Provision(ctx)
95+
if err != nil {
96+
return err
97+
}
98+
8699
return nil
87100
}
88101

@@ -104,7 +117,7 @@ func (app *OutlineApp) defineMetrics() error {
104117
if err != nil {
105118
return err
106119
}
107-
app.Metrics, err = registerCollector(r, metrics)
120+
app.metrics, err = registerCollector(r, metrics)
108121
if err != nil {
109122
return err
110123
}

caddy/connection_handler.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2024 The Outline Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package outlinecaddy
16+
17+
import (
18+
"encoding/json"
19+
"fmt"
20+
21+
"github.com/caddyserver/caddy/v2"
22+
"github.com/mholt/caddy-l4/layer4"
23+
)
24+
25+
type ConnectionHandler struct {
26+
Name string `json:"name,omitempty"`
27+
WrappedHandlerRaw json.RawMessage `json:"handle,omitempty" caddy:"namespace=layer4.handlers inline_key=handler"`
28+
29+
compiled layer4.NextHandler
30+
}
31+
32+
var (
33+
_ caddy.Provisioner = (*ConnectionHandler)(nil)
34+
_ layer4.NextHandler = (*ConnectionHandler)(nil)
35+
)
36+
37+
// Provision sets up the connection handler.
38+
func (ch *ConnectionHandler) Provision(ctx caddy.Context) error {
39+
mod, err := ctx.LoadModule(ch, "WrappedHandlerRaw")
40+
if err != nil {
41+
return err
42+
}
43+
compiled, ok := mod.(layer4.NextHandler)
44+
if !ok {
45+
return fmt.Errorf("module is of type `%T`, expected `layer4.NextHandler`", compiled)
46+
}
47+
ch.compiled = compiled
48+
return nil
49+
}
50+
51+
func (ch *ConnectionHandler) Handle(cx *layer4.Connection, next layer4.Handler) error {
52+
return ch.compiled.Handle(cx, next)
53+
}
54+
55+
type ConnectionHandlers []*ConnectionHandler
56+
57+
// Provision sets up all the connection handlers.
58+
func (ch ConnectionHandlers) Provision(ctx caddy.Context) error {
59+
for i, h := range ch {
60+
err := h.Provision(ctx)
61+
if err != nil {
62+
return fmt.Errorf("connection handler %d: %v", i, err)
63+
}
64+
}
65+
return nil
66+
}

caddy/examples/config_example.json

Lines changed: 48 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -31,115 +31,53 @@
3131
]
3232
}
3333
]
34-
}
35-
}
36-
},
37-
"layer4": {
38-
"servers": {
39-
"1": {
40-
"listen": [
41-
"tcp/[::]:9000",
42-
"udp/[::]:9000"
43-
],
34+
},
35+
"ws1": {
36+
"listen": [":8000"],
4437
"routes": [
4538
{
46-
"match": [
47-
{
48-
"http": [
49-
{
50-
"path": [
51-
"/tcp"
52-
],
53-
"header": {
54-
"Connection": ["*Upgrade*"],
55-
"Upgrade": ["websocket"]
56-
}
57-
}
58-
]
59-
}
60-
],
39+
"match": [{"path": ["/tcp"]}],
6140
"handle": [
6241
{
63-
"handler": "websocket",
64-
"type": "stream"
65-
},
66-
{
67-
"handler": "shadowsocks",
68-
"keys": [
69-
{
70-
"id": "user-0",
71-
"cipher": "chacha20-ietf-poly1305",
72-
"secret": "Secret0"
73-
},
74-
{
75-
"id": "user-1",
76-
"cipher": "chacha20-ietf-poly1305",
77-
"secret": "Secret1"
78-
}
79-
]
42+
"handler": "ws2outline",
43+
"type": "stream",
44+
"connection_handler": "main"
8045
}
8146
]
8247
},
8348
{
84-
"match": [
85-
{
86-
"http": [
87-
{
88-
"path": [
89-
"/udp"
90-
],
91-
"header": {
92-
"Connection": ["*Upgrade*"],
93-
"Upgrade": ["websocket"]
94-
}
95-
}
96-
]
97-
}
98-
],
49+
"match": [{"path": ["/udp"]}],
9950
"handle": [
10051
{
101-
"handler": "websocket",
102-
"type": "packet"
103-
},
104-
{
105-
"handler": "shadowsocks",
106-
"keys": [
107-
{
108-
"id": "user-0",
109-
"cipher": "chacha20-ietf-poly1305",
110-
"secret": "Secret0"
111-
},
112-
{
113-
"id": "user-1",
114-
"cipher": "chacha20-ietf-poly1305",
115-
"secret": "Secret1"
116-
}
117-
]
52+
"handler": "ws2outline",
53+
"type": "packet",
54+
"connection_handler": "main"
11855
}
11956
]
120-
},
57+
}
58+
]
59+
}
60+
}
61+
},
62+
"layer4": {
63+
"servers": {
64+
"ss1": {
65+
"listen": [
66+
"tcp/[::]:9000",
67+
"udp/[::]:9000"
68+
],
69+
"routes": [
12170
{
12271
"handle": [
12372
{
124-
"handler": "shadowsocks",
125-
"keys": [
126-
{
127-
"id": "user-0",
128-
"cipher": "chacha20-ietf-poly1305",
129-
"secret": "Secret0"
130-
},
131-
{
132-
"id": "user-1",
133-
"cipher": "chacha20-ietf-poly1305",
134-
"secret": "Secret1"
135-
}
136-
]
73+
"handler": "outline",
74+
"connection_handler": "main"
13775
}
13876
]
13977
}
14078
]
14179
},
142-
"2": {
80+
"ss2_no_replaycache": {
14381
"listen": [
14482
"tcp/[::]:9001",
14583
"udp/[::]:9001"
@@ -166,7 +104,27 @@
166104
"outline": {
167105
"shadowsocks": {
168106
"replay_history": 10000
169-
}
107+
},
108+
"connection_handlers": [
109+
{
110+
"name": "main",
111+
"handle": {
112+
"handler": "shadowsocks",
113+
"keys": [
114+
{
115+
"id": "user-0",
116+
"cipher": "chacha20-ietf-poly1305",
117+
"secret": "Secret0"
118+
},
119+
{
120+
"id": "user-1",
121+
"cipher": "chacha20-ietf-poly1305",
122+
"secret": "Secret1"
123+
}
124+
]
125+
}
126+
}
127+
]
170128
}
171129
}
172130
}

caddy/examples/simple.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
# Copyright 2024 The Outline Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
115
---
216
admin:
317
disabled: true

caddy/examples/websocket.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
# Copyright 2024 The Outline Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
115
---
216
admin:
317
disabled: true

caddy/go.mod

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ require (
66
github.com/Jigsaw-Code/outline-sdk v0.0.16
77
github.com/Jigsaw-Code/outline-ss-server v1.7.3-0.20240923183844-cb5965fd1a69
88
github.com/caddyserver/caddy/v2 v2.8.4
9-
github.com/gorilla/websocket v1.5.3
109
github.com/mholt/caddy-l4 v0.0.0-20240812213304-afa78d72257b
1110
github.com/prometheus/client_golang v1.20.0
11+
go.uber.org/zap v1.27.0
12+
golang.org/x/net v0.30.0
1213
)
1314

1415
require (
@@ -108,17 +109,15 @@ require (
108109
go.uber.org/automaxprocs v1.5.3 // indirect
109110
go.uber.org/mock v0.4.0 // indirect
110111
go.uber.org/multierr v1.11.0 // indirect
111-
go.uber.org/zap v1.27.0 // indirect
112112
go.uber.org/zap/exp v0.2.0 // indirect
113-
golang.org/x/crypto v0.24.0 // indirect
113+
golang.org/x/crypto v0.28.0 // indirect
114114
golang.org/x/crypto/x509roots/fallback v0.0.0-20240507223354-67b13616a595 // indirect
115115
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
116116
golang.org/x/mod v0.17.0 // indirect
117-
golang.org/x/net v0.26.0 // indirect
118-
golang.org/x/sync v0.7.0 // indirect
119-
golang.org/x/sys v0.22.0 // indirect
120-
golang.org/x/term v0.21.0 // indirect
121-
golang.org/x/text v0.16.0 // indirect
117+
golang.org/x/sync v0.8.0 // indirect
118+
golang.org/x/sys v0.26.0 // indirect
119+
golang.org/x/term v0.25.0 // indirect
120+
golang.org/x/text v0.19.0 // indirect
122121
golang.org/x/time v0.5.0 // indirect
123122
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
124123
google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae // indirect

0 commit comments

Comments
 (0)