Skip to content

Commit 87e735d

Browse files
authored
Merge branch 'master' into delint-ioutil
2 parents aaf94f4 + c6225e3 commit 87e735d

13 files changed

+124
-195
lines changed

Diff for: .github/workflows/ci.yml

+19-19
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
on:
22
push:
3-
branches: '**'
3+
branches: "**"
44
paths-ignore:
5-
- 'docs/**'
5+
- "docs/**"
66
pull_request:
7-
branches: '**'
7+
branches: "**"
88
paths-ignore:
9-
- 'docs/**'
9+
- "docs/**"
1010

1111
name: Test
1212
jobs:
@@ -20,23 +20,23 @@ jobs:
2020

2121
strategy:
2222
matrix:
23-
go-version: [1.15.x, 1.16.x, 1.17.x, 1.18.x, 1.19.x, 1.20.x, 1.21.x, 1.22.x, 1.23.x]
23+
go-version: [1.20.x, 1.21.x, 1.22.x, 1.23.x, 1.24.x]
2424
os: [ubuntu-latest, windows-latest]
2525

2626
runs-on: ${{ matrix.os }}
2727

2828
steps:
29-
- name: Install Go
30-
uses: actions/setup-go@v5
31-
with:
32-
go-version: ${{ matrix.go-version }}
33-
check-latest: true
34-
cache: false
35-
- name: Checkout code
36-
uses: actions/checkout@v4
37-
with:
38-
path: ${{ env.GOPATH }}/src/github.com/${{ github.repository }}
39-
- name: Test
40-
run: |
41-
go get -d -t ./...
42-
make test
29+
- name: Install Go
30+
uses: actions/setup-go@v5
31+
with:
32+
go-version: ${{ matrix.go-version }}
33+
check-latest: true
34+
cache: false
35+
- name: Checkout code
36+
uses: actions/checkout@v4
37+
with:
38+
path: ${{ env.GOPATH }}/src/github.com/${{ github.repository }}
39+
- name: Test
40+
run: |
41+
go get -d -t ./...
42+
make test

Diff for: go.mod

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
module github.com/go-chi/chi/v5
22

3-
go 1.16
3+
// Chi supports the four most recent major versions of Go.
4+
// See https://github.com/go-chi/chi/issues/963.
5+
go 1.20

Diff for: middleware/compress.go

+2-5
Original file line numberDiff line numberDiff line change
@@ -274,16 +274,13 @@ type compressResponseWriter struct {
274274
func (cw *compressResponseWriter) isCompressible() bool {
275275
// Parse the first part of the Content-Type response header.
276276
contentType := cw.Header().Get("Content-Type")
277-
if idx := strings.Index(contentType, ";"); idx >= 0 {
278-
contentType = contentType[0:idx]
279-
}
277+
contentType, _, _ = strings.Cut(contentType, ";")
280278

281279
// Is the content type compressible?
282280
if _, ok := cw.contentTypes[contentType]; ok {
283281
return true
284282
}
285-
if idx := strings.Index(contentType, "/"); idx > 0 {
286-
contentType = contentType[0:idx]
283+
if contentType, _, hadSlash := strings.Cut(contentType, "/"); hadSlash {
287284
_, ok := cw.contentWildcards[contentType]
288285
return ok
289286
}

Diff for: middleware/get_head_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -38,27 +38,27 @@ func TestGetHead(t *testing.T) {
3838
defer ts.Close()
3939

4040
if _, body := testRequest(t, ts, "GET", "/hi", nil); body != "bye" {
41-
t.Fatalf(body)
41+
t.Fatal(body)
4242
}
4343
if req, body := testRequest(t, ts, "HEAD", "/hi", nil); body != "" || req.Header.Get("X-Test") != "yes" {
44-
t.Fatalf(body)
44+
t.Fatal(body)
4545
}
4646
if _, body := testRequest(t, ts, "GET", "/", nil); body != "404 page not found\n" {
47-
t.Fatalf(body)
47+
t.Fatal(body)
4848
}
4949
if req, body := testRequest(t, ts, "HEAD", "/", nil); body != "" || req.StatusCode != 404 {
50-
t.Fatalf(body)
50+
t.Fatal(body)
5151
}
5252

5353
if _, body := testRequest(t, ts, "GET", "/articles/5", nil); body != "article:5" {
54-
t.Fatalf(body)
54+
t.Fatal(body)
5555
}
5656
if req, body := testRequest(t, ts, "HEAD", "/articles/5", nil); body != "" || req.Header.Get("X-Article") != "5" {
5757
t.Fatalf("expecting X-Article header '5' but got '%s'", req.Header.Get("X-Article"))
5858
}
5959

6060
if _, body := testRequest(t, ts, "GET", "/users/1", nil); body != "user:1" {
61-
t.Fatalf(body)
61+
t.Fatal(body)
6262
}
6363
if req, body := testRequest(t, ts, "HEAD", "/users/1", nil); body != "" || req.Header.Get("X-User") != "-" {
6464
t.Fatalf("expecting X-User header '-' but got '%s'", req.Header.Get("X-User"))

Diff for: middleware/realip.go

+16-33
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@ import (
99
"strings"
1010
)
1111

12-
var defaultHeaders = []string{
13-
"True-Client-IP", // Cloudflare Enterprise plan
14-
"X-Real-IP",
15-
"X-Forwarded-For",
16-
}
12+
var trueClientIP = http.CanonicalHeaderKey("True-Client-IP")
13+
var xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
14+
var xRealIP = http.CanonicalHeaderKey("X-Real-IP")
1715

1816
// RealIP is a middleware that sets a http.Request's RemoteAddr to the results
1917
// of parsing either the True-Client-IP, X-Real-IP or the X-Forwarded-For headers
@@ -32,7 +30,7 @@ var defaultHeaders = []string{
3230
// how you're using RemoteAddr, vulnerable to an attack of some sort).
3331
func RealIP(h http.Handler) http.Handler {
3432
fn := func(w http.ResponseWriter, r *http.Request) {
35-
if rip := getRealIP(r, defaultHeaders); rip != "" {
33+
if rip := realIP(r); rip != "" {
3634
r.RemoteAddr = rip
3735
}
3836
h.ServeHTTP(w, r)
@@ -41,33 +39,18 @@ func RealIP(h http.Handler) http.Handler {
4139
return http.HandlerFunc(fn)
4240
}
4341

44-
// RealIPFromHeaders is a middleware that sets a http.Request's RemoteAddr to the results
45-
// of parsing the custom headers.
46-
//
47-
// usage:
48-
// r.Use(RealIPFromHeaders("CF-Connecting-IP"))
49-
func RealIPFromHeaders(headers ...string) func(http.Handler) http.Handler {
50-
f := func(h http.Handler) http.Handler {
51-
fn := func(w http.ResponseWriter, r *http.Request) {
52-
if rip := getRealIP(r, headers); rip != "" {
53-
r.RemoteAddr = rip
54-
}
55-
h.ServeHTTP(w, r)
56-
}
57-
return http.HandlerFunc(fn)
58-
}
59-
return f
60-
}
42+
func realIP(r *http.Request) string {
43+
var ip string
6144

62-
func getRealIP(r *http.Request, headers []string) string {
63-
for _, header := range headers {
64-
if ip := r.Header.Get(header); ip != "" {
65-
ips := strings.Split(ip, ",")
66-
if ips[0] == "" || net.ParseIP(ips[0]) == nil {
67-
continue
68-
}
69-
return ips[0]
70-
}
45+
if tcip := r.Header.Get(trueClientIP); tcip != "" {
46+
ip = tcip
47+
} else if xrip := r.Header.Get(xRealIP); xrip != "" {
48+
ip = xrip
49+
} else if xff := r.Header.Get(xForwardedFor); xff != "" {
50+
ip, _, _ = strings.Cut(xff, ",")
51+
}
52+
if ip == "" || net.ParseIP(ip) == nil {
53+
return ""
7154
}
72-
return ""
55+
return ip
7356
}

Diff for: middleware/realip_test.go

-49
Original file line numberDiff line numberDiff line change
@@ -113,52 +113,3 @@ func TestInvalidIP(t *testing.T) {
113113
t.Fatal("Invalid IP used.")
114114
}
115115
}
116-
117-
func TestCustomIPHeader(t *testing.T) {
118-
var customHeaderKey = "X-CUSTOM-IP"
119-
req, _ := http.NewRequest("GET", "/", nil)
120-
req.Header.Add(customHeaderKey, "100.100.100.100")
121-
w := httptest.NewRecorder()
122-
123-
r := chi.NewRouter()
124-
r.Use(RealIPFromHeaders(customHeaderKey))
125-
126-
realIP := ""
127-
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
128-
realIP = r.RemoteAddr
129-
w.Write([]byte("Hello World"))
130-
})
131-
r.ServeHTTP(w, req)
132-
133-
if w.Code != 200 {
134-
t.Fatal("Response Code should be 200")
135-
}
136-
137-
if realIP != "100.100.100.100" {
138-
t.Fatal("Test get real IP precedence error.")
139-
}
140-
}
141-
142-
func TestCustomIPHeaderWithoutDefault(t *testing.T) {
143-
req, _ := http.NewRequest("GET", "/", nil)
144-
req.Header.Add("X-REAL-IP", "100.100.100.100")
145-
w := httptest.NewRecorder()
146-
147-
r := chi.NewRouter()
148-
r.Use(RealIPFromHeaders("CF-Connecting-IP"))
149-
150-
realIP := ""
151-
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
152-
realIP = r.RemoteAddr
153-
w.Write([]byte("Hello World"))
154-
})
155-
r.ServeHTTP(w, req)
156-
157-
if w.Code != 200 {
158-
t.Fatal("Response Code should be 200")
159-
}
160-
161-
if realIP != "" {
162-
t.Fatal("Invalid IP used.")
163-
}
164-
}

Diff for: middleware/route_headers.go

+1-7
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,7 @@ type Pattern struct {
133133

134134
func NewPattern(value string) Pattern {
135135
p := Pattern{}
136-
if i := strings.IndexByte(value, '*'); i >= 0 {
137-
p.wildcard = true
138-
p.prefix = value[0:i]
139-
p.suffix = value[i+1:]
140-
} else {
141-
p.prefix = value
142-
}
136+
p.prefix, p.suffix, p.wildcard = strings.Cut(value, "*")
143137
return p
144138
}
145139

Diff for: middleware/throttle.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,9 @@ type token struct{}
126126
type throttler struct {
127127
tokens chan token
128128
backlogTokens chan token
129+
retryAfterFn func(ctxDone bool) time.Duration
129130
backlogTimeout time.Duration
130131
statusCode int
131-
retryAfterFn func(ctxDone bool) time.Duration
132132
}
133133

134134
// setRetryAfterHeaderIfNeeded sets Retry-After HTTP header if corresponding retryAfterFn option of throttler is initialized.

Diff for: middleware/url_format_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,16 @@ func TestURLFormat(t *testing.T) {
3636
defer ts.Close()
3737

3838
if _, resp := testRequest(t, ts, "GET", "/articles/1.json", nil); resp != "1" {
39-
t.Fatalf(resp)
39+
t.Fatal(resp)
4040
}
4141
if _, resp := testRequest(t, ts, "GET", "/articles/1.xml", nil); resp != "1" {
42-
t.Fatalf(resp)
42+
t.Fatal(resp)
4343
}
4444
if _, resp := testRequest(t, ts, "GET", "/samples/articles/samples.1.json", nil); resp != "1" {
45-
t.Fatalf(resp)
45+
t.Fatal(resp)
4646
}
4747
if _, resp := testRequest(t, ts, "GET", "/samples/articles/samples.1.xml", nil); resp != "1" {
48-
t.Fatalf(resp)
48+
t.Fatal(resp)
4949
}
5050
}
5151

@@ -64,6 +64,6 @@ func TestURLFormatInSubRouter(t *testing.T) {
6464
defer ts.Close()
6565

6666
if _, resp := testRequest(t, ts, "GET", "/articles/1/subroute.json", nil); resp != "1" {
67-
t.Fatalf(resp)
67+
t.Fatal(resp)
6868
}
6969
}

Diff for: middleware/wrap_writer.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,19 @@ type WrapResponseWriter interface {
7272
// http.ResponseWriter interface.
7373
type basicWriter struct {
7474
http.ResponseWriter
75-
wroteHeader bool
75+
tee io.Writer
7676
code int
7777
bytes int
78-
tee io.Writer
78+
wroteHeader bool
7979
discard bool
8080
}
8181

8282
func (b *basicWriter) WriteHeader(code int) {
83-
if !b.wroteHeader {
83+
if code >= 100 && code <= 199 && code != http.StatusSwitchingProtocols {
84+
if !b.discard {
85+
b.ResponseWriter.WriteHeader(code)
86+
}
87+
} else if !b.wroteHeader {
8488
b.code = code
8589
b.wroteHeader = true
8690
if !b.discard {

0 commit comments

Comments
 (0)