mirror of
https://github.com/tinygo-org/net.git
synced 2026-08-15 08:53:40 +00:00
Upgrade net package from Go 1.21.4 to Go 1.26.2
Backport upstream Go standard library changes to TinyGo's net package. Unmodified files (18) replaced directly from Go 1.26.2 source. Modified files (22) merged via 3-way diff, preserving all TinyGo netdev adaptations, TINYGO markers, and embedded-device constraints. Notable upstream changes included: - net/http: cookie handling improvements, fs enhancements, reverse proxy updates, chunked encoding fixes - net/http: ServeMux routing and pattern matching updates - net: IP parsing and MAC address handling improvements - Various doc link syntax modernization (e.g. [Dial], [Buffers]) TinyGo-only files (netdev.go, tlssock.go) unchanged. All TINYGO comment markers preserved.
This commit is contained in:
+58
-42
@@ -1,4 +1,4 @@
|
||||
// TINYGO: The following is copied and modified from Go 1.21.4 official implementation.
|
||||
// TINYGO: The following is copied and modified from Go 1.26.2 official implementation.
|
||||
|
||||
// TINYGO: Removed trace stuff
|
||||
// TINYGO: Hook readTransfer is onEOF callback to get notified when request of
|
||||
@@ -15,12 +15,14 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"internal/godebug"
|
||||
"io"
|
||||
"maps"
|
||||
"net/http/internal"
|
||||
"net/http/internal/ascii"
|
||||
"net/textproto"
|
||||
"reflect"
|
||||
"sort"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -313,7 +315,7 @@ func (t *transferWriter) writeHeader(w io.Writer) error {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
if len(keys) > 0 {
|
||||
sort.Strings(keys)
|
||||
slices.Sort(keys)
|
||||
// TODO: could do better allocation-wise here, but trailers are rare,
|
||||
// so being lazy for now.
|
||||
if _, err := io.WriteString(w, "Trailer: "+strings.Join(keys, ",")+"\r\n"); err != nil {
|
||||
@@ -342,7 +344,7 @@ func (t *transferWriter) writeBody(w io.Writer) (err error) {
|
||||
// nopCloser or readTrackingBody. This is to ensure that we can take advantage of
|
||||
// OS-level optimizations in the event that the body is an
|
||||
// *os.File.
|
||||
if t.Body != nil {
|
||||
if !t.ResponseToHEAD && t.Body != nil {
|
||||
var body = t.unwrapBody()
|
||||
if chunked(t.TransferEncoding) {
|
||||
if bw, ok := w.(*bufio.Writer); ok && !t.IsResponse {
|
||||
@@ -384,7 +386,7 @@ func (t *transferWriter) writeBody(w io.Writer) (err error) {
|
||||
t.ContentLength, ncopy)
|
||||
}
|
||||
|
||||
if chunked(t.TransferEncoding) {
|
||||
if !t.ResponseToHEAD && chunked(t.TransferEncoding) {
|
||||
// Write Trailer header
|
||||
if t.Trailer != nil {
|
||||
if err := t.Trailer.Write(w); err != nil {
|
||||
@@ -402,7 +404,10 @@ func (t *transferWriter) writeBody(w io.Writer) (err error) {
|
||||
//
|
||||
// This function is only intended for use in writeBody.
|
||||
func (t *transferWriter) doBodyCopy(dst io.Writer, src io.Reader) (n int64, err error) {
|
||||
n, err = io.Copy(dst, src)
|
||||
buf := getCopyBuf()
|
||||
defer putCopyBuf(buf)
|
||||
|
||||
n, err = io.CopyBuffer(dst, src, buf)
|
||||
if err != nil && err != io.EOF {
|
||||
t.bodyReadError = err
|
||||
}
|
||||
@@ -523,7 +528,7 @@ func readTransfer(msg any, r *bufio.Reader, onEOF func()) (err error) {
|
||||
return err
|
||||
}
|
||||
if isResponse && t.RequestMethod == "HEAD" {
|
||||
if n, err := parseContentLength(t.Header.get("Content-Length")); err != nil {
|
||||
if n, err := parseContentLength(t.Header["Content-Length"]); err != nil {
|
||||
return err
|
||||
} else {
|
||||
t.ContentLength = n
|
||||
@@ -642,19 +647,6 @@ func (t *transferReader) parseTransferEncoding() error {
|
||||
return &unsupportedTEError{fmt.Sprintf("unsupported transfer encoding: %q", raw[0])}
|
||||
}
|
||||
|
||||
// RFC 7230 3.3.2 says "A sender MUST NOT send a Content-Length header field
|
||||
// in any message that contains a Transfer-Encoding header field."
|
||||
//
|
||||
// but also: "If a message is received with both a Transfer-Encoding and a
|
||||
// Content-Length header field, the Transfer-Encoding overrides the
|
||||
// Content-Length. Such a message might indicate an attempt to perform
|
||||
// request smuggling (Section 9.5) or response splitting (Section 9.4) and
|
||||
// ought to be handled as an error. A sender MUST remove the received
|
||||
// Content-Length field prior to forwarding such a message downstream."
|
||||
//
|
||||
// Reportedly, these appear in the wild.
|
||||
delete(t.Header, "Content-Length")
|
||||
|
||||
t.Chunked = true
|
||||
return nil
|
||||
}
|
||||
@@ -662,7 +654,7 @@ func (t *transferReader) parseTransferEncoding() error {
|
||||
// Determine the expected body length, using RFC 7230 Section 3.3. This
|
||||
// function is not a method, because ultimately it should be shared by
|
||||
// ReadResponse and ReadRequest.
|
||||
func fixLength(isResponse bool, status int, requestMethod string, header Header, chunked bool) (int64, error) {
|
||||
func fixLength(isResponse bool, status int, requestMethod string, header Header, chunked bool) (n int64, err error) {
|
||||
isRequest := !isResponse
|
||||
contentLens := header["Content-Length"]
|
||||
|
||||
@@ -686,6 +678,14 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header,
|
||||
contentLens = header["Content-Length"]
|
||||
}
|
||||
|
||||
// Reject requests with invalid Content-Length headers.
|
||||
if len(contentLens) > 0 {
|
||||
n, err = parseContentLength(contentLens)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
|
||||
// Logic based on response type or status
|
||||
if isResponse && noResponseBodyExpected(requestMethod) {
|
||||
return 0, nil
|
||||
@@ -698,23 +698,29 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header,
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// According to RFC 9112, "If a message is received with both a
|
||||
// Transfer-Encoding and a Content-Length header field, the Transfer-Encoding
|
||||
// overrides the Content-Length. Such a message might indicate an attempt to
|
||||
// perform request smuggling (Section 11.2) or response splitting (Section 11.1)
|
||||
// and ought to be handled as an error. An intermediary that chooses to forward
|
||||
// the message MUST first remove the received Content-Length field and process
|
||||
// the Transfer-Encoding (as described below) prior to forwarding the message downstream."
|
||||
//
|
||||
// Chunked-encoding requests with either valid Content-Length
|
||||
// headers or no Content-Length headers are accepted after removing
|
||||
// the Content-Length field from header.
|
||||
//
|
||||
// Logic based on Transfer-Encoding
|
||||
if chunked {
|
||||
header.Del("Content-Length")
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
// Logic based on Content-Length
|
||||
var cl string
|
||||
if len(contentLens) == 1 {
|
||||
cl = textproto.TrimString(contentLens[0])
|
||||
}
|
||||
if cl != "" {
|
||||
n, err := parseContentLength(cl)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if len(contentLens) > 0 {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
header.Del("Content-Length")
|
||||
|
||||
if isRequest {
|
||||
@@ -812,10 +818,10 @@ type body struct {
|
||||
onHitEOF func() // if non-nil, func to call when EOF is Read
|
||||
}
|
||||
|
||||
// ErrBodyReadAfterClose is returned when reading a Request or Response
|
||||
// ErrBodyReadAfterClose is returned when reading a [Request] or [Response]
|
||||
// Body after the body has been closed. This typically happens when the body is
|
||||
// read after an HTTP Handler calls WriteHeader or Write on its
|
||||
// ResponseWriter.
|
||||
// read after an HTTP [Handler] calls WriteHeader or Write on its
|
||||
// [ResponseWriter].
|
||||
var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed Body")
|
||||
|
||||
func (b *body) Read(p []byte) (n int, err error) {
|
||||
@@ -945,9 +951,7 @@ func mergeSetHeader(dst *Header, src Header) {
|
||||
*dst = src
|
||||
return
|
||||
}
|
||||
for k, vv := range src {
|
||||
(*dst)[k] = vv
|
||||
}
|
||||
maps.Copy(*dst, src)
|
||||
}
|
||||
|
||||
// unreadDataSizeLocked returns the number of bytes of unread input.
|
||||
@@ -1034,19 +1038,31 @@ func (bl bodyLocked) Read(p []byte) (n int, err error) {
|
||||
return bl.b.readLocked(p)
|
||||
}
|
||||
|
||||
// parseContentLength trims whitespace from s and returns -1 if no value
|
||||
// is set, or the value if it's >= 0.
|
||||
func parseContentLength(cl string) (int64, error) {
|
||||
cl = textproto.TrimString(cl)
|
||||
if cl == "" {
|
||||
var httplaxcontentlength = godebug.New("httplaxcontentlength")
|
||||
|
||||
// parseContentLength checks that the header is valid and then trims
|
||||
// whitespace. It returns -1 if no value is set otherwise the value
|
||||
// if it's >= 0.
|
||||
func parseContentLength(clHeaders []string) (int64, error) {
|
||||
if len(clHeaders) == 0 {
|
||||
return -1, nil
|
||||
}
|
||||
cl := textproto.TrimString(clHeaders[0])
|
||||
|
||||
// The Content-Length must be a valid numeric value.
|
||||
// See: https://datatracker.ietf.org/doc/html/rfc2616/#section-14.13
|
||||
if cl == "" {
|
||||
if httplaxcontentlength.Value() == "1" {
|
||||
httplaxcontentlength.IncNonDefault()
|
||||
return -1, nil
|
||||
}
|
||||
return 0, badStringError("invalid empty Content-Length", cl)
|
||||
}
|
||||
n, err := strconv.ParseUint(cl, 10, 63)
|
||||
if err != nil {
|
||||
return 0, badStringError("bad Content-Length", cl)
|
||||
}
|
||||
return int64(n), nil
|
||||
|
||||
}
|
||||
|
||||
// finishAsyncByteRead finishes reading the 1-byte sniff
|
||||
|
||||
Reference in New Issue
Block a user