Igor Martins
Igor Martins

Reputation: 2047

How to handle error using NewSingleHostReverseProxy

I'm trying to do a load balancer to study some go packages.

I want to handle errors when the request timeout or give 404 error but can't find how to do that.

func main() {
    // start server
    http.HandleFunc("/", handleRequestAndRedirect)

    if err := http.ListenAndServe(getListenAddress(), nil); err != nil {
        panic(err)
    }
}

func handleRequestAndRedirect(res http.ResponseWriter, req *http.Request) {

    ur, _ := url.Parse("https://www.instagram.com/")
    proxy := httputil.NewSingleHostReverseProxy(ur)
    // Update the headers to allow for SSL redirection
    req.URL.Host = ur.Host
    req.URL.Scheme = ur.Scheme
    req.Header.Set("X-Forwarded-Host", req.Header.Get("Host"))
    req.Host = ur.Host
    req.Header.Set("Key", "Teste")

    proxy.ServeHTTP(res, req)

}

Upvotes: 0

Views: 1908

Answers (2)

martin
martin

Reputation: 123

As I ended up here looking for a way to handle 404 errors from the proxied host, I would like to complement the accepted answer, if it may be of any help for people landing on this page.

As stated in the official documentation (https://golang.org/pkg/net/http/httputil/#ReverseProxy):

ModifyResponse is an optional function that modifies the Response from the backend. It is called if the backend returns a response at all, with any HTTP status code. If the backend is unreachable, the optional ErrorHandler is called without any call to ModifyResponse. If ModifyResponse returns an error, ErrorHandler is called with its error value. If ErrorHandler is nil, its default implementation is used.

So if you want to catch not only "real" errors (host not reachable) but also error response codes from the server (404, 500...) you should use ModifyResponse to check the response status code and return an error, which will be then catched by your ErrorHandler function. The accepted answer example becomes:

func handleRequestAndRedirect(res http.ResponseWriter, req *http.Request) {
    
    ur, _ := url.Parse("https://www.instagram.com/")
    proxy := httputil.NewSingleHostReverseProxy(ur)
    // Update the headers to allow for SSL redirection
    req.URL.Host = ur.Host
    req.URL.Scheme = ur.Scheme
    req.Header.Set("X-Forwarded-Host", req.Header.Get("Host"))
    req.Host = ur.Host
    req.Header.Set("Key", "Teste")

    proxy.ErrorHandler = ErrHandle
    proxy.ModifyResponse = ModifyResponse
    proxy.ServeHTTP(res, req)
}

func ModifyResponse(res *http.Response) error {
    if res.StatusCode == 404 {
        return errors.New("404 error from the host")
    }
    return nil
}  

func ErrHandle(res http.ResponseWriter, req *http.Request, err error) {
    fmt.Println(err)
}  

Upvotes: 2

dchaofei
dchaofei

Reputation: 24

use proxy.ErrorHandler

ErrorHandler func(http.ResponseWriter, *http.Request, error)

  func handleRequestAndRedirect(res http.ResponseWriter, req *http.Request) {

        ur, _ := url.Parse("https://www.instagram.com/")
        proxy := httputil.NewSingleHostReverseProxy(ur)
        // Update the headers to allow for SSL redirection
        req.URL.Host = ur.Host
        req.URL.Scheme = ur.Scheme
        req.Header.Set("X-Forwarded-Host", req.Header.Get("Host"))
        req.Host = ur.Host
        req.Header.Set("Key", "Teste")

        proxy.ErrorHandler = ErrHandle
        proxy.ServeHTTP(res, req)
    }

    func ErrHandle(res http.ResponseWriter, req *http.Request, err error) {
        fmt.Println(err)
    }     

Upvotes: 1

Related Questions