-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Copy pathbody.go
142 lines (132 loc) · 3.36 KB
/
body.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
140
141
142
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.24
package http3
import (
"errors"
"fmt"
"io"
"sync"
)
// A bodyWriter writes a request or response body to a stream
// as a series of DATA frames.
type bodyWriter struct {
st *stream
remain int64 // -1 when content-length is not known
flush bool // flush the stream after every write
name string // "request" or "response"
}
func (w *bodyWriter) Write(p []byte) (n int, err error) {
if w.remain >= 0 && int64(len(p)) > w.remain {
return 0, &streamError{
code: errH3InternalError,
message: w.name + " body longer than specified content length",
}
}
w.st.writeVarint(int64(frameTypeData))
w.st.writeVarint(int64(len(p)))
n, err = w.st.Write(p)
if w.remain >= 0 {
w.remain -= int64(n)
}
if w.flush && err == nil {
err = w.st.Flush()
}
if err != nil {
err = fmt.Errorf("writing %v body: %w", w.name, err)
}
return n, err
}
func (w *bodyWriter) Close() error {
if w.remain > 0 {
return errors.New(w.name + " body shorter than specified content length")
}
return nil
}
// A bodyReader reads a request or response body from a stream.
type bodyReader struct {
st *stream
mu sync.Mutex
remain int64
err error
}
func (r *bodyReader) Read(p []byte) (n int, err error) {
// The HTTP/1 and HTTP/2 implementations both permit concurrent reads from a body,
// in the sense that the race detector won't complain.
// Use a mutex here to provide the same behavior.
r.mu.Lock()
defer r.mu.Unlock()
if r.err != nil {
return 0, r.err
}
defer func() {
if err != nil {
r.err = err
}
}()
if r.st.lim == 0 {
// We've finished reading the previous DATA frame, so end it.
if err := r.st.endFrame(); err != nil {
return 0, err
}
}
// Read the next DATA frame header,
// if we aren't already in the middle of one.
for r.st.lim < 0 {
ftype, err := r.st.readFrameHeader()
if err == io.EOF && r.remain > 0 {
return 0, &streamError{
code: errH3MessageError,
message: "body shorter than content-length",
}
}
if err != nil {
return 0, err
}
switch ftype {
case frameTypeData:
if r.remain >= 0 && r.st.lim > r.remain {
return 0, &streamError{
code: errH3MessageError,
message: "body longer than content-length",
}
}
// Fall out of the loop and process the frame body below.
case frameTypeHeaders:
// This HEADERS frame contains the message trailers.
if r.remain > 0 {
return 0, &streamError{
code: errH3MessageError,
message: "body shorter than content-length",
}
}
// TODO: Fill in Request.Trailer.
if err := r.st.discardFrame(); err != nil {
return 0, err
}
return 0, io.EOF
default:
if err := r.st.discardUnknownFrame(ftype); err != nil {
return 0, err
}
}
}
// We are now reading the content of a DATA frame.
// Fill the read buffer or read to the end of the frame,
// whichever comes first.
if int64(len(p)) > r.st.lim {
p = p[:r.st.lim]
}
n, err = r.st.Read(p)
if r.remain > 0 {
r.remain -= int64(n)
}
return n, err
}
func (r *bodyReader) Close() error {
// Unlike the HTTP/1 and HTTP/2 body readers (at the time of this comment being written),
// calling Close concurrently with Read will interrupt the read.
r.st.stream.CloseRead()
return nil
}