-
Notifications
You must be signed in to change notification settings - Fork 69
/
http.go
139 lines (124 loc) · 3.44 KB
/
http.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Package http implements crazy ideas for http optimizations that should be
// mostly std compatible.
package http
import (
"bytes"
"context"
"crypto/tls"
"io"
"mime/multipart"
"net/http"
"net/url"
"strings"
"unsafe"
"github.com/go-faster/errors"
)
// httpRequest is copied version of http.Request structure.
type httpRequest struct {
Method string
Proto string // "HTTP/1.0"
URL *url.URL
ProtoMajor int // 1
ProtoMinor int // 0
Header http.Header
Body io.ReadCloser
GetBody func() (io.ReadCloser, error)
ContentLength int64
TransferEncoding []string
Close bool
Host string
Form url.Values
PostForm url.Values
MultipartForm *multipart.Form
Trailer http.Header
RemoteAddr string
RequestURI string
TLS *tls.ConnectionState
Cancel <-chan struct{}
Response *http.Response
ctx context.Context
}
func toPrivate(req *http.Request) *httpRequest {
return (*httpRequest)(unsafe.Pointer(req))
}
// Set sets request context without shallow copy of request.
func Set(req *http.Request, ctx context.Context) {
p := toPrivate(req)
p.ctx = ctx
}
// SetValue wraps context.WithValue call on request context.
func SetValue(req *http.Request, k, v interface{}) {
ctx := context.WithValue(req.Context(), k, v)
Set(req, ctx)
}
func init() {
// Explicitly check that structures have at least equal size.
stdSize := unsafe.Sizeof(http.Request{})
gotSize := unsafe.Sizeof(httpRequest{})
if stdSize != gotSize {
panic(errors.Errorf("%d (net/http) != %d", stdSize, gotSize))
}
}
// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
// return true if the string includes a port.
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
// removeEmptyPort strips the empty port in ":port" to ""
// as mandated by RFC 3986 Section 6.2.3.
func removeEmptyPort(host string) string {
if hasPort(host) {
return strings.TrimSuffix(host, ":")
}
return host
}
// NewRequest is optimized version of http.NewRequestWithContext.
func NewRequest(ctx context.Context, method string, u *url.URL, body io.Reader) *http.Request {
req := AcquireRequest()
Set(req, ctx)
rc, ok := body.(io.ReadCloser)
if !ok && body != nil {
rc = io.NopCloser(body)
}
// The host's colon:port should be normalized. See Issue 14836.
u.Host = removeEmptyPort(u.Host)
req.Proto = "HTTP/1.1"
req.ProtoMajor = 1
req.ProtoMinor = 1
if req.Header == nil {
req.Header = make(http.Header)
}
req.Body = rc
req.Host = u.Host
req.Method = method
req.URL = u
if body != nil {
switch v := body.(type) {
case *bytes.Buffer:
req.ContentLength = int64(v.Len())
buf := v.Bytes()
req.GetBody = func() (io.ReadCloser, error) {
r := bytes.NewReader(buf)
return io.NopCloser(r), nil
}
case *bytes.Reader:
req.ContentLength = int64(v.Len())
snapshot := *v
req.GetBody = func() (io.ReadCloser, error) {
r := snapshot
return io.NopCloser(&r), nil
}
case *strings.Reader:
req.ContentLength = int64(v.Len())
snapshot := *v
req.GetBody = func() (io.ReadCloser, error) {
r := snapshot
return io.NopCloser(&r), nil
}
default:
}
if req.GetBody != nil && req.ContentLength == 0 {
req.Body = http.NoBody
req.GetBody = func() (io.ReadCloser, error) { return http.NoBody, nil }
}
}
return req
}