Full-featured, plugin-driven, extensible HTTP client toolkit for Go

Overview

gentleman Build Status GitHub release GoDoc Coverage Status Go Report Card Go Version

Full-featured, plugin-driven, middleware-oriented toolkit to easily create rich, versatile and composable HTTP clients in Go.

gentleman embraces extensibility and composition principles in order to provide a flexible way to easily create featured HTTP client layers based on built-in or third-party plugins that you can register and reuse across HTTP clients.

As an example, you can easily provide retry policy capabilities or dynamic server discovery in your HTTP clients simply attaching the retry or consul plugins.

Take a look to the examples, list of supported plugins, HTTP entities or middleware layer to get started.

For testing purposes, see baloo, an utility library for expressive end-to-end HTTP API testing, built on top of gentleman toolkit. For HTTP mocking, see gentleman-mock, which uses gock under the hood for easy and expressive HTTP client request mocking.

Versions

  • v2 - Latest version. Stable. Recommended.
  • v1 - First version. Stable. Actively maintained.

Features

  • Plugin driven architecture.
  • Simple, expressive, fluent API.
  • Idiomatic built on top of net/http package.
  • Context-aware hierarchical middleware layer supporting all the HTTP life cycle.
  • Built-in multiplexer for easy composition capabilities.
  • Easy to extend via plugins/middleware.
  • Ability to easily intercept and modify HTTP traffic on-the-fly.
  • Convenient helpers and abstractions over Go's HTTP primitives.
  • URL template path params.
  • Built-in JSON, XML and multipart bodies serialization and parsing.
  • Easy to test via HTTP mocking (e.g: gentleman-mock).
  • Supports data passing across plugins/middleware via its built-in context.
  • Fits good while building domain-specific HTTP API clients.
  • Easy to hack.
  • Dependency free.

Installation

go get -u gopkg.in/h2non/gentleman.v2

Requirements

  • Go 1.9+

Plugins

Name Docs Status Description
url Easily declare URL, base URL and path values in HTTP requests
auth Declare authorization headers in your requests
body Easily define bodies based on JSON, XML, strings, buffers or streams
bodytype Define body MIME type by alias
cookies Declare and store HTTP cookies easily
compression Helpers to define enable/disable HTTP compression
headers Manage HTTP headers easily
multipart Create multipart forms easily. Supports files and text fields
proxy Configure HTTP proxy servers
query Easily manage query params
redirect Easily configure a custom redirect policy
timeout Easily configure the HTTP timeouts (request, dial, TLS...)
transport Define a custom HTTP transport easily
tls Configure the TLS options used by the HTTP transport
retry Provide retry policy capabilities to your HTTP clients
mock Easy HTTP mocking using gock
consul Consul based server discovery with configurable retry/backoff policy

Community plugins

Name Docs Status Description
logger Easily log requests and responses

Send a PR to add your plugin to the list.

Creating plugins

You can create your own plugins for a wide variety of purposes, such as server discovery, custom HTTP tranport, modify any request/response param, intercept traffic, authentication and so on.

Plugins are essentially a set of middleware function handlers for one or multiple HTTP life cycle phases exposing a concrete interface consumed by gentleman middleware layer.

For more details about plugins see the plugin package and examples.

Also you can take a look to a plugin implementation example.

HTTP entities

gentleman provides two HTTP high level entities: Client and Request.

Each of these entities provides a common API and are both middleware capable, giving you the ability to plug in custom components with own logic into any of them.

gentleman was designed to provide strong reusability capabilities. This is mostly achieved via its built-in hierarchical, inheritance-based middleware layer.

The following list describes how inheritance hierarchy works and is used across gentleman's entities.

  • Client entity can inherit from other Client entity.
  • Request entity can inherit from a Client entity.
  • Client entity is mostly designed for reusability.
  • Client entity can create multiple Request entities who implicitly inherits from Client entity itself.
  • Request entity is designed to have specific HTTP request logic that is not typically reused.
  • Both Client and Request entities are full middleware capable interfaces.
  • Both Client and Request entities can be cloned in order to produce a copy but side-effects free new entity.

You can see an inheritance usage example here.

Middleware

gentleman is completely based on a hierarchical middleware layer based on plugins that executes one or multiple function handlers (aka plugin interface) providing a simple way to plug in intermediate custom logic in your HTTP client.

It supports multiple phases which represents the full HTTP request/response life cycle, giving you the ability to perform actions before and after an HTTP transaction happen, even intercepting and stopping it.

The middleware stack chain is executed in FIFO order designed for single thread model. Plugins can support goroutines, but plugins implementors should prevent data race issues due to concurrency in multithreading programming.

For more implementation details about the middleware layer, see the middleware package and examples.

Middleware phases

Supported middleware phases triggered by gentleman HTTP dispatcher:

  • request - Executed before a request is sent over the network.
  • response - Executed when the client receives the response, even if it failed.
  • error - Executed in case that an error ocurrs, support both injected or native error.
  • stop - Executed in case that the request has been manually stopped via middleware (e.g: after interception).
  • intercept - Executed in case that the request has been intercepted before network dialing.
  • before dial - Executed before a request is sent over the network.
  • after dial - Executed after the request dialing was done and the response has been received.

Note that the middleware layer has been designed for easy extensibility, therefore new phases may be added in the future and/or the developer could be able to trigger custom middleware phases if needed.

Feel free to fill an issue to discuss this capabilities in detail.

API

See godoc reference for detailed API documentation.

Subpackages

  • plugin - godoc - Plugin layer for gentleman.
  • mux - godoc - HTTP client multiplexer with built-in matchers.
  • middleware - godoc - Middleware layer used by gentleman.
  • context - godoc - HTTP context implementation for gentleman's middleware.
  • utils - godoc - HTTP utilities internally used.

Examples

See examples directory for featured examples.

Simple request

package main

import (
  "fmt"

  "gopkg.in/h2non/gentleman.v2"
)

func main() {
  // Create a new client
  cli := gentleman.New()

  // Define base URL
  cli.URL("http://httpbin.org")

  // Create a new request based on the current client
  req := cli.Request()

  // Define the URL path at request level
  req.Path("/headers")

  // Set a new header field
  req.SetHeader("Client", "gentleman")

  // Perform the request
  res, err := req.Send()
  if err != nil {
    fmt.Printf("Request error: %s\n", err)
    return
  }
  if !res.Ok {
    fmt.Printf("Invalid server response: %d\n", res.StatusCode)
    return
  }

  // Reads the whole body and returns it as string
  fmt.Printf("Body: %s", res.String())
}

Send JSON body

package main

import (
  "fmt"

  "gopkg.in/h2non/gentleman.v2"
  "gopkg.in/h2non/gentleman.v2/plugins/body"
)

func main() {
  // Create a new client
  cli := gentleman.New()

  // Define the Base URL
  cli.URL("http://httpbin.org/post")

  // Create a new request based on the current client
  req := cli.Request()

  // Method to be used
  req.Method("POST")

  // Define the JSON payload via body plugin
  data := map[string]string{"foo": "bar"}
  req.Use(body.JSON(data))

  // Perform the request
  res, err := req.Send()
  if err != nil {
    fmt.Printf("Request error: %s\n", err)
    return
  }
  if !res.Ok {
    fmt.Printf("Invalid server response: %d\n", res.StatusCode)
    return
  }

  fmt.Printf("Status: %d\n", res.StatusCode)
  fmt.Printf("Body: %s", res.String())
}

Composition via multiplexer

package main

import (
  "fmt"

  "gopkg.in/h2non/gentleman.v2"
  "gopkg.in/h2non/gentleman.v2/mux"
  "gopkg.in/h2non/gentleman.v2/plugins/url"
)

func main() {
  // Create a new client
  cli := gentleman.New()

  // Define the server url (must be first)
  cli.Use(url.URL("http://httpbin.org"))

  // Create a new multiplexer based on multiple matchers
  mx := mux.If(mux.Method("GET"), mux.Host("httpbin.org"))

  // Attach a custom plugin on the multiplexer that will be executed if the matchers passes
  mx.Use(url.Path("/headers"))

  // Attach the multiplexer on the main client
  cli.Use(mx)

  // Perform the request
  res, err := cli.Request().Send()
  if err != nil {
    fmt.Printf("Request error: %s\n", err)
    return
  }
  if !res.Ok {
    fmt.Printf("Invalid server response: %d\n", res.StatusCode)
    return
  }

  fmt.Printf("Status: %d\n", res.StatusCode)
  fmt.Printf("Body: %s", res.String())
}

License

MIT - Tomas Aparicio

Comments
  • the

    the "net/http/httputil.DumpRequest" will destory the context value storage.

    package main
    
    import (
    	"fmt"
    	"net/http/httputil"
    
    	"gopkg.in/h2non/gentleman.v1"
    	"gopkg.in/h2non/gentleman.v1/context"
    )
    
    func main() {
    	req := gentleman.NewRequest()
    	req.Method("GET")
    	req.URL("httpbin.org/post")
    	req.BodyString("Body Hell!")
    	req.UseHandler("before dial", func(ctx *context.Context, h context.Handler) {
    		fmt.Printf("Before setting context:%T - %v\n", ctx.Request.Body, ctx.Request.Body)
    		ctx.Set("Foo", "Bar")
    		fmt.Printf("After setting context:%T - %v\n", ctx.Request.Body, ctx.Request.Body)
    		httputil.DumpRequest(ctx.Request, true)
    		fmt.Printf("After DumpRequest:%T - %v\n", ctx.Request.Body, ctx.Request.Body)
    		h.Next(ctx)
    	})
    	req.Do()
    }
    

    Output:

    Before setting context:*context.contextReadCloser - &{{0xc042068d80} map[$phase:before dial]}
    After setting context:*context.contextReadCloser - &{{0xc042068d80} map[Foo:Bar $phase:before dial]}
    After DumpRequest:ioutil.nopCloser - {Body Hell!}
    

    With so many issues caused by wrapping context storage in http.Request.Body , I think we urgently need a solution for context storing.

    enhancement 
    opened by elonzh 6
  • HTTP method is overridden

    HTTP method is overridden

    I wanted to try gentleman doing a simple:

    func (r *Repository) Send(authPayload, method, url string, data interface{}) ([]byte, error) {
        req := r.c.Request()
        req.AddHeader("Auth-Server-Payload", authPayload)
        req.Method(method)
        req.URL(url)
        req.JSON(data)
        res, err := req.Send()
        if err != nil {
            return nil, err
        }
            return res.Bytes(), nil
    }
    

    But when I use it, the HTTP method is overridden and the client always send a POST.

    When I write fmt.Printf(req.Context.Request.Method) just before the Send and it prints a GET, I can see that a fmt.Printf(ctx.Request.Method) in the doDial method of the dispatcher prints a POST.

    Am I doing something wrong ?

    bug enhancement 
    opened by solher 6
  • Thread Safety of Gentleman instance

    Thread Safety of Gentleman instance

    I am wondering if one instance of Gentleman can be shared across multiple go-routines Or every go-routine should create it's own instance every time a request comes?

    question 
    opened by sachin-walia 5
  • Allow client setting methods reusability

    Allow client setting methods reusability

    I may be wrong but for now, it seems that the setting client methods such as func (c *Client) BaseURL(uri string) *Client only allows one time uses.

    You can't really do:

    cli := gentleman.New()
    cli.BaseURL("foo.com")
    cli.BaseURL("bar.com")
    

    Because a new middleware is added each time.

    The thing is I really need that kind of feature to use gentleman in a database driver because I need to be able to switch the DB url at any time, without touching the other middlewares.

    I'm pretty sure there must be a workaround to do that right now. What would be the best way ?

    question 
    opened by solher 5
  • On Go 1.9 redirect plugin produce duplicate of headers.

    On Go 1.9 redirect plugin produce duplicate of headers.

    Same code compiled with go 1.7 and 1.9 produce different requests.

    On go 1.7:

    DEBU[0001] http request
    GET /api-v2.1/ HTTP/0.0
    Host: test.sdn:8765
    Content-Type: application/json
    Referer: http://test.sdn:8765/api-v2.1
    User-Agent: gentleman/2.0.2
    X-Auth-Token: censored
    

    Go 1.9:

    DEBU[0001] http request
    GET /api-v2.1/ HTTP/0.0
    Host: test.sdn:8765
    Content-Type: application/json
    Content-Type: application/json
    Referer: http://test.sdn:8765/api-v2.1
    User-Agent: gentleman/2.0.2
    User-Agent: gentleman/2.0.2
    X-Auth-Token: censored
    X-Auth-Token: censored
    

    I workaround that by making custom plugin:

    func NewRedirectPlugin() plugin.Plugin {
    	return plugin.NewRequestPlugin(func(ctx *context.Context, h context.Handler) {
    		ctx.Client.CheckRedirect = func(req *http.Request, pool []*http.Request) error {
    			if len(pool) >= redirect.RedirectLimit {
    				return redirect.ErrRedirectLimitExceeded
    			}
    
    			// redirect.Config() copy Headers from pool[0].Headers to req.Headers using req.Headers.Add()
    			// That works on Go 1.7 (and seems required), but on Go 1.9 that produce header duplicates.
    			// E.g. two X-Auth-Token instead of one. That broke auth.
    			// Seems that in Go 1.9 net/http do headers copy
    
    			return nil
    		}
    		h.Next(ctx)
    	})
    }
    
    bug 
    opened by vooon 3
  • Error middlewares don't run if layer has parent

    Error middlewares don't run if layer has parent

    Steps to reproduce:

    1. create a client
    2. create a request from the client
    3. register middleware for error phase for the request (not the client!)
    4. trigger an error (eg: call a random url: localhost:12345)

    Registered middleware won't run.

    In middleware.go, Layer.Run method:

    	ctx = s.parent.Run(phase, ctx)
    	if ctx.Error != nil || ctx.Stopped {
    		return ctx
    	}
    

    This will cause middlewares registered for error phase not to run if there is an error.

    Possible solution:

    	ctx = s.parent.Run(phase, ctx)
    	if (phase != "error" && ctx.Error != nil) || ctx.Stopped {
    		return ctx
    	}
    

    but I'm not sure what that would break.

    bug 
    opened by masterada 3
  • Finalizers are used incorrectly?

    Finalizers are used incorrectly?

    Some of our tests are run with --race to check for race conditions and I have traced an issue with a failing test that seem to come from gentelmen's use of finalizer. There are a few uses of runtime.SetFinalizer() which I believe are incorrect. See the following code:

    func SetTransportFinalizer(transport *http.Transport) {
    	runtime.SetFinalizer(&transport, func(t **http.Transport) {
    		(*t).CloseIdleConnections()
    	})
    }
    

    You get a pointer to an http.Transport and you're setting a finalizer for the pointer to that pointer. This means that the finalizer will fire when the local variable is GCed and not when the value itself. Suggestions

    • The following solves the issue for us:
    func SetTransportFinalizer(transport *http.Transport) {
    	runtime.SetFinalizer(transport, func(t *http.Transport) {
    		t.CloseIdleConnections()
    	})
    }
    
    • this probably needs to be changed also in response.EnsureResponseFinalized too

    • But.. I think you should reconsider the usage of the finalizer at all. I know it as been copied from Hashicorp's go-cleanhttp package but note that they have removed that from their package.

    • Also note that in the same file you have an almost identical func EnsureTransporterFinalized()which doesn't seem to be used anywhere.

    bug enhancement 
    opened by eyalpost 3
  • Enhance redirect plugin to allow for trusted hosts

    Enhance redirect plugin to allow for trusted hosts

    We're using the gentleman client to make calls to 1st party services that can result in redirects. Initially, we were using the 'Trusted' field of the plugin to ensure that any headers were forwarded. For security purposes, we wanted to add a capability and limit header forwarding to only our 1st party services so any potential redirection to a 3rd party service wouldn't end up exposing sensitive information. In order to accomplish this, we created a fork of the redirect plugin that includes a field for a list of trusted host suffixes before doing the header copying.

    It would be great to have that feature as part of the upstream gentleman, and I'd be happy to put together a PR for it if there is interest. But I wanted to open an issue and check on feasibility before just dropping a PR on your doorstep.

    Thanks!

    enhancement 
    opened by caseyhadden 2
  • Using body plugin will destroy the context.

    Using body plugin will destroy the context.

    You wrap the context storage in *http.Request.Body.

    When using body plugin, the plugin replace the *http.Request.Body directly.

    Here is a example to reproduce the problem.

    package main
    
    import (
    	"fmt"
    
    	"gopkg.in/h2non/gentleman.v1"
    )
    
    func main() {
    	req := gentleman.NewRequest()
    	req.Method("GET")
    	req.URL("http://httpbin.org/headers")
    	// Define the URL path at request level
    	//req.Path("/headers")
    	// Set a new header field
    	req.SetHeader("Client", "gentleman")
    	req.Context.Set("Foo", "Bar")
    	req.BodyString("Body Hell!")
    	// Perform the request
    	fmt.Println(req.Context.GetAll(), req.Context.Stopped)
    	res, err := req.Do()
    	fmt.Println(req.Context.GetAll(), req.Context.Stopped)
    	fmt.Println(res.Context.GetAll(), res.Context.Stopped)
    	if err != nil {
    		fmt.Printf("Request error: %s\n", err)
    		return
    	}
    	if !res.Ok {
    		fmt.Printf("Invalid server response: %d\n", res.StatusCode)
    		return
    	}
    	// Reads the whole body and returns it as string
    	fmt.Printf("Body: %s", res.String())
    }
    

    Output:

    map[Foo:Bar] false
    map[$phase:response] false
    map[$phase:response] false
    Body: {
      "headers": {
        "Accept-Encoding": "gzip", 
        "Client": "gentleman", 
        "Content-Length": "14", 
        "Content-Type": "application/json", 
        "Host": "httpbin.org", 
        "User-Agent": "gentleman/1.0.2"
      }
    }
    
    bug 
    opened by elonzh 2
  • Add HTTP Options request to the client.

    Add HTTP Options request to the client.

    We are using openapi-cli-generator which uses gentleman as its http client. We need to use the options method, and that is what this PR for. Thank you

    opened by ArasAzimi 1
  • RFC: add String function to Request

    RFC: add String function to Request

    I imagined that cloning a request and running its request phase would be enough to have a representation of its body. However this solution doesn't work when the request is generated using an io.Reader

    If you run the tests i added the last assert fails.

    opened by jonasagx 1
  •  GetBody() the  Method in Golang  is not define in gentleman.

    GetBody() the Method in Golang is not define in gentleman.

    When creating a new request, the GetBody property of the request is set according to the body parameter. This attribute will affect the client's behavior after receiving the 307, 308 response code. If it is empty, it is likely that it will not follow redirect.

    if body != nil {
    		switch v := body.(type) {
    		case *bytes.Buffer:
    			req.ContentLength = int64(v.Len())
    			buf := v.Bytes()
    			req.GetBody = func() (io.ReadCloser, error) {
    				r := bytes.NewReader(buf)
    				return io.NopCloser(r), nil
    			}
    }
    
    if ireq.GetBody == nil && ireq.outgoingLength() != 0 {
    	// We had a request body, and 307/308 require
    	// re-sending it, but GetBody is not defined. So just
    	// return this response to the user instead of an
    	// error, like we did in Go 1.7 and earlier.
    	shouldRedirect = false
    }
    

    But in gentleman the 'body.go ' and the createRequest the GetBody() Method is not define.

    // String defines the HTTP request body based on the given string.
    func String(data string) p.Plugin {
    	return p.NewRequestPlugin(func(ctx *c.Context, h c.Handler) {
    		ctx.Request.Method = getMethod(ctx)
    		ctx.Request.Body = utils.StringReader(data)
    		ctx.Request.ContentLength = int64(bytes.NewBufferString(data).Len())
    		h.Next(ctx)
    	})
    }
    
    
    // JSON defines a JSON body in the outgoing request.
    // Supports strings, array of bytes or buffer.
    func JSON(data interface{}) p.Plugin {
    	return p.NewRequestPlugin(func(ctx *c.Context, h c.Handler) {
    		buf := &bytes.Buffer{}
    	switch data.(type) {
    	case string:
    		buf.WriteString(data.(string))
    	case []byte:
    		buf.Write(data.([]byte))
    	default:
    		if err := json.NewEncoder(buf).Encode(data); err != nil {
    			h.Error(ctx, err)
    			return
    		}
    	}
    
    	ctx.Request.Method = getMethod(ctx)
    	ctx.Request.Body = ioutil.NopCloser(buf)
    	ctx.Request.ContentLength = int64(buf.Len())
    	ctx.Request.Header.Set("Content-Type", "application/json")
    	h.Next(ctx)
    })
    ..........
    }
    
    // createRequest creates a default http.Request instance.
    func createRequest() *http.Request {
    	// Create HTTP request
    	req := &http.Request{
    		Method:     "GET",
    		URL:        &url.URL{},
    		Host:       "",
    		ProtoMajor: 1,
    		ProtoMinor: 1,
    		Proto:      "HTTP/1.1",
    		Header:     make(http.Header),
    		Body:       utils.NopCloser(),
    	}
    	// Return shallow copy of Request with the new context
    	return req.WithContext(emptyContext())
    }
    
    opened by leleyi 0
  • While using mutlipart form-data I am not able to set fieldName diff from fileName

    While using mutlipart form-data I am not able to set fieldName diff from fileName

    I am creating formdata like below : Server requires form-data as : name=file ; filename= The value of name is expected to "file" on sever and filename is the actual filename Using the current mutipart I can only generate something like : 1> Content-Disposition;form-data; name=file ; filename=file 2> Content-Disposition;form-data; name=a.txt ; filename=a.txt

    Cannot get something like : 3>Content-Disposition;form-data; name=file ; filename=a.txt

    ------WebKitFormBoundaryePkpFF7tjBAqx29L Content-Disposition: form-data; name="file"; filename="a.txt" Content-Type: application/octet**

    ... contents of file goes here ...

    ------WebKitFormBoundaryePkpFF7tjBAqx29L--

    Here is the snippet I use and then pass it to gentelman's client.Use(multipart.Data(dt))

    fields := map[string]common.Values{"attachment": {"SomeValue"}} dt := common.FormData{ Data: fields, Files: []common.FormFile{{Name: , Reader: r}}, }

    It seems that when I pass "Name" in Formfile the fieldname also switches to "Name" . It seems in the file :plugins/multipart/multipart.go (in function writeFile this change happens before passing these values to multipartWriter.CreateFormFile(fileName, file.Name)).

    opened by fsduser 0
  • gentleman/proxy error in example and code

    gentleman/proxy error in example and code

    cli.Use(proxy.Set([]string{"http://proxy:3128", "http://proxy2:3128"})) This is from example but in code proxy.Set accept map[string]string with parameters by url schema, and same proxy. It's all nothing - but there is one nuance, map take a schema by request schema. It's not good, but you can adapt. But map can only contain unique keys. So we can use only 1 proxy per 1 scheme. This is not good.

    opened by eGGshke 0
  • Default gentleman use doesn't seem to participate in TLS session resumption

    Default gentleman use doesn't seem to participate in TLS session resumption

    We use gentleman as a client library between services in our product. We began noticing particular services receiving a lot of restart events due to CPU limit violations. When we enabled pprof for those services and took a look at the data, there was a lot of time being spent in addTLS - https://github.com/golang/go/blob/master/src/net/http/transport.go#L1508. This was true even across multiple calls to the same URL and service. This also matched with our observation that the restarts were more frequent with TLS across the full environment instead of it being terminated at the reverse proxy.

    We were able to replicate this in a small test outside of the larger application. I've pushed that test to http://github.com/caseyhadden/gentle-http-test. This test runs 10 identical GET calls against httpbin.org using both HTTPS and HTTP. The output below is fairly representative of the type of difference we see. We ran the test cases against our internal server so latency wouldn't have as much of a chance to affect results, but this is what I came up with as something showing the issue and could be shared. The regular "gentleman" usage is consistently around 2x as slow in this microbenchmark. We stuck the grequests library in as another "not bare net/http" representative.

    *** HTTPS
    2021/02/22 12:49:32 nethttp: 757.704072ms
    2021/02/22 12:49:34 gentleman: 1.266130582s
    2021/02/22 12:49:35 gentleman transport: 899.396886ms
    2021/02/22 12:49:35 grequests: 461.614291ms
    *** HTTP
    2021/02/22 12:49:36 nethttp: 968.82515ms
    2021/02/22 12:49:37 gentleman: 843.205109ms
    2021/02/22 12:49:38 gentleman transport: 724.24357ms
    2021/02/22 12:49:38 grequests: 769.325955ms
    

    When you check the test, you can see that the difference between "gentleman" and "gentleman transport" instances is that the latter calls cli.Use(transport.Set(http.DefaultTransport)) in order to reuse the HTTP transport instance across the client.

    We used a basic load testing tool against one part of the application with such a change. That showed a marked improvement in both throughput and memory usage.

    I guess I have a few questions related to this issue:

    1. Do you expect to see this difference for TLS and non-TLS connections seemingly related to TLS resumption protocols?
    2. Should the default gentleman client have a better setup for this?
    3. If not, is there some better way than setting the transport that you're expecting folks to have TLS connections function more performantly?
    opened by caseyhadden 0
  • The checksum on sum.golang.org is incorrect

    The checksum on sum.golang.org is incorrect

    $ go mod tidy
    gopkg.in/h2non/[email protected]: verifying module: checksum mismatch
    	downloaded: h1:Qq4Sk2jY7GoYBu8C5rZF/+RU9GdcnzPN9v3z5aBBGg8=
    	sum.golang.org: h1:9R3K6CFYd/RdXDLi0pGXwaPnRx/pn5EZlrN3VkNygWc=
    
    SECURITY ERROR
    This download does NOT match the one reported by the checksum server.
    The bits may have been replaced on the origin server, or an attacker may
    have intercepted the download attempt.
    
    For more information, see 'go help module-auth'.
    
    opened by kleijnweb 0
  • http/1.1 and http/2

    http/1.1 and http/2

    Supposedly, the newer version GO (I'm using GO 1.14.9) would use http/2 with TLS by default on the client-side when the server-side supports http/2. I expect to see HTTP/2 getting logged on the server-side when I use the following code (gentleman.V2 2.0.4)

    cli := gentleman.New()
    cli.URL("https://mytestserver.com")
    cli.Request().Send()
    

    However, I got this logged on the server-side,

    [12/Oct/2020:15:43:03 +0000] "GET / HTTP/1.1" 200 14195 "-" "gentleman/2.0.4"

    If I do this,

    cli := gentleman.New()
    cli.Use(transport.Set(http.DefaultTransport))
    cli.URL("https://mytestserver.com")
    cli.Request().Send()
    

    I got this logged on the server-side,

    [12/Oct/2020:15:58:12 +0000] "GET / HTTP/2.0" 200 14175 "-" "gentleman/2.0.4"

    Of course, a simple call from the http package like below also got HTTP/2.0 logged on the server-side,

    http.Get("https://mytestserver.com")

    I've not found the gentleman documentation about this behavior (probably, I'm not thorough enough).

    Any comments?

    enhancement 
    opened by eraserx99 1
Releases(v2.0.5)
  • v2.0.4(Feb 17, 2020)

  • v2.0.3(Oct 13, 2017)

  • v2.0.2(Sep 20, 2017)

  • v2.0.1(Sep 14, 2017)

    Changes

    v2.0.1 / 2017-09-14

    • fix(#35): do not stop on parent middleware layer in error phase
    • fix(travis): gentleman requires Go 1.7+
    • fix(examples): formatting issue
    • fix(examples): formatting issue
    • Merge branch 'master' of https://github.com/h2non/gentleman
    • feat(#33): add error handling example
    • refactor(docs): split community based plugins
    • Merge pull request #34 from izumin5210/logger
    • Add link for gentleman-logger
    • Merge pull request #31 from djui/patch-1
    • Fix typo
    • feat(docs): add gock reference
    Source code(tar.gz)
    Source code(zip)
  • v2.0.0(Jul 28, 2017)

    Changes

    v2.0.0 / 2017-07-26

    • fix(merge): resolve conflicts from master
    • feat(docs): add versions notes
    • refactor(docs): update version badge
    • refactor(docs): update version badge
    • Merge branch 'master' of https://github.com/h2non/gentleman into v2
    • rerafctor(docs): use v2 godoc links
    • refactor(examples): normalize import statements
    • fix(bodytype): add Type() alias public method
    • feat(History): update changes
    • feat(version): bump to v2.0.0-rc.0
    • fix: format code accordingly
    • fix(travis): use Go 1.7+
    • feat(v2): push v2 preview release
    Source code(tar.gz)
    Source code(zip)
  • v1.0.4(Jul 28, 2017)

    Changes

    v1.0.4 / 2017-05-31

    • Merge pull request #28 from eyalpost/master
    • refactor: remove finalizers (this introduces a minor breaking change)
    • feat(docs): add v2 notice
    Source code(tar.gz)
    Source code(zip)
  • v1.0.3(Mar 17, 2017)

    v1.0.3 / 2017-03-17

    • fix(#23): persist context data across body updates
    • fix(lint): use proper code style
    • fix(test): several lint issues
    • fix(#22): adds support for multipart multiple form values. This introduces a minor interface breaking change
    • feat(travis): add Go 1.8 CI support
    Source code(tar.gz)
    Source code(zip)
Httpx - a fast and multi-purpose HTTP toolkit allow to run multiple probers using retryablehttp library

httpx is a fast and multi-purpose HTTP toolkit allow to run multiple probers using retryablehttp library, it is designed to maintain the result reliability with increased threads.

Will Pape 0 Feb 3, 2022
Http client call for golang http api calls

httpclient-call-go This library is used to make http calls to different API services Install Package go get

pzenteno 16 Oct 7, 2022
fhttp is a fork of net/http that provides an array of features pertaining to the fingerprint of the golang http client.

fhttp The f stands for flex. fhttp is a fork of net/http that provides an array of features pertaining to the fingerprint of the golang http client. T

Flexagon 61 Jan 1, 2023
Fast HTTP package for Go. Tuned for high performance. Zero memory allocations in hot paths. Up to 10x faster than net/http

fasthttp Fast HTTP implementation for Go. Currently fasthttp is successfully used by VertaMedia in a production serving up to 200K rps from more than

Aliaksandr Valialkin 18.9k Jan 2, 2023
Speak HTTP like a local. (the simple, intuitive HTTP console, golang version)

http-gonsole This is the Go port of the http-console. Speak HTTP like a local Talking to an HTTP server with curl can be fun, but most of the time it'

mattn 65 Jul 14, 2021
NATS HTTP Round Tripper - This is a Golang http.RoundTripper that uses NATS as a transport.

This is a Golang http.RoundTripper that uses NATS as a transport. Included is a http.RoundTripper for clients, a server that uses normal HTTP Handlers and any existing http handler mux and a Caddy Server transport.

R.I.Pienaar 81 Dec 6, 2022
Simple HTTP package that wraps net/http

Simple HTTP package that wraps net/http

Kris 0 Jan 17, 2022
Http-conection - A simple example of how to establish a HTTP connection using Golang

A simple example of how to establish a HTTP connection using Golang

Jonathan Gonzaga 0 Feb 1, 2022
An enhanced http client for Golang

go-http-client An enhanced http client for Golang Documentation on go.dev ?? This package provides you a http client package for your http requests. Y

Furkan Bozdag 51 Dec 23, 2022
An enhanced HTTP client for Go

Heimdall Description Installation Usage Making a simple GET request Creating a hystrix-like circuit breaker Creating a hystrix-like circuit breaker wi

Gojek 2.4k Jan 9, 2023
Enriches the standard go http client with retry functionality.

httpRetry Enriches the standard go http client with retry functionality using a wrapper around the Roundtripper interface. The advantage of this libra

Alexander Gehres 28 Dec 10, 2022
http client for golang

Request HTTP client for golang, Inspired by Javascript-axios Python-request. If you have experience about axios or requests, you will love it. No 3rd

Monaco.HappyHacking 223 Dec 18, 2022
Simple HTTP and REST client library for Go

Resty Simple HTTP and REST client library for Go (inspired by Ruby rest-client) Features section describes in detail about Resty capabilities Resty Co

Go Resty 7.1k Jan 1, 2023
A nicer interface for golang stdlib HTTP client

rq A nicer interface for golang stdlib HTTP client Documents rq: here client: here jar: here Why? Because golang HTTP client is a pain in the a... Fea

Ddo 44 Dec 12, 2022
A Go HTTP client library for creating and sending API requests

Sling Sling is a Go HTTP client library for creating and sending API requests. Slings store HTTP Request properties to simplify sending requests and d

Dalton Hubble 1.5k Jan 7, 2023
a Go HTTP client with timeouts

go-httpclient requires Go 1.1+ as of v0.4.0 the API has been completely re-written for Go 1.1 (for a Go 1.0.x compatible release see 1adef50) Provides

Matt Reiferson 291 Nov 10, 2022
GoRequest -- Simplified HTTP client ( inspired by nodejs SuperAgent )

GoRequest GoRequest -- Simplified HTTP client ( inspired by famous SuperAgent lib in Node.js ) "Shooting Requests like a Machine Gun" - Gopher Sending

Theeraphol Wattanavekin 3.2k Jan 1, 2023
gout to become the Swiss Army Knife of the http client @^^@---> gout 是http client领域的瑞士军刀,小巧,强大,犀利。具体用法可看文档,如使用迷惑或者API用得不爽都可提issues

gout gout 是go写的http 客户端,为提高工作效率而开发 构架 feature 支持设置 GET/PUT/DELETE/PATH/HEAD/OPTIONS 支持设置请求 http header(可传 struct,map,array,slice 等类型) 支持设置 URL query(可

guonaihong 1.1k Dec 29, 2022
Retry, Race, All, Some, etc strategies for http.Client calls

reqstrategy Package reqstrategy provides functions for coordinating http.Client calls. It wraps typical call strategies like making simultaneous reque

Sergiy Yavorsky 11 Apr 30, 2021