client

package module
v1.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 3, 2026 License: Apache-2.0 Imports: 25 Imported by: 32

README

go-client

This repository contains a generic HTTP client which can be adapted to provide:

  • General HTTP methods for GET and POST of data
  • Ability to send and receive JSON, plaintext and XML data
  • Ability to send files and data of type multipart/form-data
  • Ability to send data of type application/x-www-form-urlencoded
  • Debugging capabilities to see the request and response data
  • Streaming text and JSON events
  • OpenTelemetry tracing for distributed observability
  • OAuth 2.0 client flows including discovery, dynamic client registration, authorization code, device authorization, and client credentials

API Documentation: https://pkg.go.dev/github.com/mutablelogic/go-client

There are also some example clients which use this library:

There are also utility packages for working with multipart file uploads, transport middleware, and OpenTelemetry:

Compatibility with go version 1.25 and above.

Basic Usage

The following example shows how to decode a response from a GET request to a JSON endpoint:

package main

import (
    "fmt"
    "log"

    client "github.com/mutablelogic/go-client"
)

func main() {
    // Create a new client
    c, err := client.New(client.OptEndpoint("https://api.example.com/api/v1"))
    if err != nil {
        log.Fatal(err)
    }

    // Send a GET request, populating a struct with the response
    var response struct {
        Message string `json:"message"`
    }
    if err := c.Do(nil, &response, client.OptPath("test")); err != nil {
        log.Fatal(err)
    }

    // Print the response
    fmt.Println(response.Message)
}

Various options can be passed to the client New method to control its behaviour:

  • OptEndpoint(value string) sets the endpoint for all requests
  • OptTimeout(value time.Duration) sets the timeout on any request, which defaults to 30 seconds. Timeouts can be ignored on a request-by-request basis using the OptNoTimeout option (see below).
  • OptUserAgent(value string) sets the user agent string on each API request.
  • OptTrace(w io.Writer, verbose bool)Deprecated. Use OptTransport with transport.NewLogging instead. See the Transport Middleware section below for details.
  • OptStrict() turns on strict content type checking on anything returned from the API.
  • OptRateLimit(value float32) sets the limit on number of requests per second and the API will sleep to regulate the rate limit when exceeded.
  • OptReqToken(value Token) sets a request token for all client requests. This can be overridden by the client for individual requests using OptToken (see below).
  • OptSkipVerify() skips TLS certificate domain verification.
  • OptHeader(key, value string) appends a custom header to each request.
  • OptTransport(fn func(http.RoundTripper) http.RoundTripper) inserts a transport middleware that wraps every request made by this client. Multiple calls stack in order so the first call becomes the outermost layer. Use this to plug in any pkg/transport middleware.
  • OptTracer(tracer trace.Tracer) sets an OpenTelemetry tracer for distributed tracing. Span names default to "METHOD /path" format. See the OpenTelemetry section below for more details.

Redirect Handling

The client automatically follows HTTP redirects (3xx responses) for GET and HEAD requests, up to a maximum of 10 redirects. Unlike the default Go HTTP client behavior:

  • The HTTP method is preserved (HEAD stays HEAD, GET stays GET)
  • Request headers are preserved across redirects
  • For security, Authorization and Cookie headers are stripped when redirecting to a different host

This behavior ensures that redirects work correctly for APIs that use CDNs or load balancers with temporary redirects.

Usage with a payload

The first argument to the Do method is the payload to send to the server, when set. You can create a payload using the following methods:

  • client.NewRequest() returns a new empty payload which defaults to GET.
  • client.NewJSONRequest(payload any, accept string) returns a new request with a JSON payload which defaults to POST.
  • client.NewMultipartRequest(payload any, accept string) returns a new request with a Multipart Form data payload which defaults to POST.
  • client.NewStreamingMultipartRequest(payload any, accept string) returns a new request with a Multipart Form data payload that streams data rather than buffering in memory. Useful for large file uploads. The returned payload implements io.Closer and is automatically closed by the HTTP client after the request completes.
  • client.NewFormRequest(payload any, accept string) returns a new request with a Form data payload which defaults to POST.

For example,

package main

import (
    "fmt"
    "log"

    client "github.com/mutablelogic/go-client"
)

func main() {
    // Create a new client
    c, err := client.New(client.OptEndpoint("https://api.example.com/api/v1"))
    if err != nil {
        log.Fatal(err)
    }

    // Send a POST request with JSON payload
    var request struct {
        Prompt string `json:"prompt"`
    }
    var response struct {
        Reply string `json:"reply"`
    }
    request.Prompt = "Hello, world!"
    payload := client.NewJSONRequest(request, client.ContentTypeJson)
    if err := c.Do(payload, &response, client.OptPath("test")); err != nil {
        log.Fatal(err)
    }

    // Print the response
    fmt.Println(response.Reply)
}

You can also implement your own payload by implementing the Payload interface:

type Payload interface {
  io.Reader

  // The method to use to send the payload
  Method() string

  // The content type of the payload
  Type() string

  // The content type which is accepted as a response, or empty string if any
  Accept() string
}

Request options

The signature of the Do method is as follows:

type Client interface {
    // Perform request and wait for response
    Do(in Payload, out any, opts ...RequestOpt) error

    // Perform request and wait for response, with context for cancellation
    DoWithContext(ctx context.Context, in Payload, out any, opts ...RequestOpt) error
}

If you pass a context to the DoWithContext method, then the request can be cancelled using the context in addition to the timeout. Various options can be passed to modify each individual request when using the Do method:

  • OptReqEndpoint(value string) sets the endpoint for the request
  • OptPath(value ...string) appends path elements onto a request endpoint
  • OptToken(value Token) adds an authorization header (overrides the client OptReqToken option)
  • OptQuery(value url.Values) sets the query parameters to a request
  • OptReqHeader(name, value string) sets a custom header to the request
  • OptNoTimeout() disables the timeout on the request, which is useful for long running requests
  • OptReqTransport(fn func(http.RoundTripper) http.RoundTripper) inserts a transport middleware for this single request only. Multiple calls stack in order; the first becomes the outermost. The middleware is applied on a per-request copy of the client and does not affect other requests.
  • OptTextStreamCallback(fn TextStreamCallback) allows you to set a callback function to process a streaming text response of type text/event-stream, where TextStreamCallback is func(TextStreamEvent) error. See below for more details.
  • OptJsonStreamCallback(fn JsonStreamCallback) allows you to set a callback for JSON streaming responses, where JsonStreamCallback is func(any) error. See below for more details.

Authentication

The authentication token can be set as follows:

package main

import (
    "log"
    "os"

    client "github.com/mutablelogic/go-client"
)

func main() {
    // Create a new client
    c, err := client.New(
        client.OptEndpoint("https://api.example.com/api/v1"),
        client.OptReqToken(client.Token{
            Scheme: "Bearer",
            Value:  os.Getenv("API_TOKEN"),
        }),
    )
    if err != nil {
        log.Fatal(err)
    }

    // Use the client...
    _ = c
}

You can also set the token on a per-request basis using the OptToken option in call to the Do method.

OAuth 2.0

The pkg/oauth package provides a full OAuth 2.0 client supporting discovery (RFC 8414), dynamic client registration (RFC 7591), authorization code + PKCE, device authorization, and client credentials flows. Use c.OAuth() to run the full flow using the client's own HTTP transport — the Authorize* methods attach the resulting credentials to the client automatically, and the token is refreshed transparently before each request:

import (
    "context"

    client "github.com/mutablelogic/go-client"
    oauth "github.com/mutablelogic/go-client/pkg/oauth"
)

func main() {
    ctx := context.Background()

    // Create the API client first so its HTTP transport is reused for all OAuth calls.
    c, err := client.New(client.OptEndpoint("https://api.example.com"))
    if err != nil {
        log.Fatal(err)
    }

    // c.OAuth() returns a flow that injects c's HTTP transport automatically.
    flow := c.OAuth()

    // 1. Discover server metadata
    metadata, err := flow.Discover(ctx, "https://example.com")
    if err != nil {
        log.Fatal(err)
    }

    // 2. Authorize (client credentials shown; see pkg/oauth for browser/device flows)
    creds, err := flow.AuthorizeWithCredentials(ctx,
        &oauth.OAuthCredentials{
            Metadata:     metadata,
            ClientID:     "my-client",
            ClientSecret: "my-secret",
        },
    )
    if err != nil {
        log.Fatal(err)
    }

    // Persist rotated refresh tokens
    creds.OnRefresh = func(c *oauth.OAuthCredentials) error {
        return saveToDisk(c) // your persistence logic
    }

    // 3. Attach credentials; the client refreshes the token automatically on expiry.
    //    AuthorizeWithCredentials (and all other Authorize* methods) attach credentials
    //    to the client automatically — no further setup needed.
}

See pkg/oauth/README.md for the full API including browser-based, device, and manual code-paste flows, token introspection, and revocation.

Form submission

You can create a payload with form data:

  • client.NewFormRequest(payload any, accept string) returns a new request with a Form data payload which defaults to POST.
  • client.NewMultipartRequest(payload any, accept string) returns a new request with a Multipart Form data payload which defaults to POST. This is useful for file uploads.
  • client.NewStreamingMultipartRequest(payload any, accept string) returns a new request that streams the multipart data rather than buffering in memory. This is recommended for large file uploads to avoid high memory usage. The returned payload implements io.Closer and is automatically closed by the HTTP client after the request completes.

The payload should be a struct where the fields are converted to form tuples. File uploads require a field of type multipart.File. For example,

package main

import (
    "log"
    "strings"

    client "github.com/mutablelogic/go-client"
    multipart "github.com/mutablelogic/go-client/pkg/multipart"
)

type FileUpload struct {
    File multipart.File `json:"file"`
}

func main() {
    // Create a new client
    c, err := client.New(client.OptEndpoint("https://api.example.com/api/v1"))
    if err != nil {
        log.Fatal(err)
    }

    // Create a file upload request
    request := FileUpload{
        File: multipart.File{
            Path: "helloworld.txt",
            Body: strings.NewReader("Hello, world!"),
        },
    }

    // Upload a file
    var response any
    payload, err := client.NewMultipartRequest(request, "*/*")
    if err != nil {
        log.Fatal(err)
    }
    if err := c.Do(payload, &response, client.OptPath("upload")); err != nil {
        log.Fatal(err)
    }
}

Unmarshalling responses

You can implement your own unmarshalling of responses by implementing the Unmarshaler interface:

type Unmarshaler interface {
  Unmarshal(header http.Header, r io.Reader) error
}

The first argument to the Unmarshal method is the HTTP header of the response, and the second argument is the body of the response. You can return one of the following error values from Unmarshal to indicate how the client should handle the response:

  • nil to indicate successful unmarshalling.
  • httpresponse.ErrNotImplemented (from github.com/mutablelogic/go-server/pkg/httpresponse) to fall back to the default unmarshaling behaviour. In this case, the body will be unmarshaled based on the Content-Type header:
    • application/json → any JSON-decodable type
    • application/xml or text/xml → any XML-decodable type
    • text/plain*string (value set to the body text), *[]byte (raw bytes), or io.Writer (body copied into it)
  • Any other error to indicate a failure in unmarshaling.

Text Streaming Responses

The client implements a streaming text event callback which can be used to process a stream of text events, as per the Mozilla specification.

In order to process streamed events, pass the OptTextStreamCallback() option to the request with a callback function, which should have the following signature:

func Callback(event client.TextStreamEvent) error {
    // Finish processing successfully
    if event.Event == "close" {
        return io.EOF
    }

    // Decode the data into a JSON object
    var data map[string]any
    if err := event.Json(data); err != nil {
        return err
    }

    // Return success - continue streaming
    return nil
}

The TextStreamEvent object has the following fields:

  • Id string — the event ID (id: field)
  • Event string — the event type (event: field; defaults to "message")
  • Data string — the event data (data: fields joined with \n)
  • Retry time.Duration — the server-requested reconnect delay (retry: field)

If you return an error of type io.EOF from the callback, then the stream will be closed. Similarly, if you return any other error the stream will be closed and the error returned.

Usually, you would pair this option with OptNoTimeout to prevent the request from timing out.

SSE Reconnect

For reconnect support, use NewTextStream() and Decode() directly rather than OptTextStreamCallback. After Decode returns, the decoder holds the last event ID and server-requested retry delay:

stream := client.NewTextStream()
if err := stream.Decode(r, callback); err != nil {
    // reconnect: set Last-Event-ID header and wait
    req.Header.Set("Last-Event-ID", stream.LastEventID())
    time.Sleep(stream.RetryDuration())
}

When the Accept type of the payload is text/event-stream or application/x-ndjson, the client automatically adds Cache-Control: no-cache and X-Accel-Buffering: no request headers to prevent intermediate proxies (including Nginx) from buffering the stream.

JSON Streaming Responses

The client decodes JSON streaming responses by passing a callback function to the OptJsonStreamCallback() option. The callback with signature func(any) error is called for each JSON object in the stream, where the argument is the same type as the object in the request.

You can return an error from the callback to stop the stream and return the error, or return io.EOF to stop the stream immediately and return success.

Transport Middleware

The pkg/transport package provides composable http.RoundTripper middleware. All middleware follows the same constructor pattern New*(... , parent http.RoundTripper) and falls back to http.DefaultTransport when parent is nil. Middleware can be composed using OptTransport (client-wide) or OptReqTransport (per-request).

Logging Transport

transport.NewLogging logs every request and response to an io.Writer. When verbose is true the request and response bodies are also printed; text/event-stream bodies are printed line-by-line as events arrive rather than buffered:

import (
    client "github.com/mutablelogic/go-client"
    "github.com/mutablelogic/go-client/pkg/transport"
    "os"
)

c, err := client.New(
    client.OptEndpoint("https://api.example.com"),
    client.OptTransport(func(next http.RoundTripper) http.RoundTripper {
        return transport.NewLogging(os.Stderr, next, true)
    }),
)

The convenience client option OptTrace(w io.Writer, verbose bool) (deprecated) is equivalent and internally calls transport.NewLogging.

Recorder Transport

transport.NewRecorder captures the HTTP status code and response headers of the most recent response. It is safe for concurrent use:

var rec *transport.Recorder

c, err := client.New(
    client.OptEndpoint("https://api.example.com"),
    client.OptTransport(func(next http.RoundTripper) http.RoundTripper {
        rec = transport.NewRecorder(next)
        return rec
    }),
)

// After a request:
fmt.Println(rec.StatusCode())     // e.g. 200
fmt.Println(rec.Header())         // cloned http.Header map
rec.Reset()                       // clear recorded values
OTel Transport

transport.NewTransport wraps an http.RoundTripper so that every hop produces an OpenTelemetry client span. See the OpenTelemetry section below for details.

OpenTelemetry

The pkg/otel package provides OpenTelemetry tracing utilities for both HTTP clients and servers.

Creating a Tracer Provider

Use otel.NewProvider to create a tracer provider that exports spans to an OTLP endpoint:

package main

import (
    "context"
    "log"
    "net/http"

    client "github.com/mutablelogic/go-client"
    "github.com/mutablelogic/go-client/pkg/otel"
    "github.com/mutablelogic/go-client/pkg/transport"
)

func main() {
    // Create a provider with an OTLP endpoint
    // Supports http://, https://, grpc://, and grpcs:// schemes
    provider, err := otel.NewProvider(
        "https://otel-collector.example.com:4318",  // OTLP endpoint
        "api-key=your-api-key",                     // Optional headers (comma-separated key=value pairs)
        "my-service",                               // Service name
        otel.Attr{Key: "environment", Value: "production"},  // Optional attributes
    )
    if err != nil {
        log.Fatal(err)
    }
    defer provider.Shutdown(context.Background())

    // Get a tracer from the provider
    tracer := provider.Tracer("my-service")

    // Use the tracer with go-client via OptTransport
    c, err := client.New(
        client.OptEndpoint("https://api.example.com"),
        client.OptTransport(func(next http.RoundTripper) http.RoundTripper {
            return transport.NewTransport(tracer, next)
        }),
    )
    if err != nil {
        log.Fatal(err)
    }

    // Use the client...
    _ = c
}
HTTP Client Tracing

OptTracer and transport.NewTransport both wrap the client's HTTP transport so that every HTTP call produces a client span — including OAuth token refresh calls and each redirect hop. Span names default to "METHOD /path" format. Attributes captured per span:

  • HTTP method, URL, and host
  • Request and response body sizes
  • HTTP status codes
  • Error recording for failed requests

Prefer composing via OptTransport so the transport layer stays explicit:

import (
    client "github.com/mutablelogic/go-client"
    "github.com/mutablelogic/go-client/pkg/transport"
)

c, err := client.New(
    client.OptEndpoint("https://api.example.com"),
    client.OptTransport(func(next http.RoundTripper) http.RoundTripper {
        return transport.NewTransport(tracer, next)
    }),
)

You can also call transport.NewTransport directly on any *http.Client:

httpClient.Transport = transport.NewTransport(tracer, httpClient.Transport)
HTTP Server Middleware

Use otel.HTTPHandler or otel.HTTPHandlerFunc to add tracing to your HTTP server:

package main

import (
    "context"
    "log"
    "net/http"

    "github.com/mutablelogic/go-client/pkg/otel"
)

func main() {
    // Create provider (see "Creating a Tracer Provider" above)
    provider, err := otel.NewProvider("https://otel-collector.example.com:4318", "", "my-server")
    if err != nil {
        log.Fatal(err)
    }
    defer provider.Shutdown(context.Background())

    tracer := provider.Tracer("my-server")

    // Wrap your handler with the middleware
    handler := otel.HTTPHandler(tracer)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello, World!"))
    }))

    // Or use HTTPHandlerFunc directly
    handlerFunc := otel.HTTPHandlerFunc(tracer)(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello, World!"))
    })

    http.Handle("/", handler)
    http.Handle("/func", handlerFunc)
    http.ListenAndServe(":8080", nil)
}

The middleware automatically:

  • Extracts trace context from incoming request headers (W3C Trace Context)
  • Creates server spans with HTTP method, URL, and host attributes
  • Captures response status codes
  • Marks spans as errors for 4xx and 5xx responses
Custom Spans

Use otel.StartSpan to create custom spans in your application:

ctx, endSpan := otel.StartSpan(tracer, ctx, "MyOperation",
    attribute.String("key", "value"),
)
// Use a closure to capture the final value of err when the function returns.
// defer endSpan(err) would capture err's value NOW (likely nil), not at return time.
defer func() { endSpan(err) }()

// Your code here...

License

This project is licensed under the Apache License, Version 2.0. See the LICENSE file for details, and the NOTICE file for copyright attribution.

What You Can Do

Under the Apache-2.0 license, you are free to:

  • Use — Use the software for any purpose, including commercial applications
  • Modify — Make changes to the source code
  • Distribute — Share the original or modified software
  • Sublicense — Grant rights to others under different terms
  • Patent Use — Use any patents held by contributors that cover this code

Requirements when redistributing:

  • Include a copy of the Apache-2.0 license
  • State any significant changes you made
  • Preserve copyright, patent, trademark, and attribution notices

Documentation

Overview

Implements a generic REST API client which can be used for creating gateway-specific clients. Basic usage:

package main

import (
	client "github.com/mutablelogic/go-client"
)

func main() {
	// Create a new client
	c := client.New(client.OptEndpoint("https://api.example.com/api/v1"))

	// Send a GET request, populating a struct with the response
	var response struct {
		Message string `json:"message"`
	}
	if err := c.Do(nil, &response, OptPath("test")); err != nil {
		// Handle error
	}

	// Print the response
	fmt.Println(response.Message)
}

Index

Constants

View Source
const (
	DefaultTimeout            = time.Second * 30
	DefaultUserAgent          = "github.com/mutablelogic/go-client"
	PathSeparator             = "/"
	ContentTypeAny            = types.ContentTypeAny
	ContentTypeJson           = types.ContentTypeJSON
	ContentTypeJsonStream     = "application/x-ndjson"
	ContentTypeTextXml        = types.ContentTypeTextXml
	ContentTypeApplicationXml = types.ContentTypeXML
	ContentTypeTextPlain      = types.ContentTypeTextPlain
	ContentTypeTextHTML       = "text/html"
	ContentTypeBinary         = types.ContentTypeBinary
)
View Source
const (
	Bearer = "Bearer"
)
View Source
const (
	// Mime type for text stream
	ContentTypeTextStream = types.ContentTypeTextStream
)

Variables

Functions

This section is empty.

Types

type Client

type Client struct {
	sync.Mutex
	*http.Client

	// Parent object for client options
	Parent any
	// contains filtered or unexported fields
}

func New

func New(opts ...ClientOpt) (*Client, error)

New creates a new client with options. OptEndpoint is required as an option to set the endpoint for all requests.

func (*Client) Do

func (client *Client) Do(in Payload, out any, opts ...RequestOpt) error

Do a JSON request with a payload, populate an object with the response and return any errors

func (*Client) DoWithContext

func (client *Client) DoWithContext(ctx context.Context, in Payload, out any, opts ...RequestOpt) error

Do a JSON request with a payload, populate an object with the response and return any errors. The context can be used to cancel the request

func (*Client) OAuth added in v1.4.0

func (client *Client) OAuth() *OAuthFlow

OAuth returns an OAuthFlow bound to this client. Every method on the returned flow uses the client's own HTTP transport so that proxy, TLS, and timeout settings are consistent across all OAuth and API calls. Pass a context directly to each flow method.

func (*Client) Request

func (client *Client) Request(req *http.Request, out any, opts ...RequestOpt) error

Do a HTTP request and decode it into an object

func (*Client) String

func (client *Client) String() string

type ClientOpt

type ClientOpt func(*Client) error

func OptEndpoint

func OptEndpoint(value string) ClientOpt

OptEndpoint sets the endpoint for all requests.

func OptHeader

func OptHeader(key, value string) ClientOpt

OptHeader appends a custom header to each request

func OptParent added in v1.0.2

func OptParent(v any) ClientOpt

OptParent sets the parent client for this client, which is used for setting additional client options in the parent

func OptRateLimit

func OptRateLimit(value float32) ClientOpt

OptRateLimit sets the limit on number of requests per second and the API will sleep when exceeded. For account tokens this is 1 per second

func OptReqToken

func OptReqToken(value Token) ClientOpt

OptReqToken sets a request token for all client requests. This can be overridden by the client for individual requests using OptToken.

func OptSkipVerify

func OptSkipVerify() ClientOpt

OptSkipVerify skips TLS certificate domain verification. It clones the client's own transport rather than mutating http.DefaultTransport.

func OptStrict

func OptStrict() ClientOpt

OptStrict turns on strict content type checking on anything returned from the API

func OptTimeout

func OptTimeout(value time.Duration) ClientOpt

OptTimeout sets the timeout on any request. By default, a timeout of 10 seconds is used if OptTimeout is not set

func OptTrace deprecated

func OptTrace(w io.Writer, verbose bool) ClientOpt

Deprecated: Use OptTransport with transport.NewLogging instead. OptTrace allows you to be the "man in the middle" on any requests so you can see traffic move back and forth. Setting verbose to true also displays the JSON response

func OptTracer added in v1.3.0

func OptTracer(tracer trace.Tracer) ClientOpt

OptTracer sets the OpenTelemetry tracer for this client. It wraps the underlying HTTP transport so that every HTTP call — including OAuth token refresh and redirect hops — produces a client span. Span names default to "METHOD /path" format.

func OptTransport added in v1.4.1

func OptTransport(fn func(http.RoundTripper) http.RoundTripper) ClientOpt

OptTransport inserts a transport middleware for all requests made by this client. Multiple calls stack in order; the first call becomes the outermost layer.

func OptUserAgent

func OptUserAgent(value string) ClientOpt

OptUserAgent sets the user agent string on each API request It is set to the default if empty string is passed

type JsonStreamCallback added in v1.0.9

type JsonStreamCallback func(v any) error

Callback for json stream events, return an error if you want to stop streaming with an error and io.EOF if you want to stop streaming and return success

type OAuthFlow added in v1.4.0

type OAuthFlow struct {
	// contains filtered or unexported fields
}

OAuthFlow is a thin wrapper around the pkg/oauth functions that automatically injects the client's own HTTP transport into every call. Obtain one by calling Client.OAuth(); then call Discover, Register, and the Authorize* methods in sequence, passing your context to each call.

func (*OAuthFlow) AuthorizeWithBrowser added in v1.4.0

func (f *OAuthFlow) AuthorizeWithBrowser(ctx context.Context, creds *oauth.OAuthCredentials, listener net.Listener, open oauth.OpenFunc, scopes ...string) (*oauth.OAuthCredentials, error)

AuthorizeWithBrowser performs the Authorization Code + PKCE flow via a local loopback HTTP server. listener must already be bound to the redirect port; open is called with the authorization URL and should open it in a browser. On success the credentials are automatically attached to the client.

func (*OAuthFlow) AuthorizeWithCode added in v1.4.0

func (f *OAuthFlow) AuthorizeWithCode(ctx context.Context, creds *oauth.OAuthCredentials, prompt oauth.PromptFunc, scopes ...string) (*oauth.OAuthCredentials, error)

AuthorizeWithCode performs the Authorization Code + PKCE flow via manual code paste. prompt is called with the authorization URL and must return the code the user copies from the browser. On success the credentials are automatically attached to the client.

func (*OAuthFlow) AuthorizeWithCredentials added in v1.4.0

func (f *OAuthFlow) AuthorizeWithCredentials(ctx context.Context, creds *oauth.OAuthCredentials, scopes ...string) (*oauth.OAuthCredentials, error)

AuthorizeWithCredentials performs the Client Credentials grant (RFC 6749 §4.4) for machine-to-machine flows where no user interaction is required. On success the credentials are automatically attached to the client.

func (*OAuthFlow) AuthorizeWithDevice added in v1.4.0

func (f *OAuthFlow) AuthorizeWithDevice(ctx context.Context, creds *oauth.OAuthCredentials, prompt oauth.DevicePromptFunc, scopes ...string) (*oauth.OAuthCredentials, error)

AuthorizeWithDevice performs the Device Authorization Grant (RFC 8628). prompt is called with the user code and verification URI that the user must visit on any browser-capable device. On success the credentials are automatically attached to the client.

func (*OAuthFlow) Discover added in v1.4.0

func (f *OAuthFlow) Discover(ctx context.Context, endpoint string) (*oauth.OAuthMetadata, error)

Discover fetches OAuth 2.0 / OIDC server metadata from the given endpoint (RFC 8414 / OIDC Discovery). The result can be passed directly to Register or any of the Authorize* methods.

func (*OAuthFlow) Introspect added in v1.4.0

func (f *OAuthFlow) Introspect(ctx context.Context) (*oauth.IntrospectionResponse, error)

Introspect queries the server for the active status and metadata of the current access token (RFC 7662). Returns an error if no credentials are attached to the client.

func (*OAuthFlow) Refresh added in v1.4.0

func (f *OAuthFlow) Refresh(ctx context.Context) error

Refresh exchanges the current credentials' refresh token (or re-runs the client credentials grant) for a fresh access token. It is a no-op when the token is still valid. OnRefresh is called if a new token is obtained. Returns an error if no credentials are attached to the client.

func (*OAuthFlow) Register added in v1.4.0

func (f *OAuthFlow) Register(ctx context.Context, metadata *oauth.OAuthMetadata, clientName string, redirectURIs ...string) (*oauth.OAuthCredentials, error)

Register performs RFC 7591 dynamic client registration against the server described by metadata. redirectURIs should include every loopback address the browser-based flow might use. Returns credentials carrying the allocated ClientID, ClientSecret, and Metadata.

func (*OAuthFlow) Revoke added in v1.4.0

func (f *OAuthFlow) Revoke(ctx context.Context) error

Revoke revokes the access and refresh tokens for the current credentials (RFC 7009) and detaches them from the client. Returns an error if no credentials are attached to the client.

type Payload

type Payload interface {
	io.Reader

	Method() string
	Accept() string
	Type() string
}

func NewFormRequest

func NewFormRequest(payload any, accept string) (Payload, error)

Return a new request with a Form data payload which defaults to POST. The accept parameter is the accepted mime-type of the response.

func NewJSONRequest

func NewJSONRequest(payload any) (Payload, error)

Return a new request with a JSON payload which defaults to POST.

func NewJSONRequestEx

func NewJSONRequestEx(method string, payload any, accept string) (Payload, error)

Return a new request with a JSON payload with method. The accept parameter is the accepted mime-type of the response.

func NewMultipartRequest

func NewMultipartRequest(payload any, accept string) (Payload, error)

Return a new request with a Multipart Form data payload which defaults to POST. The accept parameter is the accepted mime-type of the response.

func NewRequest

func NewRequest() Payload

Return a new empty request which defaults to GET

func NewRequestEx

func NewRequestEx(method, accept string) Payload

Return a new empty request. The accept parameter is the accepted mime-type of the response.

func NewStreamingMultipartRequest added in v1.3.5

func NewStreamingMultipartRequest(payload any, accept string) (Payload, error)

NewStreamingMultipartRequest returns a new request with a Multipart Form data payload that streams the encoded data rather than buffering it in memory. This is useful for large file uploads where buffering would consume too much memory.

The encoding happens in a background goroutine that writes to a pipe while the HTTP client reads from the other end. Encoding errors are propagated via the pipe.

type RequestOpt

type RequestOpt func(*requestOpts) error

func OptJsonStreamCallback added in v1.0.9

func OptJsonStreamCallback(fn JsonStreamCallback) RequestOpt

OptJsonStreamCallback is called for each decoded JSON event

func OptNoTimeout

func OptNoTimeout() RequestOpt

OptNoTimeout disables the timeout for this request, useful for long-running requests. The context can be used instead for cancelling requests

func OptPath

func OptPath(value ...any) RequestOpt

OptPath appends path elements onto a request

func OptQuery

func OptQuery(value url.Values) RequestOpt

OptQuery adds query parameters to a request

func OptReqEndpoint

func OptReqEndpoint(value string) RequestOpt

OptReqEndpoint modifies the request endpoint for this request only

func OptReqHeader

func OptReqHeader(name, value string) RequestOpt

OptReqHeader sets a header value to the request

func OptReqTransport added in v1.4.1

func OptReqTransport(fn func(http.RoundTripper) http.RoundTripper) RequestOpt

OptReqTransport inserts a transport middleware for this request only. Multiple calls stack in order; the first call becomes the outermost layer.

func OptTextStreamCallback added in v1.0.5

func OptTextStreamCallback(fn TextStreamCallback) RequestOpt

OptTextStreamCallback is called for each event in a text stream

func OptToken

func OptToken(value Token) RequestOpt

OptToken adds an authorization header. The header format is "Authorization: Bearer <token>"

type TextStream added in v1.0.5

type TextStream struct {
	// contains filtered or unexported fields
}

Implementation of a text stream, as per https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events

After Decode returns, LastEventID and RetryDuration can be used to reconnect:

req.Header.Set("Last-Event-ID", stream.LastEventID())

func NewTextStream added in v1.0.5

func NewTextStream() *TextStream

Create a new text stream decoder

func (*TextStream) Decode added in v1.0.5

func (t *TextStream) Decode(r io.Reader, callback TextStreamCallback) error

Decode a text stream. The reader should be a stream of text/event-stream data and the method will return when all the data has been scanned, or the callback returns an error

func (*TextStream) LastEventID added in v1.4.1

func (t *TextStream) LastEventID() string

LastEventID returns the last event ID received during Decode. Send this as the "Last-Event-ID" request header when reconnecting.

func (*TextStream) RetryDuration added in v1.4.1

func (t *TextStream) RetryDuration() time.Duration

RetryDuration returns the server-requested reconnect delay from the most recent "retry:" field, or zero if none was received.

type TextStreamCallback added in v1.0.5

type TextStreamCallback func(TextStreamEvent) error

Callback for text stream events, return an error if you want to return from the Decode method

type TextStreamEvent added in v1.0.5

type TextStreamEvent struct {
	// The event ID to set the EventSource object's last event ID value.
	Id string `json:"id,omitempty"`

	// A string identifying the type of event described
	Event string `json:"event,omitempty"`

	// The data field for the message
	Data string `json:"data"`

	// The reconnection time. If the connection to the server is lost,
	// the client should wait for the specified time before attempting to reconnect.
	Retry time.Duration `json:"retry,omitempty"`
}

Implementation of a text stream, as per https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format

func (*TextStreamEvent) IsZero added in v1.0.5

func (t *TextStreamEvent) IsZero() bool

Return true if the event contains no content

func (*TextStreamEvent) Json added in v1.0.5

func (t *TextStreamEvent) Json(v any) error

Decode the text stream event data as JSON

func (TextStreamEvent) String added in v1.0.5

func (t TextStreamEvent) String() string

Return the text stream event as a string

type Token

type Token struct {
	Scheme string
	Value  string
}

func (Token) String

func (token Token) String() string

Stringify the token value. Returns an empty string when Value is empty.

type Unmarshaler

type Unmarshaler interface {
	Unmarshal(header http.Header, r io.Reader) error
}

Unmarshaler is an interface which can be implemented by a type to unmarshal a response body

Directories

Path Synopsis
cmd
api command
pkg
bitwarden
bitwarden implements an API client for bitwarden
bitwarden implements an API client for bitwarden
homeassistant
homeassistant implements an API client for Home Assistant API https://developers.home-assistant.io/docs/api/rest/
homeassistant implements an API client for Home Assistant API https://developers.home-assistant.io/docs/api/rest/
ipify
ipify implements a generic API client which parses a JSON response.
ipify implements a generic API client which parses a JSON response.
oauth
Package oauth provides helpers for OAuth 2.0 authorization flows, token lifecycle management, and server metadata discovery.
Package oauth provides helpers for OAuth 2.0 authorization flows, token lifecycle management, and server metadata discovery.
transport
Package transport provides HTTP transport middleware for logging, recording, and other cross-cutting concerns.
Package transport provides HTTP transport middleware for logging, recording, and other cross-cutting concerns.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL