ghratelimit

package module
v0.0.0-...-6a5eb44 Latest Latest
Warning

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

Go to latest
Published: Jul 13, 2025 License: MIT Imports: 14 Imported by: 0

README

GitHub Rate Limit HTTP Transport Go Reference

A Golang http.RoundTripper for monitoring the GitHub rate-limit responses from GitHub's REST API.

Example

Demonstrates how to use ghratelimit.Transport with go-github and Prometheus to monitor GitHub rate limits:

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"

	ghratelimit "github.com/bored-engineer/github-rate-limit-http-transport"
	"github.com/google/go-github/v71/github"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
	// Register Prometheus metrics
	RateLimitRemaining = promauto.NewGaugeVec(
		prometheus.GaugeOpts{
			Name:      "rate_limit_remaining",
			Help:      "Number of requests remaining in the current rate limit window",
			Subsystem: "github",
		},
		[]string{"resource"},
	)
	RateLimitReset = promauto.NewGaugeVec(
		prometheus.GaugeOpts{
			Name:      "rate_limit_reset",
			Help:      "Unix timestamp when the current rate limit window resets",
			Subsystem: "github",
		},
		[]string{"resource"},
	)
)

func main() {
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
	defer cancel()

	// Serve the Prometheus metrics
	go http.ListenAndServe("127.0.0.1:1971", promhttp.Handler())

	// Create a new Transport to observe rate limits
	transport := &ghratelimit.Transport{
		Base: http.DefaultTransport,
		Limits: ghratelimit.Limits{
			Notify: func(resp *http.Response, resource ghratelimit.Resource, rate *ghratelimit.Rate) {
				RateLimitRemaining.WithLabelValues(resource.String()).Set(float64(rate.Remaining))
				RateLimitReset.WithLabelValues(resource.String()).Set(float64(rate.Reset))
			},
		},
	}

	// The rate-limits will be updated as HTTP responses are received from GitHub by the *ghratelimit.Transport
	// However, it is useful to refresh the rate-limits periodically via the /rate_limits endpoint
	go transport.Poll(ctx, time.Minute, nil)

	// Create a GitHub client using the Transport
	client := github.NewClient(&http.Client{
		Transport: transport,
	})

	// Perform a request to GitHub
	user, _, err := client.Users.Get(ctx, "bored-engineer")
	if err != nil {
		log.Fatalf("(*github.UsersService).Get failed: %v", err)
	}
	fmt.Printf("bio: %s\n", user.GetBio())

	// Demonstrate that the rate-limits can be manually fetched
	rate := transport.Limits.Load(ghratelimit.ResourceCore)
	fmt.Printf("remaining: %d\n", rate.Remaining)
	fmt.Printf("reset: %d\n", rate.Reset)

	// Wait for the context to be cancelled before exiting
	<-ctx.Done()
}

Additionally, the ghratelimit.BalancingTransport can be used to automatically balance requests across multiple ghratelimit.Transport instances (presumably backed by different GitHub credentials) based on whichever transport has the highest remaining GitHub rate-limit.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaultURL = &url.URL{
	Scheme: "https",
	Host:   "api.github.com",
	Path:   "/rate_limit",
}

DefaultURL is the default URL used to poll rate limits. It is set to https://api.github.com/rate_limit.

ValidResources represents the list of valid/known rate-limit resources. Modifying this slice at runtime may result in undefined behavior.

Functions

This section is empty.

Types

type BalancingTransport

type BalancingTransport []*Transport

BalancingTransport distributes requests to the transport with the highest "remaining" rate limit to execute the request. This can be used to distributes requests across multiple GitHub authentication tokens or applications.

func (BalancingTransport) Poll

func (bt BalancingTransport) Poll(ctx context.Context, interval time.Duration, u *url.URL)

Poll calls (*Transport).Poll for every transport

func (BalancingTransport) RoundTrip

func (bt BalancingTransport) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip implements http.RoundTripper

type Limits

type Limits struct {

	// Notify is called when a new rate limit is stored.
	// It can be a useful hook to update metric gauges.
	Notify func(*http.Response, Resource, *Rate)
	// contains filtered or unexported fields
}

Limits represents the rate limits for all known resource types.

func (*Limits) Fetch

func (l *Limits) Fetch(ctx context.Context, transport http.RoundTripper, u *url.URL) error

Fetch the latest rate limits from the GitHub API and update the Limits instance. If the provided URL is nil, it defaults to DefaultURL (https://api.github.com/rate_limit).

func (*Limits) Iter

func (l *Limits) Iter() iter.Seq2[Resource, *Rate]

Iter loops over the resource types and yields each resource type and its rate limit.

func (*Limits) Load

func (l *Limits) Load(resource Resource) *Rate

Load the rate-limit for the given resource type.

func (*Limits) Parse

func (l *Limits) Parse(resp *http.Response) error

Parse updates the rate limits based on the provided HTTP response.

func (*Limits) Store

func (l *Limits) Store(resp *http.Response, resource Resource, rate *Rate)

Store the rate limit for the given resource type.

func (*Limits) String

func (l *Limits) String() string

String implements fmt.Stringer

type Rate

type Rate struct {
	// The maximum number of requests that you can make per hour.
	Limit uint64 `json:"limit"`
	// The number of requests you have made in the current rate limit window.
	Used uint64 `json:"used"`
	// The number of requests remaining in the current rate limit window.
	Remaining uint64 `json:"remaining"`
	// The time at which the current rate limit window resets, in UTC epoch seconds.
	Reset uint64 `json:"reset"`
}

Rate represents the rate limit information for a given resource type.

func ParseRate

func ParseRate(headers http.Header) (r Rate, _ error)

Parse extracts the rate limit information from the HTTP response headers.

func (*Rate) String

func (r *Rate) String() string

String implements fmt.Stringer

type Resource

type Resource string

Resource represents the X-Ratelimit-Resource header value.

const (
	// The core REST API's rate limit.
	ResourceCore Resource = "core"

	// Search API's rate limit.
	ResourceSearch Resource = "search"

	// GraphQL API's rate limit.
	ResourceGraphQL Resource = "graphql"

	// App manifest API's rate limit.
	ResourceIntegrationManifest Resource = "integration_manifest"

	// Import API's rate limit.
	ResourceSourceImport Resource = "source_import"

	// Code Scanning upload API's rate limit.
	ResourceCodeScanningUpload Resource = "code_scanning_upload"

	// Code Scanning autofix API's rate limit.
	ResourceCodeScanningAutofix Resource = "code_scanning_autofix"

	// Actions Runner Registration API's rate limit.
	ResourceActionsRunnerRegistration Resource = "actions_runner_registration"

	// SCIM API's rate limit.
	ResourceSCIM Resource = "scim"

	// Dependency Snapshots API's rate limit.
	ResourceDependencySnapshots Resource = "dependency_snapshots"

	// Audit Log API's rate limit.
	ResourceAuditLog Resource = "audit_log"

	// Audit Log Streaming API's rate limit.
	ResourceAuditLogStreaming Resource = "audit_log_streaming"

	// Code Search API's rate limit.
	ResourceCodeSearch Resource = "code_search"
)

func InferResource

func InferResource(req *http.Request) Resource

InferResource guessed which rate-limit resource that will be consumed by the provided HTTP request.

func ParseResource

func ParseResource(headers http.Header) Resource

ParseResource extracts the Resource from the X-RateLimit-Resource header of the HTTP response.

func (Resource) String

func (r Resource) String() string

String implements fmt.Stringer.

func (Resource) Valid

func (r Resource) Valid() bool

Valid checks if the resource is valid/known.

type Transport

type Transport struct {
	// Base is the base RoundTripper used to make HTTP requests.
	// If nil, http.DefaultTransport is used.
	Base http.RoundTripper
	// Limits is the most recent rate-limit information
	Limits Limits
}

Transport updates the Limits field with the most recent rate-limit information as responses from GitHub are executed. It implements the http.RoundTripper interface, so it can be used as a base transport for http.Client.

func (*Transport) Poll

func (t *Transport) Poll(ctx context.Context, interval time.Duration, u *url.URL)

Poll calls (*Transport).Limits.Update every interval, starting immediately.

func (*Transport) RoundTrip

func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error)

RoundTrip implements http.RoundTripper

Jump to

Keyboard shortcuts

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