How to know proxy used by http client in given request - http

I'm doing some requests through some proxy servers. The function that defines which proxy url to use will choose randomly from a list of proxies. I would like to know for a given request, which proxy url is being used. As far as I know, when using a proxy server the http headers remain the same, but the tcp headers are the one that change.
Here's some code illustrating it (no error handling for simplicity):
func main() {
transport := &http.Transport{Proxy: chooseProxy}
client := http.Client{Transport: transport}
request, err := http.NewRequest(http.MethodGet, "https://www.google.com", nil)
checkErr(err)
// How to know here which proxy was used? Suppose the same client will perform several requests to different URL's.
response, err := client.Do(request)
checkErr(err)
dump, _ := httputil.DumpRequest(response.Request, false)
fmt.Println(dump)
}
func chooseProxy(request *http.Request) (*url.URL, error) {
proxies := []string{"proxy1", "proxy2", "proxy3"}
proxyToUse := proxies[rand.Intn(len(proxies))]
return url.Parse(proxyToUse)
}
I'm assuming that the Proxy function in the transport is called for each request even if the same client is used, as per the docs that say "Proxy specifies a function to return a proxy for a given Request". Am I right?

Some HTTP proxies add a Via header that tell who they are.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Via

You can modify your chooseProxy function so that it saves the proxy selected.
To do that, you can transform the chooseProxy func into a method of a type that will be used as storage for the information you want to keep:
type proxySelector string
func (sel *proxySelector) chooseProxy(request *http.Request) (*url.URL, error) {
proxies := []string{"proxy1", "proxy2", "proxy3"}
proxyToUse := proxies[rand.Intn(len(proxies))]
*sel = proxySelector(proxyToUse) // <-----
return url.Parse(proxyToUse)
}
func main() {
var proxy proxySelector
transport := &http.Transport{Proxy: proxy.chooseProxy} // <-----
client := http.Client{Transport: transport}
request, err := http.NewRequest(http.MethodGet, "https://www.google.com", nil)
checkErr(err)
// How to know here which proxy was used? Suppose the same client will perform several requests to different URL's.
response, err := client.Do(request)
checkErr(err)
dump, _ := httputil.DumpRequest(response.Request, false)
fmt.Println(dump)
fmt.Println("Proxy:", string(proxy)) // <-----
}

The request which contains the target URI is given as argument request to chooseProxy. So you can have the correct mapping already inside your chooseProxy function, all you need to to is check proxyToUse vs. request.URL there.
If you don't really trust the code that this mapping is actually done, then you need to look outside the code. For example you can look at the actual network traffic with Wireshark to see which proxy gets accessed.

Related

Go HTTP RoundTripper: Preventing Connection Reuse Based on Response

I have a use case where I want to use an HTTP client in Go with pooled connections (connection re-use), but with the special case where a connection is intentionally closed (not allowed for re-use) if a request on that connection returns a specific HTTP status code.
I've implemented a custom http.RoundTripper, which wraps an http.Transport, and can inspect the response status code. However, I can't seem to find a way to prevent the http.Transport from re-using that connection, without also preventing it from re-using any other connection.
Is this possible using the net/http package? If not, any suggested workaround for accomplishing this?
My current code looks something like this:
type MyTransport struct {
transport *http.Transport
}
func (mt *MyTransport) RoundTrip(req *http.Request) (*http.Response, error) {
resp, err := tt.transport.RoundTrip(req)
if err != nil {
return resp, err
}
if resp.StatusCode == 567 {
// HERE:
// Do something to prevent re-use of this connection
}
return resp, err
}

Golang ReverseProxy per host

I am trying to implement a Reverse Proxy in Go that proxies traffic to different hosts based on some tenant embedded in the URL. The implementation looks like this:
type Offloader struct {
tenantHostMap map[string]string // Map a tenant to its host:port
tenantProxyMap map[string](*httputil.ReverseProxy) // Map a tenant to its reverse proxy
}
func (o *Offloader) OnCreate() {
// Tenants Map
o.tenantHostMap = make(map[string]string)
o.tenantProxyMap = make(map[string]*httputil.ReverseProxy)
o.PopulateTenantHostMap()
// Rx
http.HandleFunc("/", o.ServeHTTP)
go http.ListenAndServe(":5555", nil)
}
// ServeHTTP is the callback that is called each time a Http Request is received.
func (o *Offloader) ServeHTTP(w http.ResponseWriter, req *http.Request) {
incomingUrl := req.URL.RequestURI()
tenant := o.GetTenantFromUrl(incomingUrl)
if proxy, ok := o.tenantProxyMap[tenant]; ok {
proxy.ServeHTTP(w, req)
}
if remoteHostAddr, ok := o.tenantHostMap[tenant]; ok {
remoteUrl, err := url.Parse(fmt.Sprintf("http://%s", remoteHostAddr))
if err != nil {
return
}
proxy := httputil.NewSingleHostReverseProxy(remoteUrl)
o.tenantProxyMap[tenant] = proxy
proxy.ServeHTTP(w, req) // non blocking
} else {
panic("Unknown Tenant")
}
}
When receiving a new HTTP request, I get the tenant from the URL. If this is the first time I am seeing this tenant I create a new ReverseProxy, otherwise I try to use the one I created before and stored in the tenantProxyMap.
When I test this, I get the following error:
2022/04/05 12:31:01 http: proxy error: readfrom tcp ****: http: invalid Read on closed Body
2022/04/05 12:31:01 http: superfluous response.WriteHeader call from net/http/httputil.(*ReverseProxy).defaultErrorHandler (reverseproxy.go:190)
If I create a new Reverse Proxy for each request rather than reusing the same proxy, the error doesn't happen.
I thought the proxy is per host and not per request (as the name suggests), so I am wondering why this error happens?
I know I need to protect the maps from concurrent reads/writes however that is irrelevant at the moment.
Thanks,
The problem is that in the scenario where a previous proxy already existed, you first pass the request on to that - and then still recreate the proxy, and again pass the request. In other words: you are making two proxied requests for each incoming request, when the tentantProxyMap is already populated for that tenant.
The ReverseProxy implementation closes the req.Body, so the second time you pass the request on to the proxy, it attempts reading from an already closed body. You're seeing the http: invalid Read on closed Body error as a result.
What you should try is to return after proxying the request, e.g. by adding a return:
if proxy, ok := o.tenantProxyMap[tenant]; ok {
proxy.ServeHTTP(w, req)
return
}

Why is there a 60 second delay on my HTTP POST request when using a Go HTTP client?

My goal is to scrape a website that requires me to log in first using HTTP requests in Golang. I actually succeeded by finding out I can send a post request to the website writing form-data into the body of the request. When I test this through an API development software I use called Postman, the response is instantaneous with no delays. However, when performing the request with an HTTP client in Go, there is a consistent 60 second delay every single time. I end up getting a logged in page, but for my program I need the response to be nearly instantaneous.
As you can see in my code, I've tried adding a bunch of headers to the request like "Connection", "Content-Type", "User-Agent" since I thought maaaaaybe the website can tell I'm requesting from a program and is forcing me to wait 60 seconds for a response. Adding these headers to make my request more legitimate(?) doesn't work at all.
Is the delay coming from Go's HTTP client being slow or is there something wrong with how I'm forming my HTTP POST request? Also, was I on to something with my headers and HTTP client is rewriting them when they send out?
Here's my simple program...
package main
import (
"bytes"
"fmt"
"mime/multipart"
"net/http"
"net/http/cookiejar"
"os"
)
func main() {
url := "https://easypronunciation.com/en/log-in"
method := "POST"
payload := &bytes.Buffer{}
writer := multipart.NewWriter(payload)
_ = writer.WriteField("email", "foo#bar.com")
_ = writer.WriteField("password", "*********")
_ = writer.WriteField("persistent_login", "on")
_ = writer.WriteField("submit", "")
err := writer.Close()
if err != nil {
fmt.Println(err)
}
cookieJar, _ := cookiejar.New(nil)
client := &http.Client{
Jar: cookieJar,
}
req, err := http.NewRequest(method, url, payload)
if err != nil {
fmt.Println(err)
}
req.Header.Set("Content-Type", writer.FormDataContentType())
req.Header.Set("Connection", "Keep-Alive")
req.Header.Set("Accept-Language", "en-US")
req.Header.Set("User-Agent", "Mozilla/5.0")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
}
defer res.Body.Close()
f, err := os.Create("response.html")
defer f.Close()
res.Write(f)
}
I doubt, this is the go client library too. I would suggest printing out the latencies for different components and see if/where the 60 second delay is. I would also replace and try different URLs instead

Terminate http request from IP layer using golang

I am making an http post request to a server using golang. Suppose the server is currently turned off (Means the machine on which the server runs is turned off) then the request is stuck at the IP layer. So my program execution is unable to proceed further. It is unable to proceed to the Application layer. So is there any way in golang to stop this.
I am using the following code.
req, err := http.NewRequest("POST", url, bytes.NewReader(b))
if err != nil {
return errors.Wrap(err, "new request error")
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return errors.Wrap(err, "http request error")
}
defer resp.Body.Close()
Is there anything that can be added to this, to terminate the request if it doesn't find anything from the IP layer.
The default http Client has no timeout. You can create an explicit http.Client yourself and set the timeout:
var cl = &http.Client{
Timeout: time.Second * 10,
}
resp, err := cl.Do(req)
if err != nil {
// err will be set on timeout
return errors.Wrap(err, "http request error")
}
defer resp.Body.Close()
If the server does not answer any more in the middle of a request, you can handle the timeout.
Use a non-default http.Transport with its DialContext field set to a function which uses a custom context with the properly configured timeout/deadline. Another option is to use a custom net.Dialer.
Something like this:
cli := http.Client{
Transport: &http.Transport{
DialContext: func (ctx context.Context, network, address string) (net.Conn, error) {
dialer := net.Dialer{
Timeout: 3 * time.Second,
}
return dialer.DialContext(ctx, network, address)
},
},
}
req, err := http.NewRequest(...)
resp, err := cli.Do(req)
Note that as per the net.Dialer's docs the context passed to its DialContext might trump the timeout set on the dialer itself—this is
exactly what we need: the dialer's Timeout field controls exactly the
"dialing" (TCP connection establishment) while you might also arm your
HTTP request with a context (using http.Request.WithContext) controlling
the timeout of the whole request, and also be able to cancel it at any time (including the dialing step).
Playground example.
The Transport #kostix refers to is definitely what you're looking for in this case. Transports as well as Clients are safe for concurrent use as well. But please read about the Transport (and I also advise reading about the Client as well) as there are a number of different ways to affect how you handle idle connections, not just the pre-mentioned DialContext.
As you may want to set your ResponseHeaderTimeout:
ResponseHeaderTimeout, if non-zero, specifies the amount of
time to wait for a server's response headers after fully
writing the request (including its body, if any). This
time does not include the time to read the response body.
Or, if you are using a secure connection, you may want to set your TLSHandshakeTimeout:
TLSHandshakeTimeout specifies the maximum amount of time waiting to
wait for a TLS handshake. Zero means no timeout.
For readability and maintainability, I suggest also maybe creating a function to build your Client, something along the lines of:
func buildClient(timeout time.Duration) *http.Client {
tr := &http.Transport{
IdleConnTimeout: timeout,
ResponseHeaderTimeout: timeout,
TLSHandshakeTimeout: timeout,
}
client := &http.Client{
Transport: tr,
Timeout: timeout,
}
return client
}

Setting up proxy for HTTP client

I'm trying to setup the HTTP client so that it uses a proxy, however I cannot quite understand how to do it. The documentation has multiple reference to "proxy" but none of the functions seem to allow to define the proxy. What I need is something like this:
client := &http.Client{}
client.SetProxy("someip:someport") // pseudo code
resp, err := client.Get("http://example.com") // do request through proxy
Any idea how to do this in Go?
lukad is correct, you could set the HTTP_PROXY environment variable, if you do this Go will use it by default.
Bash:
export HTTP_PROXY="http://proxyIp:proxyPort"
Go:
os.Setenv("HTTP_PROXY", "http://proxyIp:proxyPort")
You could also construct your own http.Client that MUST use a proxy regardless of the environment's configuration:
proxyUrl, err := url.Parse("http://proxyIp:proxyPort")
myClient := &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyUrl)}}
This is useful if you can not depend on the environment's configuration, or do not want to modify it.
You could also modify the default transport used by the "net/http" package. This would affect your entire program (including the default HTTP client).
proxyUrl, err := url.Parse("http://proxyIp:proxyPort")
http.DefaultTransport = &http.Transport{Proxy: http.ProxyURL(proxyUrl)}
Go will use the the proxy defined in the environment variable HTTP_PROXY if it's set. Otherwise it will use no proxy.
You could do it like this:
os.Setenv("HTTP_PROXY", "http://someip:someport")
resp, err := http.Get("http://example.com")
if err != nil {
panic(err)
}
May you could also try this:
url_i := url.URL{}
url_proxy, _ := url_i.Parse(proxy_addr)
transport := http.Transport{}
transport.Proxy = http.ProxyURL(url_proxy)// set proxy
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} //set ssl
client := &http.Client{}
client.Transport = transport
resp, err := client.Get("http://example.com") // do request through proxy
The Go built-in proxy use is briefly documented in the DefaultTransport:
// DefaultTransport .... It uses HTTP proxies
// as directed by the $HTTP_PROXY and $NO_PROXY (or $http_proxy and
// $no_proxy) environment variables.
var DefaultTransport RoundTripper = &Transport{
Proxy: ProxyFromEnvironment,
This points to the usefulness of creating custom Transports from DefaultTransport vs. from scratch to take advantage of the built-in ProxyFromEnvironment function
If you run something like this:
HTTP_PROXY=89.x.y.z path_to_program
Then the HTTP_PROXY setting is set for that command only, which is useful if you don't want to set it for the whole shell session.
Note: there's no ; between the setting and the path; if you put a semicolon, it would set (but not export) HTTP_PROXY for that shell
For an alternative way, you can also use GoRequest which has a feature that you can set proxy easily for any single request.
request := gorequest.New()
resp, body, errs:= request.Proxy("http://proxy:999").Get("http://example.com").End()
resp2, body2, errs2 := request.Proxy("http://proxy2:999").Get("http://example2.com").End()
Or you can set for the whole at once.
request := gorequest.New().Proxy("http://proxy:999")
resp, body, errs:= request.Get("http://example.com").End()
resp2, body2, errs2 := request.Get("http://example2.com").End()

Resources