Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 13, 20:30 UTC
reverseproxy: Sync up `handleUpgradeResponse` with stdlib (#4664)
* reverseproxy: Sync up `handleUpgradeResponse` with stdlib I had left this as a TODO for when we bump to minimum 1.17, but I should've realized it was under `internal` so it couldn't be used directly. Copied the functions we needed for parity. Hopefully this is ok! * Add tests and fix godoc comments Co-authored-by: Matthew Holt <mholt@users.noreply.github.com>
Francis Lavoie committed
Apr 11, 2022 at 14:49 UTC
e4ce40f8ff0424054045de2461fc7228c66f3c61
3 files changed
+180
-4
modules/caddyhttp/reverseproxy/ascii.go
new
+57
@@ -0,0 +1,57 @@
1
+// Copyright 2015 Matthew Holt and The Caddy 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
+// Most of the code in this file was initially borrowed from the Go
16
+// standard library and modified; It had this copyright notice:
17
+// Copyright 2021 The Go Authors. All rights reserved.
18
+// Use of this source code is governed by a BSD-style
19
+// license that can be found in the LICENSE file.
20
+
21
+// Original source, copied because the package was marked internal:
22
+// https://github.com/golang/go/blob/5c489514bc5e61ad9b5b07bd7d8ec65d66a0512a/src/net/http/internal/ascii/print.go
23
+
24
+package reverseproxy
25
+
26
+// asciiEqualFold is strings.EqualFold, ASCII only. It reports whether s and t
27
+// are equal, ASCII-case-insensitively.
28
+func asciiEqualFold(s, t string) bool {
29
+ if len(s) != len(t) {
30
+ return false
31
+ }
32
+ for i := 0; i < len(s); i++ {
33
+ if asciiLower(s[i]) != asciiLower(t[i]) {
34
+ return false
35
+ }
36
+ }
37
+ return true
38
+}
39
+
40
+// asciiLower returns the ASCII lowercase version of b.
41
+func asciiLower(b byte) byte {
42
+ if 'A' <= b && b <= 'Z' {
43
+ return b + ('a' - 'A')
44
+ }
45
+ return b
46
+}
47
+
48
+// asciiIsPrint returns whether s is ASCII and printable according to
49
+// https://tools.ietf.org/html/rfc20#section-4.2.
50
+func asciiIsPrint(s string) bool {
51
+ for i := 0; i < len(s); i++ {
52
+ if s[i] < ' ' || s[i] > '~' {
53
+ return false
54
+ }
55
+ }
56
+ return true
57
+}
modules/caddyhttp/reverseproxy/ascii_test.go
new
+114
@@ -0,0 +1,114 @@
1
+// Copyright 2015 Matthew Holt and The Caddy 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
+// Most of the code in this file was initially borrowed from the Go
16
+// standard library and modified; It had this copyright notice:
17
+// Copyright 2021 The Go Authors. All rights reserved.
18
+// Use of this source code is governed by a BSD-style
19
+// license that can be found in the LICENSE file.
20
+
21
+// Original source, copied because the package was marked internal:
22
+// https://github.com/golang/go/blob/5c489514bc5e61ad9b5b07bd7d8ec65d66a0512a/src/net/http/internal/ascii/print_test.go
23
+
24
+package reverseproxy
25
+
26
+import "testing"
27
+
28
+func TestEqualFold(t *testing.T) {
29
+ var tests = []struct {
30
+ name string
31
+ a, b string
32
+ want bool
33
+ }{
34
+ {
35
+ name: "empty",
36
+ want: true,
37
+ },
38
+ {
39
+ name: "simple match",
40
+ a: "CHUNKED",
41
+ b: "chunked",
42
+ want: true,
43
+ },
44
+ {
45
+ name: "same string",
46
+ a: "chunked",
47
+ b: "chunked",
48
+ want: true,
49
+ },
50
+ {
51
+ name: "Unicode Kelvin symbol",
52
+ a: "chunKed", // This "K" is 'KELVIN SIGN' (\u212A)
53
+ b: "chunked",
54
+ want: false,
55
+ },
56
+ }
57
+ for _, tt := range tests {
58
+ t.Run(tt.name, func(t *testing.T) {
59
+ if got := asciiEqualFold(tt.a, tt.b); got != tt.want {
60
+ t.Errorf("AsciiEqualFold(%q,%q): got %v want %v", tt.a, tt.b, got, tt.want)
61
+ }
62
+ })
63
+ }
64
+}
65
+
66
+func TestIsPrint(t *testing.T) {
67
+ var tests = []struct {
68
+ name string
69
+ in string
70
+ want bool
71
+ }{
72
+ {
73
+ name: "empty",
74
+ want: true,
75
+ },
76
+ {
77
+ name: "ASCII low",
78
+ in: "This is a space: ' '",
79
+ want: true,
80
+ },
81
+ {
82
+ name: "ASCII high",
83
+ in: "This is a tilde: '~'",
84
+ want: true,
85
+ },
86
+ {
87
+ name: "ASCII low non-print",
88
+ in: "This is a unit separator: \x1F",
89
+ want: false,
90
+ },
91
+ {
92
+ name: "Ascii high non-print",
93
+ in: "This is a Delete: \x7F",
94
+ want: false,
95
+ },
96
+ {
97
+ name: "Unicode letter",
98
+ in: "Today it's 280K outside: it's freezing!", // This "K" is 'KELVIN SIGN' (\u212A)
99
+ want: false,
100
+ },
101
+ {
102
+ name: "Unicode emoji",
103
+ in: "Gophers like 🧀",
104
+ want: false,
105
+ },
106
+ }
107
+ for _, tt := range tests {
108
+ t.Run(tt.name, func(t *testing.T) {
109
+ if got := asciiIsPrint(tt.in); got != tt.want {
110
+ t.Errorf("IsASCIIPrint(%q): got %v want %v", tt.in, got, tt.want)
111
+ }
112
+ })
113
+ }
114
+}
modules/caddyhttp/reverseproxy/streaming.go
+9
-4
@@ -32,10 +32,15 @@ import (
32
func (h Handler) handleUpgradeResponse(logger *zap.Logger, rw http.ResponseWriter, req *http.Request, res *http.Response) {
33
reqUpType := upgradeType(req.Header)
34
resUpType := upgradeType(res.Header)
35
- // TODO: Update to use "net/http/internal/ascii" once we bumped
36
- // the minimum Go version to 1.17.
37
- // See https://github.com/golang/go/commit/5c489514bc5e61ad9b5b07bd7d8ec65d66a0512a
38
- if reqUpType != resUpType {
35
+
36
+ // Taken from https://github.com/golang/go/commit/5c489514bc5e61ad9b5b07bd7d8ec65d66a0512a
37
+ // We know reqUpType is ASCII, it's checked by the caller.
38
+ if !asciiIsPrint(resUpType) {
39
+ h.logger.Debug("backend tried to switch to invalid protocol",
40
+ zap.String("backend_upgrade", resUpType))
41
+ return
42
+ }
43
+ if !asciiEqualFold(reqUpType, resUpType) {
44
h.logger.Debug("backend tried to switch to unexpected protocol via Upgrade header",
45
zap.String("backend_upgrade", resUpType),
46
zap.String("requested_upgrade", reqUpType))