gout 以及net/http post文件流时 内存区别
使用gout 上传文件流时, 发现上传文件过程中会占用很大的内存。
研究其代码发现;对于文件流,其会copy到内存里面,然后在post;文件有多大,他就会拷贝多大。
// Encode Add Encoder core function, used to set io.Writer into the http body func (b *BodyEncode) Encode(w io.Writer) error { if r, ok := b.obj.(io.Reader); ok { _, err := io.Copy(w, r) return err } -------------- }
目前希望的是将文件流os.File 直接复制给body;最后send 系统调用发起时,从文件流里面read数据然后send数据,一直到数据send发送完!或者调用sendfile 零拷贝。
后续调研接口;发送net/http
func NewRequestWithContext(ctx context.Context, method, url string, body io.Reader) (*Request, error) { if method == "" { // We document that "" means "GET" for Request.Method, and people have // relied on that from NewRequest, so keep that working. // We still enforce validMethod for non-empty methods. method = "GET" } if !validMethod(method) { return nil, fmt.Errorf("net/http: invalid method %q", method) } if ctx == nil { return nil, errors.New("net/http: nil Context") } u, err := urlpkg.Parse(url) if err != nil { return nil, err } 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 := &Request{ ctx: ctx, Method: method, URL: u, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, Header: make(Header), Body: rc, Host: u.Host, } --------------------------------------- }
其直接将os.File 赋值到body;
后续发送时
func (pc *persistConn) writeLoop() {
defer close(pc.writeLoopDone)
for {
select {
case wr := <-pc.writech:
startBytesWritten := pc.nwrite
err := wr.req.Request.write(pc.bw, pc.isProxy, wr.req.extra, pc.waitForContinue(wr.continueCh))
if bre, ok := err.(requestBodyReadError); ok {
最后直接调用write --- copy(dst,src)
// extraHeaders may be nil
// waitForContinue may be nil
// always closes body
func (r *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, waitForContinue func() bool) (err error) {
trace := httptrace.ContextClientTrace(r.Context())
if trace != nil && trace.WroteRequest != nil {
defer func() {
trace.WroteRequest(httptrace.WroteRequestInfo{
Err: err,
})
}()
}
closed := false
defer func() {
if closed {
return
}
if closeErr := r.closeBody(); closeErr != nil && err == nil {
err = closeErr
}
}()
// Find the target host. Prefer the Host: header, but if that
// is not given, use the host from the request URL.
//
// Clean the host, in case it arrives with unexpected stuff in it.
host := r.Host
if host == "" {
if r.URL == nil {
return errMissingHost
}
host = r.URL.Host
}
host, err = httpguts.PunycodeHostPort(host)
if err != nil {
return err
}
// Validate that the Host header is a valid header in general,
// but don't validate the host itself. This is sufficient to avoid
// header or request smuggling via the Host field.
// The server can (and will, if it's a net/http server) reject
// the request if it doesn't consider the host valid.
if !httpguts.ValidHostHeader(host) {
// Historically, we would truncate the Host header after '/' or ' '.
// Some users have relied on this truncation to convert a network
// address such as Unix domain socket path into a valid, ignored
// Host header (see https://go.dev/issue/61431).
//
// We don't preserve the truncation, because sending an altered
// header field opens a smuggling vector. Instead, zero out the
// Host header entirely if it isn't valid. (An empty Host is valid;
// see RFC 9112 Section 3.2.)
//
// Return an error if we're sending to a proxy, since the proxy
// probably can't do anything useful with an empty Host header.
if !usingProxy {
host = ""
} else {
return errors.New("http: invalid Host header")
}
}
// According to RFC 6874, an HTTP client, proxy, or other
// intermediary must remove any IPv6 zone identifier attached
// to an outgoing URI.
host = removeZone(host)
ruri := r.URL.RequestURI()
if usingProxy && r.URL.Scheme != "" && r.URL.Opaque == "" {
ruri = r.URL.Scheme + "://" + host + ruri
} else if r.Method == "CONNECT" && r.URL.Path == "" {
// CONNECT requests normally give just the host and port, not a full URL.
ruri = host
if r.URL.Opaque != "" {
ruri = r.URL.Opaque
}
}
if stringContainsCTLByte(ruri) {
return errors.New("net/http: can't write control character in Request.URL")
}
// TODO: validate r.Method too? At least it's less likely to
// come from an attacker (more likely to be a constant in
// code).
// Wrap the writer in a bufio Writer if it's not already buffered.
// Don't always call NewWriter, as that forces a bytes.Buffer
// and other small bufio Writers to have a minimum 4k buffer
// size.
var bw *bufio.Writer
if _, ok := w.(io.ByteWriter); !ok {
bw = bufio.NewWriter(w)
w = bw
}
_, err = fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(r.Method, "GET"), ruri)
if err != nil {
return err
}
// Header lines
_, err = fmt.Fprintf(w, "Host: %s\r\n", host)
if err != nil {
return err
}
if trace != nil && trace.WroteHeaderField != nil {
trace.WroteHeaderField("Host", []string{host})
}
// Use the defaultUserAgent unless the Header contains one, which
// may be blank to not send the header.
userAgent := defaultUserAgent
if r.Header.has("User-Agent") {
userAgent = r.Header.Get("User-Agent")
}
if userAgent != "" {
userAgent = headerNewlineToSpace.Replace(userAgent)
userAgent = textproto.TrimString(userAgent)
_, err = fmt.Fprintf(w, "User-Agent: %s\r\n", userAgent)
if err != nil {
return err
}
if trace != nil && trace.WroteHeaderField != nil {
trace.WroteHeaderField("User-Agent", []string{userAgent})
}
}
// Process Body,ContentLength,Close,Trailer
tw, err := newTransferWriter(r)
if err != nil {
return err
}
err = tw.writeHeader(w, trace)
if err != nil {
return err
}
err = r.Header.writeSubset(w, reqWriteExcludeHeader, trace)
if err != nil {
return err
}
if extraHeaders != nil {
err = extraHeaders.write(w, trace)
if err != nil {
return err
}
}
_, err = io.WriteString(w, "\r\n")
if err != nil {
return err
}
if trace != nil && trace.WroteHeaders != nil {
trace.WroteHeaders()
}
// Flush and wait for 100-continue if expected.
if waitForContinue != nil {
if bw, ok := w.(*bufio.Writer); ok {
err = bw.Flush()
if err != nil {
return err
}
}
if trace != nil && trace.Wait100Continue != nil {
trace.Wait100Continue()
}
if !waitForContinue() {
closed = true
r.closeBody()
return nil
}
}
if bw, ok := w.(*bufio.Writer); ok && tw.FlushHeaders {
if err := bw.Flush(); err != nil {
return err
}
}
// Write body and trailer
closed = true
err = tw.writeBody(w)
if err != nil {
if tw.bodyReadError == err {
err = requestBodyReadError{err}
}
return err
}
if bw != nil {
return bw.Flush()
}
return nil
}
http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!!
但行好事 莫问前程
--身高体重180的胖子

浙公网安备 33010602011771号