Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5d8c67e

Browse files
Alex Dyukovalexdyukov
Alex Dyukov
authored andcommitted
Fix: skip websocket connections and rewrite benchmarks
1 parent 5ec9238 commit 5d8c67e

File tree

7 files changed

+229
-106
lines changed

7 files changed

+229
-106
lines changed

‎.golangci.yml‎

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
run:
2+
timeout: 5m
3+
tests: true
4+
modules-download-mode: readonly
5+
6+
linters:
7+
enable-all: true
8+
disable:
9+
- funlen
10+
- nonamedreturns
11+
12+
linters-settings:
13+
errcheck:
14+
check-type-assertions: true
15+
check-blank: true
16+
gosimple:
17+
checks: ["all"]
18+
govet:
19+
enable-all: true
20+
settings:
21+
shadow:
22+
strict: true
23+
staticcheck:
24+
checks: ["all"]
25+
decorder:
26+
ignore-underscore-vars: false
27+
disable-dec-order-check: false
28+
disable-init-func-first-check: false
29+
disable-dec-num-check: false
30+
disable-type-dec-num-check: false
31+
disable-const-dec-num-check: false
32+
disable-var-dec-num-check: false
33+
dogsled:
34+
max-blank-identifiers: 1
35+
errchkjson:
36+
report-no-exported: true
37+
gocognit:
38+
min-complexity: 20
39+
gocritic:
40+
enabled-tags:
41+
- diagnostic
42+
- style
43+
- performance
44+
settings:
45+
captLocal:
46+
paramsOnly: false
47+
hugeParam:
48+
sizeThreshold: 40
49+
gofumpt:
50+
extra-rules: true
51+
gomoddirectives:
52+
replace-local: true
53+
gosec:
54+
config:
55+
global:
56+
audit: true
57+
grouper:
58+
const-require-single-const: true
59+
const-require-grouping: true
60+
import-require-single-import: true
61+
import-require-grouping: true
62+
type-require-single-type: true
63+
type-require-grouping: false
64+
var-require-single-var: true
65+
var-require-grouping: false
66+
varnamelen:
67+
check-receiver: true
68+
check-return: true
69+
check-type-param: true
70+
ignore-type-assert-ok: true
71+
ignore-map-index-ok: true
72+
ignore-chan-recv-ok: true
73+
ignore-names:
74+
- err
75+
whitespace:
76+
multi-if: true
77+
multi-func: true
78+
depguard:
79+
rules:
80+
main:
81+
files:
82+
- "$all"
83+
- "!$test"
84+
allow:
85+
- "$gostd"

‎README.md‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,21 @@ According to RFCs there is no 'Accept-Encoding' header at server side response.
88

99
## Benchmarks
1010

11-
There is a little overhead to compare to `if strings.Contains(request.Header.Get("Accept-Encoding"), "myencoding")`
11+
There is a little overhead to compare to `if strings.Contains(request.Header.Get("Accept-Encoding"), "myencoding")`:
1212
```
1313
$ go test -bench=. -benchmem -benchtime=10000000x
1414
warning: GOPATH set to GOROOT (/home/user/go) has no effect
1515
goos: linux
1616
goarch: amd64
1717
pkg: github.com/alexdyukov/httpencoder
1818
cpu: AMD Ryzen 7 8845H w/ Radeon 780M Graphics
19-
BenchmarkRaw-16 10000000 226.3 ns/op 720 B/op 5 allocs/op
20-
BenchmarkRawEncode-16 10000000 251.3 ns/op 720 B/op 5 allocs/op
21-
BenchmarkWrappedEncodeDecode-16 10000000 905.7 ns/op 1537 B/op 13 allocs/op
22-
BenchmarkWrappedDecode-16 10000000 252.2 ns/op 720 B/op 5 allocs/op
23-
BenchmarkWrappedEncode-16 10000000 876.4 ns/op 1537 B/op 13 allocs/op
19+
BenchmarkRaw-16 10000000 200.7 ns/op 720 B/op 5 allocs/op
20+
BenchmarkIfedEncode-16 10000000 489.1 ns/op 1456 B/op 9 allocs/op
21+
BenchmarkWrappedEncodeDecode-16 10000000 1083 ns/op 1569 B/op 15 allocs/op
22+
BenchmarkWrappedDecode-16 10000000 391.4 ns/op 752 B/op 7 allocs/op
23+
BenchmarkWrappedEncode-16 10000000 917.2 ns/op 1537 B/op 13 allocs/op
2424
PASS
25-
ok github.com/alexdyukov/httpencoder 25.135s
25+
ok github.com/alexdyukov/httpencoder 30.844s
2626
```
2727

2828
## Examples
@@ -79,4 +79,4 @@ func (gzipper) Decode(ctx context.Context, to io.Writer, from []byte) (err error
7979

8080
## License
8181

82-
MIT licensed. See the included LICENSE file for details.
82+
MIT licensed. See the included LICENSE file for details.

‎benchmark_test.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func BenchmarkRaw(b *testing.B) {
2424
}
2525
}
2626

27-
func BenchmarkRawEncode(b *testing.B) {
27+
func BenchmarkIfedEncode(b *testing.B) {
2828
b.StopTimer()
2929

3030
body := bytes.NewBufferString("abcd")
@@ -46,7 +46,6 @@ func BenchmarkWrappedEncodeDecode(b *testing.B) {
4646
body := bytes.NewBufferString("aabbccdd")
4747
request := httptest.NewRequest(http.MethodPost, "/", body)
4848
request.Header.Set("Accept-Encoding", "repeate")
49-
request.Header.Set("Content-Encoding", "repeate")
5049

5150
encoders := map[string]httpencoder.Encoder{"repeate": repeaterEntity}
5251
decoders := map[string]httpencoder.Decoder{"repeate": repeaterEntity}
@@ -56,6 +55,7 @@ func BenchmarkWrappedEncodeDecode(b *testing.B) {
5655
b.StartTimer()
5756

5857
for i := 0; i < b.N; i++ {
58+
request.Header.Set("Content-Encoding", "repeate")
5959
handler.ServeHTTP(httptest.NewRecorder(), request)
6060
}
6161
}
@@ -65,7 +65,6 @@ func BenchmarkWrappedDecode(b *testing.B) {
6565

6666
body := bytes.NewBufferString("aabbccdd")
6767
request := httptest.NewRequest(http.MethodPost, "/", body)
68-
request.Header.Set("Content-Encoding", "repeate")
6968

7069
encoders := map[string]httpencoder.Encoder{}
7170
decoders := map[string]httpencoder.Decoder{"repeate": repeaterEntity}
@@ -75,6 +74,7 @@ func BenchmarkWrappedDecode(b *testing.B) {
7574
b.StartTimer()
7675

7776
for i := 0; i < b.N; i++ {
77+
request.Header.Set("Content-Encoding", "repeate")
7878
handler.ServeHTTP(httptest.NewRecorder(), request)
7979
}
8080
}

‎decode.go‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ import (
77
)
88

99
func decode(bufferPool *sync.Pool, decoders map[string]Decoder, next http.Handler) http.Handler {
10-
if len(decoders) == 0 {
11-
return next
12-
}
13-
1410
return http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
1511
header := compactAndLow([]byte(request.Header.Get("Content-Encoding")))
1612
if len(header) == 0 {

‎encode.go‎

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,21 @@ func (responseWriter *wrappedWriter) WriteHeader(statusCode int) {
2121
*(responseWriter.statusCode) = statusCode
2222
}
2323

24-
funcencode(bufferPool*sync.Pool, encodersmap[string]Encoder, next http.Handler) http.Handler {
25-
iflen(encoders) ==0 {
26-
returnnext
27-
}
24+
// wrappedWriter doesnt support Flush method
25+
// because its hard to implement Encoder with partial responses.
26+
// func (*wrappedWriter) Flush() {
27+
// }
2828

29+
func encode(bufferPool *sync.Pool, encoders map[string]Encoder, next http.Handler) http.Handler {
2930
return http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
3031
header := compactAndLow([]byte(request.Header.Get("Accept-Encoding")))
31-
if len(header) == 0 {
32+
if len(header) == 0 ||request.Header.Get("Upgrade") !=""{
3233
next.ServeHTTP(responseWriter, request)
3334

3435
return
3536
}
3637

37-
encoder, okay := getEncode(header, encoders)
38+
encoder, encodingType, okay := getPreferedEncoder(header, encoders)
3839
if !okay {
3940
next.ServeHTTP(responseWriter, request)
4041

@@ -54,11 +55,21 @@ func encode(bufferPool *sync.Pool, encoders map[string]Encoder, next http.Handle
5455

5556
upstreamResponseBody := upstreamResponse.Bytes()
5657

58+
if responseWriter.Header().Get("Content-Encoding") != "" { // already encoded
59+
responseWriter.WriteHeader(statusCode)
60+
61+
if _, err := responseWriter.Write(upstreamResponseBody); err != nil {
62+
http.Error(responseWriter, err.Error(), http.StatusInternalServerError)
63+
}
64+
65+
return
66+
}
67+
5768
if responseWriter.Header().Get("Content-Type") == "" {
5869
responseWriter.Header().Set("Content-Type", http.DetectContentType(upstreamResponseBody))
5970
}
6071

61-
responseWriter.Header().Set("Content-Encoding", encoder.String())
72+
responseWriter.Header().Set("Content-Encoding", encodingType)
6273
responseWriter.Header().Del("Content-Length")
6374
responseWriter.WriteHeader(statusCode)
6475

@@ -70,40 +81,38 @@ func encode(bufferPool *sync.Pool, encoders map[string]Encoder, next http.Handle
7081
})
7182
}
7283

73-
func getEncode(acceptEncodingHeader []byte, encoders map[string]Encoder) (Encoder, bool) {
84+
//nolint:ireturn // helper function
85+
func getPreferedEncoder(acceptEncodingHeader []byte, encoders map[string]Encoder) (Encoder, string, bool) {
7486
var (
75-
preferedEncodeFunc Encoder
76-
preferedQuality int
77-
found = false
78-
encodingType string
79-
qualityValue int
87+
preferedEncodingFunc Encoder
88+
preferedEncodingType string
89+
preferedQuality int
90+
found = false
91+
encodingType string
92+
qualityValue int
8093
)
8194

8295
for pos := 0; pos < len(acceptEncodingHeader); pos++ {
83-
encodingType, pos = getNextEncodingType(acceptEncodingHeader, pos)
96+
encodingType, pos = getNextAcceptEncodingType(acceptEncodingHeader, pos)
8497
qualityValue, pos = getNextQualityValue(acceptEncodingHeader, pos)
8598

8699
encoder, exist := encoders[encodingType]
87100
if exist && preferedQuality < qualityValue {
88101
preferedQuality = qualityValue
89-
preferedEncodeFunc = encoder
90-
102+
preferedEncodingFunc = encoder
103+
preferedEncodingType=encodingType
91104
found = true
92105
}
93106
}
94107

95-
return preferedEncodeFunc, found
108+
return preferedEncodingFunc, preferedEncodingType, found
96109
}
97110

98-
func getNextEncodingType(header []byte, start int) (encodingType string, newPosition int) {
111+
func getNextAcceptEncodingType(header []byte, start int) (encodingType string, newPosition int) {
99112
for start < len(header) && !isAlpha(header[start]) {
100113
start++
101114
}
102115

103-
if start >= len(header) {
104-
return "", start
105-
}
106-
107116
end := start
108117

109118
for end < len(header) && isAlpha(header[end]) {

‎httpencoder.go‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ type (
1313
Encoder interface {
1414
// Encode encodes http.ResponseWriter body.
1515
Encode(ctx context.Context, to io.Writer, from []byte) error
16-
// String used to be set Content-Encoding field.
17-
String() string
1816
}
1917
// Decoder implements reader for http.Request body.
2018
Decoder interface {
@@ -37,9 +35,15 @@ func New(encoders map[string]Encoder, decoders map[string]Decoder) func(next htt
3735
}
3836

3937
return func(next http.Handler) http.Handler {
40-
decodedHandler := decode(bufferPool, decoders, next)
38+
if len(decoders) != 0 {
39+
next = decode(bufferPool, decoders, next)
40+
}
41+
42+
if len(encoders) != 0 {
43+
next = encode(bufferPool, encoders, next)
44+
}
4145

42-
return encode(bufferPool, encoders, decodedHandler)
46+
return next
4347
}
4448
}
4549

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /