@marcel91200 / caddy Mirror · read-only
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 11, 02:15 UTC

master
go 134 lines 4.42 KB
Raw
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 package fastcgi
16
17 import (
18 "errors"
19 "net/http"
20 "strings"
21 "testing"
22
23 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
24 )
25
26 // TestDoRejectsUnusableContentLength pins what the client does with a
27 // CONTENT_LENGTH it cannot use, and which of those cases carries which message.
28 //
29 // Only "-1" is reachable through Transport.RoundTrip, which is the shape a
30 // chunked request takes: Get/Head/Options/Post all set CONTENT_LENGTH before
31 // Do sees it, from the r.ContentLength that net/http reports as -1 when it has
32 // no length. The absent and empty cases guard Do's own contract for any future
33 // caller; an absent key was the one that used to return a 411 with no error at
34 // all, while an empty value carried strconv's syntax error, true but useless
35 // to an operator. TestPostRejectsUnknownLengthWithRemedy covers the reachable
36 // path end to end.
37 func TestDoRejectsUnusableContentLength(t *testing.T) {
38 t.Parallel()
39
40 tests := []struct {
41 name string
42 env map[string]string
43 wantErr error
44 }{
45 {
46 name: "absent",
47 env: map[string]string{},
48 wantErr: errNoContentLength,
49 },
50 {
51 name: "empty",
52 env: map[string]string{"CONTENT_LENGTH": ""},
53 wantErr: errNoContentLength,
54 },
55 {
56 // The reported case: what a chunked request becomes by the time it
57 // reaches here, and the only one production can produce.
58 name: "negative, as a chunked request arrives",
59 env: map[string]string{"CONTENT_LENGTH": "-1"},
60 wantErr: errNoContentLength,
61 },
62 {
63 name: "not a number",
64 env: map[string]string{"CONTENT_LENGTH": "banana"},
65 wantErr: errBadContentLength,
66 },
67 }
68
69 for _, tc := range tests {
70 t.Run(tc.name, func(t *testing.T) {
71 t.Parallel()
72
73 c := &client{}
74 _, err := c.Do(tc.env, strings.NewReader("body"))
75 if err == nil {
76 t.Fatal("expected an error")
77 }
78
79 var handlerErr caddyhttp.HandlerError
80 if !errors.As(err, &handlerErr) {
81 t.Fatalf("error %v is not a caddyhttp.HandlerError", err)
82 }
83 if handlerErr.StatusCode != http.StatusLengthRequired {
84 t.Errorf("status = %d, want %d", handlerErr.StatusCode, http.StatusLengthRequired)
85 }
86 // The status alone is what sent people hunting through their
87 // backend logs, so the cause has to travel with it.
88 if handlerErr.Err == nil {
89 t.Fatal("the 411 carries no error to explain it")
90 }
91 if !errors.Is(err, tc.wantErr) {
92 t.Errorf("error %v does not wrap %v", err, tc.wantErr)
93 }
94 })
95 }
96 }
97
98 // TestNoContentLengthErrorNamesTheRemedy keeps the message actionable: the
99 // operator has to learn that buffering is what supplies the missing length.
100 func TestNoContentLengthErrorNamesTheRemedy(t *testing.T) {
101 t.Parallel()
102
103 if !strings.Contains(errNoContentLength.Error(), "request_buffers") {
104 t.Errorf("error %q does not name request_buffers as the remedy", errNoContentLength)
105 }
106 }
107
108 // TestPostRejectsUnknownLengthWithRemedy goes through the entry point RoundTrip
109 // actually uses. Transport.RoundTrip passes r.ContentLength straight to Post,
110 // and net/http reports -1 for a chunked body, so this is the exact call the
111 // reporter of #7386 made. Asserting it only against Do would let the message
112 // rot on a branch production never reaches.
113 func TestPostRejectsUnknownLengthWithRemedy(t *testing.T) {
114 t.Parallel()
115
116 c := &client{}
117 env := map[string]string{}
118
119 _, err := c.Post(env, http.MethodPost, "application/octet-stream", strings.NewReader("body"), -1)
120 if err == nil {
121 t.Fatal("expected an error")
122 }
123 if !errors.Is(err, errNoContentLength) {
124 t.Errorf("error %v does not wrap errNoContentLength", err)
125 }
126 if !strings.Contains(err.Error(), "request_buffers") {
127 t.Errorf("the error operators see, %q, does not name the remedy", err)
128 }
129
130 var handlerErr caddyhttp.HandlerError
131 if !errors.As(err, &handlerErr) || handlerErr.StatusCode != http.StatusLengthRequired {
132 t.Errorf("error %v is not a 411 HandlerError", err)
133 }
134 }