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

fix: add timeout to proxy connection reading and writing #1791

Merged
merged 1 commit into from
Jun 19, 2024

Conversation

Sniper91
Copy link
Contributor

@Sniper91 Sniper91 commented Jun 12, 2024

fast http client may hang forever when reading from proxy connection.
code to reproduce the bug:

proxy server

package main

import (
	"log"
	"net/http"
	"time"
)

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("Received request %s %s %s\n", r.Method, r.Host, r.RemoteAddr)
		if r.Method == http.MethodConnect {
			time.Sleep(1 * time.Hour) // proxy server may hang a long time for some reason
		}
	})
	log.Fatal(http.ListenAndServe(":8181", handler))
}

fast http client hangs and ignores the timeout because proxy server doesn't response

package main

import (
	"fmt"
	"os"
	"time"

	"github.com/valyala/fasthttp"
	"github.com/valyala/fasthttp/fasthttpproxy"
)

func main() {
	client := fasthttp.Client{
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 5 * time.Second,
		Dial:         fasthttpproxy.FasthttpHTTPDialerTimeout("127.0.0.1:8181", 5*time.Second),
	}
	req := fasthttp.AcquireRequest()
	req.SetRequestURI("https://localhost:8080/")
	req.Header.SetMethod(fasthttp.MethodGet)
	resp := fasthttp.AcquireResponse()
	err := client.Do(req, resp)
	fasthttp.ReleaseRequest(req)
	if err == nil {
		fmt.Printf("DEBUG Response: %s\n", resp.Body())
	} else {
		fmt.Fprintf(os.Stderr, "ERR Connection error: %v\n", err)
	}
	fasthttp.ReleaseResponse(resp)
}

http client from standard library behaves normally

package main

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"time"
)

func main() {
	uu, _ := url.Parse("http://localhost:8181")
	client := &http.Client{
		Timeout: 5 * time.Second,
		Transport: &http.Transport{
			Proxy: http.ProxyURL(uu),
		},
	}
	resp, err := client.Get("https://localhost:8080/")
	if err != nil {
		fmt.Println(err)
	}
	io.Copy(os.Stdout, resp.Body)
	resp.Body.Close()
}

The implementation refers code from https://github.com/golang/go/blob/fe9b3c339978f37aad53875d9e6d2df35a1996ad/src/net/http/transport.go#L1725

@Sniper91 Sniper91 changed the title fix: set deadline for proxy connection fix: add timeout to proxy connection reading and writing Jun 13, 2024
Copy link
Collaborator

@erikdubbelboer erikdubbelboer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just set a deadline on the connection? https://pkg.go.dev/net#Conn.SetDeadline

@Sniper91
Copy link
Contributor Author

Sniper91 commented Jun 14, 2024

Why not just set a deadline on the connection? https://pkg.go.dev/net#Conn.SetDeadline

The connection will be read or write after function return. Calling net#Conn.SetDeadline here may cause unexpected os.ErrDeadlineExceeded error.

@erikdubbelboer
Copy link
Collaborator

You can call conn.SetDeadline(time.Time{}) before returning from the function to remove the deadline again.

@Sniper91 Sniper91 force-pushed the bugfix/fix-proxy-hang branch 3 times, most recently from 0340276 to 926da81 Compare June 17, 2024 08:59
@Sniper91
Copy link
Contributor Author

You can call conn.SetDeadline(time.Time{}) before returning from the function to remove the deadline again.

Oh, It's more concise. I've already reimplemented this PR.

fasthttpproxy/http.go Outdated Show resolved Hide resolved
@erikdubbelboer erikdubbelboer merged commit 21b235d into valyala:master Jun 19, 2024
18 of 19 checks passed
@erikdubbelboer
Copy link
Collaborator

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants