go-rest-http-blaster is a Go HTTP client that provides built-in support for circuit breakers, statsd, and open tracing.
Go straight to the examples
It is recommended that you initialize go-rest-http-blaster
with package-level defaults when your application bootstraps.
To do this, use the Defaults
struct in the SetDefaults
function.
type Defaults struct {
// ServiceName is the name of the calling service
ServiceName string
// TracerProviderFunc is a function that provides
// the opentracing.Tracer for tracing HTTP requests
TracerProviderFunc func(ctx context.Context, operationName string, r *http.Request) (*http.Request, opentracing.Span)
// RequestIDProviderFunc is a function that provides the
// parent Request id used in tracing the caller's Request.
// If this function is not set, the client will generate
// a new UUID for the Request id.
RequestIDProviderFunc func(ctx context.Context) (string, bool)
// RequestSourceProviderFunc is a function that provides
// the Request-Source header
RequestSourceProviderFunc func(ctx context.Context) (string, bool)
// UserAgent is a package-level user agent header used for
// each outgoing request
UserAgent string
// RequireHeaders will cancel any request and return an error if any of the following
// headers are missing:
// Request-ID
// Request-Source
// Calling-Service
RequireHeaders bool
// StatsdRate is the statsd reporting rate
StatsdRate float64
}
It is strongly recommended that you provide these defaults. However, if they are not provided, go-rest-http-blaster
will provide its own defaults:
ServiceName
- if the service name is not provided,go-rest-http-blaster
will look for an environment variable calledSERVICE_NAME
. If that variable doesnt exist,blaster
will fall back toHOSTNAME
TracerProviderFunc
- The function that will wrap the request for http tracing. No function is used if not providedRequestIDProviderFunc
- function that provides theRequest-ID
header. If no function is set, theRequest-ID
header will not be setRequestSourceProviderFunc
function that provides theRequest-Source
header. If no function is set, theRequest-Source
header will not be sent.UserAgent
User supplied user-agent string. Defaults to[service name]-[hostname]
RequireHeaders
- will return an error if the REQ014 headers are not provided when the request is launchedStatsdRate
- The statsd reporting rate.
Typical usage, with jelly functions:
package main
import (
"fmt"
blaster "github.com/joelhill/go-rest-http-blaster"
)
const serviceName = "my-service"
func main() {
blaster.SetDefaults(&blaster.Defaults{
ServiceName: serviceName,
RequestIDProviderFunc: uuidFunc,
})
}
go-rest-http-blaster
does not force you to use a circuit breaker, or, if you do, which library to use. go-rest-http-blaster
is built to implicitly support the Sony GoBreaker library. The circuit
breaker implementation must conform to the following interface:
type CircuitBreakerPrototype interface {
Execute(func() (interface{}, error)) (interface{}, error)
}
To set the circuit breaker, use the SetCircuitBreaker
function.
By default, blaster
sets the Content-Type
header to application/json
. You may override this header if
you are sending a different content type by using the SetContentType
function.
There are two ways to access the response payload from go-rest-http-blaster
. If you want to access the raw bytes
returned in the response, or if you are not expecting a JSON response payload, you can use the RawResponse
function to access the response bytes.
The more common way to access the payload is to use one of the Saturate
functions. You provide blaster
with an empty struct pointer, and it will be saturated when the response returns. Each works slightly
differently depending on what you're trying to do:
WillSaturate
- Use this function to saturate the struct you expect when the request succeeds. A request is considered successful if the response code is between 200 and 299, inclusive.
WillSaturateOnError
- Use this function to saturate the struct you expect when the request fails. A request is considered a
failure if the response code is below 200 or above 299. Also note that this struct will only be saturated
if the actual response is an error.
blaster
will not saturate any response if the error originated at the caller.
- Use this function to saturate the struct you expect when the request fails. A request is considered a
failure if the response code is below 200 or above 299. Also note that this struct will only be saturated
if the actual response is an error.
WillSaturateWithStatusCode
-
Use this function to saturate the struct you expect when a specific status code is returned.
Structs provided here take precedence overWillSaturate
andWillSaturateOnError
. For example, if a specific response is expected for a401
error, and a struct is provided for401
, then that struct will e saturated when the response is401
, regardless what was provided inWillSaturateOnError
-
go-rest-http-blaster
provides the following functions for convenience:
Get
- perform an HTTP GET request with no outgoing payloadPost
- perform an HTTP POST request with an outgoing payloadPut
- perform an HTTP PUT request with an outgoing payloadPatch
- perform an HTTP PATCH request with an outgoing payloadDelete
- perform an HTTP DELETE request with no outgoing payload
The following headers are set for every request:
Request-ID
Content-Type
User-Agent
- the user agent is in the form{{service name}}-{{namespace}}-{{tenancy}}
Calling-Service
Additionally, the Content-Length
header is set for all requests with an outgoing payload (POST
, PUT
, PATCH
)
You can set additional headers by using the SetHeader
function:
c.SetHeader("X-Forwarded-For", "127.0.0.1")
package main
import (
"context"
"log"
blaster "github.com/joelhill/go-rest-http-blaster"
)
type ResponsePayload struct {
Foo string `json:"foo"`
Fizz string `json:"fizz"`
}
func main() {
ctx := context.Background()
// make you a client
c, err := blaster.NewClient("http://localhost:8080/foo/bar")
if err != nil {
log.Fatalln(err.Error())
}
// make empty struct pointer
payload := &ResponsePayload{}
// we will saturate the response with this GET
c.WillSaturate(payload)
statusCode, err := c.Get(ctx)
log.Println(statusCode)
log.Printf("%+v", payload)
}
package main
import (
"context"
"log"
blaster "github.com/joelhill/go-rest-http-blaster"
)
func main() {
ctx := context.Background()
// make you a client
c, err := blaster.NewClient("http://localhost:8080/foo/bar/xml")
if err != nil {
log.Fatalln(err.Error())
}
// xml? it could happen ;)
c.SetHeader("Accept", "application/xml")
c.KeepRawResponse()
// run the request
statusCode, err := c.Get(ctx)
if err != nil {
log.Fatalln(err.Error())
}
// get the raw byte slice
data := c.RawResponse()
log.Println(statusCode)
log.Printf("%+v", data)
}
package main
import (
"context"
"log"
blaster "github.com/joelhill/go-rest-http-blaster"
)
type Payload struct {
Foo string `json:"foo"`
Fizz string `json:"fizz"`
}
type ResponsePayload struct {
Bar string `json:"bar"`
Baz string `json:"baz"`
}
func main() {
payload := &Payload{
Foo: "this is foo",
Fizz: "this is fizz",
}
ctx := context.Background()
// make you a client
c, err := blaster.NewClient("http://localhost:8080/foo/bar")
if err != nil {
log.Fatalln(err.Error())
}
// make the empty struct pointer
response := &ResponsePayload{}
// we will saturate the response with this post
c.WillSaturate(response)
statusCode, err := c.Post(ctx, payload)
if err != nil {
log.Fatalln(err.Error())
}
log.Println(statusCode)
log.Printf("%+v", response)
}
package main
import (
"context"
"log"
blaster "github.com/joelhill/go-rest-http-blaster"
)
type SuccessPayload struct {
Foo string `json:"foo"`
Fizz string `json:"fizz"`
}
type ErrorPayload struct {
Reason string `json:"reason"`
Code int `json:"code"`
}
type ForbiddenPayload struct {
Domain string `json:"domain"`
UserID string `json:"userID"`
}
func main() {
ctx := context.Background()
// make you a client
c, err := blaster.NewClient("http://localhost:8080/foo/bar")
if err != nil {
log.Fatalln(err.Error())
}
// make empty struct pointers
payload := &SuccessPayload{}
errorPayload := &ErrorPayload{}
forbiddenPayload := &ForbiddenPayload{}
// this gets saturated on error
c.WillSaturateOnError(errorPayload)
// this gets saturated on 403
c.WillSaturateWithStatusCode(403, forbiddenPayload)
// we will saturate the response with this GET
c.WillSaturate(payload)
statusCode, err := c.Get(ctx)
log.Println(statusCode)
if statusCode == 403 {
log.Printf("%+v", forbiddenPayload)
} else if c.StatusCodeIsError() {
log.Printf("%+v", errorPayload)
} else {
log.Printf("%+v", payload)
}
}