Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net/http: Server fails to clean up MultipartForm files when Request.WithContext is used #58809

Closed
JellyZero opened this issue Mar 1, 2023 · 6 comments
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@JellyZero
Copy link

JellyZero commented Mar 1, 2023

What version of Go are you using (go version)?

$ go version
go version go1.19.5 darwin/arm64

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE="on"
GOARCH="arm64"
GOBIN="/Users/jeffreybool/code/go/bin"
GOCACHE="/Users/jeffreybool/Library/Caches/go-build"
GOENV="/Users/jeffreybool/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="arm64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/jeffreybool/code/go/pkg/mod"
GONOPROXY="git.xxx.com"
GONOSUMDB="git.xxx.com"
GOOS="darwin"
GOPATH="/Users/jeffreybool/code/go"
GOPRIVATE="git.xxx.com"
GOPROXY="https://goproxy.cn,direct"
GOROOT="/opt/homebrew/Cellar/go/1.19.5/libexec"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/opt/homebrew/Cellar/go/1.19.5/libexec/pkg/tool/darwin_arm64"
GOVCS=""
GOVERSION="go1.19.5"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/dev/null"
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/1b/9qbcfwxs19g5k9_5h05_9h9m0000gn/T/go-build2306283356=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

request, err := http.NewRequest(http.MethodGet,"/test",nil)
	if err != nil {
		return 
	}
	request.WithContext(request.Context())

What did you expect to see?

labstack/echo#2413

What did you see instead?

The pointer address changes after calling the WithContext method, which causes the server. go finishRequest() method to fail to execute normally

@bcmills
Copy link
Contributor

bcmills commented Mar 1, 2023

causes the server. go finishRequest() method to fail to execute normally

Causes it to fail in what way? (What are the symptoms you actually saw?)

@bcmills bcmills added WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels Mar 1, 2023
@JellyZero
Copy link
Author

JellyZero commented Mar 1, 2023

causes the server. go finishRequest() method to fail to execute normally

Causes it to fail in what way? (What are the symptoms you actually saw?)

package main

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	http.HandleFunc("/upload", foo(bar(upload)))
	http.ListenAndServe(":1323", nil)
}

func foo(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("foo("))

		// do something before
		ctx := context.WithValue(r.Context(), "foo", "bar")
		r = r.WithContext(ctx)

		next(w, r)
		w.Write([]byte(")"))
	}
}

func bar(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("bar("))
		next(w, r)
		w.Write([]byte(")"))
	}
}

func upload(w http.ResponseWriter, r *http.Request) {
	r.ParseMultipartForm(32 << 20)
	file, handler, err := r.FormFile("file")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close()
	f, err := os.OpenFile(handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer f.Close()
	io.Copy(f, file)
	fmt.Fprintln(w, "upload ok!")
}

After calling WithContext with 'r=r.WithContext (ctx)', the request variable pointer address was changed, so it was not executed finishRequest() method, resulting in temporary files not being cleared

@seankhliao
Copy link
Member

WithContext makes a shallow copy of Request, with MultiPartForm == nil
ParseForm changes the value of MultiPartForm from nil to a parsed form, but only on the new copy.
Once the request is completed, finishRequest is run on the original Request, where MultiPartForm is nil, so nothing gets cleaned up.

@bcmills bcmills removed the WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided. label Mar 1, 2023
@JellyZero
Copy link
Author

How to solve it?

@seankhliao seankhliao changed the title net/http: When using the WithContext method of http request, the finishRequest() cannot be executed normally? net/http: multipart tmp files not cleaned up if ParseForm is called after WithContext Mar 1, 2023
@bcmills bcmills changed the title net/http: multipart tmp files not cleaned up if ParseForm is called after WithContext net/http: Server fails to clean up MultipartForm files when Request.WithContext is used Mar 1, 2023
@bcmills bcmills added this to the Backlog milestone Mar 1, 2023
@seankhliao
Copy link
Member

call ParseForm before WithContext, or call RemoveAll yourself.

@JellyZero
Copy link
Author

You mean that the middleware implements req ParseForm method

@golang golang locked and limited conversation to collaborators Mar 1, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

4 participants