podman

Форк
0
240 строк · 7.0 Кб
1
// Copyright 2018 The Go Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
4

5
// Package h2c implements the unencrypted "h2c" form of HTTP/2.
6
//
7
// The h2c protocol is the non-TLS version of HTTP/2 which is not available from
8
// net/http or golang.org/x/net/http2.
9
package h2c
10

11
import (
12
	"bufio"
13
	"bytes"
14
	"encoding/base64"
15
	"errors"
16
	"fmt"
17
	"io"
18
	"log"
19
	"net"
20
	"net/http"
21
	"net/textproto"
22
	"os"
23
	"strings"
24

25
	"golang.org/x/net/http/httpguts"
26
	"golang.org/x/net/http2"
27
)
28

29
var (
30
	http2VerboseLogs bool
31
)
32

33
func init() {
34
	e := os.Getenv("GODEBUG")
35
	if strings.Contains(e, "http2debug=1") || strings.Contains(e, "http2debug=2") {
36
		http2VerboseLogs = true
37
	}
38
}
39

40
// h2cHandler is a Handler which implements h2c by hijacking the HTTP/1 traffic
41
// that should be h2c traffic. There are two ways to begin a h2c connection
42
// (RFC 7540 Section 3.2 and 3.4): (1) Starting with Prior Knowledge - this
43
// works by starting an h2c connection with a string of bytes that is valid
44
// HTTP/1, but unlikely to occur in practice and (2) Upgrading from HTTP/1 to
45
// h2c - this works by using the HTTP/1 Upgrade header to request an upgrade to
46
// h2c. When either of those situations occur we hijack the HTTP/1 connection,
47
// convert it to an HTTP/2 connection and pass the net.Conn to http2.ServeConn.
48
type h2cHandler struct {
49
	Handler http.Handler
50
	s       *http2.Server
51
}
52

53
// NewHandler returns an http.Handler that wraps h, intercepting any h2c
54
// traffic. If a request is an h2c connection, it's hijacked and redirected to
55
// s.ServeConn. Otherwise the returned Handler just forwards requests to h. This
56
// works because h2c is designed to be parseable as valid HTTP/1, but ignored by
57
// any HTTP server that does not handle h2c. Therefore we leverage the HTTP/1
58
// compatible parts of the Go http library to parse and recognize h2c requests.
59
// Once a request is recognized as h2c, we hijack the connection and convert it
60
// to an HTTP/2 connection which is understandable to s.ServeConn. (s.ServeConn
61
// understands HTTP/2 except for the h2c part of it.)
62
//
63
// The first request on an h2c connection is read entirely into memory before
64
// the Handler is called. To limit the memory consumed by this request, wrap
65
// the result of NewHandler in an http.MaxBytesHandler.
66
func NewHandler(h http.Handler, s *http2.Server) http.Handler {
67
	return &h2cHandler{
68
		Handler: h,
69
		s:       s,
70
	}
71
}
72

73
// extractServer extracts existing http.Server instance from http.Request or create an empty http.Server
74
func extractServer(r *http.Request) *http.Server {
75
	server, ok := r.Context().Value(http.ServerContextKey).(*http.Server)
76
	if ok {
77
		return server
78
	}
79
	return new(http.Server)
80
}
81

82
// ServeHTTP implement the h2c support that is enabled by h2c.GetH2CHandler.
83
func (s h2cHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
84
	// Handle h2c with prior knowledge (RFC 7540 Section 3.4)
85
	if r.Method == "PRI" && len(r.Header) == 0 && r.URL.Path == "*" && r.Proto == "HTTP/2.0" {
86
		if http2VerboseLogs {
87
			log.Print("h2c: attempting h2c with prior knowledge.")
88
		}
89
		conn, err := initH2CWithPriorKnowledge(w)
90
		if err != nil {
91
			if http2VerboseLogs {
92
				log.Printf("h2c: error h2c with prior knowledge: %v", err)
93
			}
94
			return
95
		}
96
		defer conn.Close()
97
		s.s.ServeConn(conn, &http2.ServeConnOpts{
98
			Context:          r.Context(),
99
			BaseConfig:       extractServer(r),
100
			Handler:          s.Handler,
101
			SawClientPreface: true,
102
		})
103
		return
104
	}
105
	// Handle Upgrade to h2c (RFC 7540 Section 3.2)
106
	if isH2CUpgrade(r.Header) {
107
		conn, settings, err := h2cUpgrade(w, r)
108
		if err != nil {
109
			if http2VerboseLogs {
110
				log.Printf("h2c: error h2c upgrade: %v", err)
111
			}
112
			w.WriteHeader(http.StatusInternalServerError)
113
			return
114
		}
115
		defer conn.Close()
116
		s.s.ServeConn(conn, &http2.ServeConnOpts{
117
			Context:        r.Context(),
118
			BaseConfig:     extractServer(r),
119
			Handler:        s.Handler,
120
			UpgradeRequest: r,
121
			Settings:       settings,
122
		})
123
		return
124
	}
125
	s.Handler.ServeHTTP(w, r)
126
	return
127
}
128

129
// initH2CWithPriorKnowledge implements creating a h2c connection with prior
130
// knowledge (Section 3.4) and creates a net.Conn suitable for http2.ServeConn.
131
// All we have to do is look for the client preface that is suppose to be part
132
// of the body, and reforward the client preface on the net.Conn this function
133
// creates.
134
func initH2CWithPriorKnowledge(w http.ResponseWriter) (net.Conn, error) {
135
	hijacker, ok := w.(http.Hijacker)
136
	if !ok {
137
		return nil, errors.New("h2c: connection does not support Hijack")
138
	}
139
	conn, rw, err := hijacker.Hijack()
140
	if err != nil {
141
		return nil, err
142
	}
143

144
	const expectedBody = "SM\r\n\r\n"
145

146
	buf := make([]byte, len(expectedBody))
147
	n, err := io.ReadFull(rw, buf)
148
	if err != nil {
149
		return nil, fmt.Errorf("h2c: error reading client preface: %s", err)
150
	}
151

152
	if string(buf[:n]) == expectedBody {
153
		return newBufConn(conn, rw), nil
154
	}
155

156
	conn.Close()
157
	return nil, errors.New("h2c: invalid client preface")
158
}
159

160
// h2cUpgrade establishes a h2c connection using the HTTP/1 upgrade (Section 3.2).
161
func h2cUpgrade(w http.ResponseWriter, r *http.Request) (_ net.Conn, settings []byte, err error) {
162
	settings, err = getH2Settings(r.Header)
163
	if err != nil {
164
		return nil, nil, err
165
	}
166
	hijacker, ok := w.(http.Hijacker)
167
	if !ok {
168
		return nil, nil, errors.New("h2c: connection does not support Hijack")
169
	}
170

171
	body, err := io.ReadAll(r.Body)
172
	if err != nil {
173
		return nil, nil, err
174
	}
175
	r.Body = io.NopCloser(bytes.NewBuffer(body))
176

177
	conn, rw, err := hijacker.Hijack()
178
	if err != nil {
179
		return nil, nil, err
180
	}
181

182
	rw.Write([]byte("HTTP/1.1 101 Switching Protocols\r\n" +
183
		"Connection: Upgrade\r\n" +
184
		"Upgrade: h2c\r\n\r\n"))
185
	return newBufConn(conn, rw), settings, nil
186
}
187

188
// isH2CUpgrade returns true if the header properly request an upgrade to h2c
189
// as specified by Section 3.2.
190
func isH2CUpgrade(h http.Header) bool {
191
	return httpguts.HeaderValuesContainsToken(h[textproto.CanonicalMIMEHeaderKey("Upgrade")], "h2c") &&
192
		httpguts.HeaderValuesContainsToken(h[textproto.CanonicalMIMEHeaderKey("Connection")], "HTTP2-Settings")
193
}
194

195
// getH2Settings returns the settings in the HTTP2-Settings header.
196
func getH2Settings(h http.Header) ([]byte, error) {
197
	vals, ok := h[textproto.CanonicalMIMEHeaderKey("HTTP2-Settings")]
198
	if !ok {
199
		return nil, errors.New("missing HTTP2-Settings header")
200
	}
201
	if len(vals) != 1 {
202
		return nil, fmt.Errorf("expected 1 HTTP2-Settings. Got: %v", vals)
203
	}
204
	settings, err := base64.RawURLEncoding.DecodeString(vals[0])
205
	if err != nil {
206
		return nil, err
207
	}
208
	return settings, nil
209
}
210

211
func newBufConn(conn net.Conn, rw *bufio.ReadWriter) net.Conn {
212
	rw.Flush()
213
	if rw.Reader.Buffered() == 0 {
214
		// If there's no buffered data to be read,
215
		// we can just discard the bufio.ReadWriter.
216
		return conn
217
	}
218
	return &bufConn{conn, rw.Reader}
219
}
220

221
// bufConn wraps a net.Conn, but reads drain the bufio.Reader first.
222
type bufConn struct {
223
	net.Conn
224
	*bufio.Reader
225
}
226

227
func (c *bufConn) Read(p []byte) (int, error) {
228
	if c.Reader == nil {
229
		return c.Conn.Read(p)
230
	}
231
	n := c.Reader.Buffered()
232
	if n == 0 {
233
		c.Reader = nil
234
		return c.Conn.Read(p)
235
	}
236
	if n < len(p) {
237
		p = p[:n]
238
	}
239
	return c.Reader.Read(p)
240
}
241

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.