@marcel91200 / caddy Mirror · read-only / commits / e4ce40f8
Fork 0

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))