httplib

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Apr 24, 2023 License: MIT Imports: 14 Imported by: 0

README

Go HTTP CLIENT - httplib

httplib is a package that provides a simple and flexible way to create and send HTTP requests in Go. This package includes features like setting headers, customizing the request body, configuring the timeout, adding middleware, rate limiting, and handling retries with backoff. If you are looking for Guzzle or Axios for Golang, you are in the right place.

Features

  1. Creating a new request
  2. Adding headers
  3. Adding a JSON body
  4. Adding a custom body
  5. Setting the timeout
  6. Using a custom HTTP client
  7. Middleware
  8. Reading a JSON response
  9. Rate limiting
  10. Retries with backoff
  11. Sending the request
  12. Chaining requests
  13. Sending requests asynchronously
  14. File upload

Usage

Creating a new request

To create a new request, use the NewRequestBuilder function:

rb := httplib.NewRequestBuilder("GET", "https://api.example.com/data")
Adding a JSON body

To add a JSON body to the request, use the WithJSONBody method:

data := map[string]interface{}{
	"key": "value",
}
rb, err := rb.WithJSONBody(data)
Adding a custom body

To add a custom body to the request, use the WithCustomBody method:

rb.WithCustomBody(strings.NewReader("<xml>data</xml>"))
Setting the timeout

To set the timeout for the request, use the WithTimeout method:

rb.WithTimeout(10 * time.Second)
Using custom HTTP client

To use a custom HTTP client for the request, use the WithClient method:

rb.WithClient(myCustomClient)
Middleware

To add a middleware function that will be executed on the response before returning it to the caller, use the Use method:

rb.Use(myMiddlewareFunction)
Reading a JSON response

To read the response body and unmarshal it into a target object, use the ReadJSONResponse method:

response, err := rb.Send()
if err == nil {
	err = rb.ReadJSONResponse(response, &myTargetObject)
}
Rate limiting

To set a rate limiter for the request, use the WithRateLimiter method:

rb.WithRateLimiter(10, 5) // 10 requests per second with a burst of 5
Retries with backoff

To set the number of retries and the backoff function for the request, use the WithRetry method:

rb.WithRetry(3, httplib.ExponentialBackoff)
Sending the request

To send the request and get the response, use the Send method:

response, err := rb.Send()
Chaining requests

To send a request and then use the response to build another request, use the SendAndChain method:

response, err := rb.SendAndChain(myChainingFunction)
Sending requests asynchronously

To send a request asynchronously and get a channel that will contain the response, use the SendAsync method:

resultChan := rb.SendAsync()

Complete Examples

Fetching data from a REST API:
package main

import (
    "fmt"
    "time"
    "github.com/joefazee/httplib"
)

type ApiResponse struct {
    Data []string `json:"data"`
}

func main() {
    rb := httplib.NewRequestBuilder("GET", "https://api.example.com/data")
    rb.WithHeader("Authorization", "Bearer <your-token>")
    rb.WithTimeout(10 * time.Second)

    response, err := rb.Send()
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    var apiResponse ApiResponse
    err = rb.ReadJSONResponse(response, &apiResponse)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Println("Data:", apiResponse.Data)
}
Posting data to a REST API:
package main

import (
    "fmt"
    "time"
    "github.com/joefazee/httplib"
)

type PostData struct {
    Title string `json:"title"`
    Body  string `json:"body"`
}

func main() {
    postData := &PostData{
        Title: "My Title",
        Body:  "This is the body of my post.",
    }

    rb := httplib.NewRequestBuilder("POST", "https://api.example.com/posts")
    rb.WithHeader("Authorization", "Bearer <your-token>")
    rb.WithTimeout(10 * time.Second)

    _, err := rb.WithJSONBody(postData)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    response, err := rb.Send()
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Println("Status:", response.Status)
}
Fetching data with rate limiting and retries:
package main

import (
    "fmt"
    "time"
    "github.com/joefazee/httplib"
)

type ApiResponse struct {
    Data []string `json:"data"`
}

func main() {
    rb := httplib.NewRequestBuilder("GET", "https://api.example.com/data")
    rb.WithHeader("Authorization", "Bearer <your-token>")
    rb.WithTimeout(10 * time.Second)
    rb.WithRateLimiter(5, 2) // 5 requests per second with a burst of 2
    rb.WithRetry(3, httplib.ExponentialBackoff)

    response, err := rb.Send()
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    var apiResponse ApiResponse
    err = rb.ReadJSONResponse(response, &apiResponse)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Println("Data:", apiResponse.Data)
}
Chainining request
package main

import (
	"fmt"
	"github.com/joefazee/httplib"
	"log"
	"net/http"
)

type User struct {
	ID       int    `json:"id"`
	Name     string `json:"name"`
	Username string `json:"username"`
	Email    string `json:"email"`
}

type Todo struct {
	ID        int    `json:"id"`
	Title     string `json:"title"`
	Completed bool   `json:"completed"`
	UserId    int    `json:"userId"`
}

func main() {
	// Create a new GET request to retrieve a user
	userRB := httplib.NewRequestBuilder("GET", "https://jsonplaceholder.typicode.com/users/1")

	// Send the request and chain another request to retrieve the user's first to-do item
	response, err := userRB.SendAndChain(func(res *http.Response) (*httplib.RequestBuilder, error) {
		// Read JSON data from the response into a User struct
		var user User
		err := userRB.ReadJSONResponse(res, &user)
		if err != nil {
			return nil, err
		}

		// Print the retrieved user information
		fmt.Printf("Retrieved User: %+v\n", user)

		// Create a new GET request to retrieve the user's first to-do item
		todoURL := fmt.Sprintf("https://jsonplaceholder.typicode.com/todos?userId=%d", user.ID)
		todoRB := httplib.NewRequestBuilder("GET", todoURL)

		return todoRB, nil
	})

	if err != nil {
		log.Fatal(err)
	}
	defer response.Body.Close()

	// Read JSON data from the response into a Todo struct
	var todos []Todo
	err = httplib.NewRequestBuilder("", "").ReadJSONResponse(response, &todos)
	if err != nil {
		log.Fatal(err)
	}

	// Print the retrieved to-do item
	fmt.Printf("Retrieved To-do Item: %+v\n", todos[0])
}
Fetching data with rate limiting and retries:
package main

import (
    "fmt"
    "time"
    "github.com/joefazee/httplib"
)

type ApiResponse struct {
    Data []string `json:"data"`
}

func main() {
    rb := httplib.NewRequestBuilder("GET", "https://api.example.com/data")
    rb.WithHeader("Authorization", "Bearer <your-token>")
    rb.WithTimeout(10 * time.Second)
    rb.WithRateLimiter(5, 2) // 5 requests per second with a burst of 2
    rb.WithRetry(3, httplib.ExponentialBackoff)

    response, err := rb.Send()
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    var apiResponse ApiResponse
    err = rb.ReadJSONResponse(response, &apiResponse)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Println("Data:", apiResponse.Data)
}

Asynchronously fetching multiple API endpoints:
package main

import (
    "fmt"
    "sync"
    "github.com/joefazee/httplib"
)

func fetchUrl(url string, wg *sync.WaitGroup) {
    defer wg.Done()

    rb := httplib.NewRequestBuilder("GET", url)
    resultChan := rb.SendAsync()

    result := <-resultChan
    if result.Error != nil {
        fmt.Println("Error:", result.Error)
        return
    }

    fmt.Println("Fetched", url, "Status:", result.Response.Status)
}
Uploading file to a server
  1. First, create a simple HTTP server to handle file uploads:
package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	http.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}

		err := r.ParseMultipartForm(32 << 20) // 32MB max memory
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		file, _, err := r.FormFile("file")
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		defer file.Close()

		fileContent, err := ioutil.ReadAll(file)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		fmt.Println("Received file content:", string(fileContent))
		w.WriteHeader(http.StatusOK)
	})

	fmt.Println("Starting server on :8080...")
	http.ListenAndServe(":8080", nil)
}

This code creates a simple HTTP server that listens on port 8080 and handles file uploads at the /upload endpoint.

  1. Next, create a client application to upload a file to the server using the httplib package:
package main

import (
	"fmt"
	"github.com/joefazee/httplib"
	"log"
)

func main() {
	filePath := "/path/to/your/file.txt"
	rb := httplib.NewRequestBuilder("POST", "http://localhost:8080/upload")
	rb, err := rb.WithFile("file", filePath)
	if err != nil {
		log.Fatal(err)
	}

	response, err := rb.Send()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("File uploaded:", response.StatusCode)
}
Upload a file to AWS S3 using the httplib

Install the AWS SDK for Go using:

go get -u github.com/aws/aws-sdk-go

Use the following code to generate a pre-signed URL:

package main

import (
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/joefazee/httplib"
	"log"
	"time"
)

func main() {
	// Initialize a new session
	sess, err := session.NewSession(&aws.Config{
		Region: aws.String("us-west-2"),
	})
	if err != nil {
		log.Fatal(err)
	}

	// Create a new S3 service client
	s3Client := s3.New(sess)

	// Generate the pre-signed URL
	req, _ := s3Client.PutObjectRequest(&s3.PutObjectInput{
		Bucket: aws.String("your-bucket-name"),
		Key:    aws.String("your-object-key"),
	})
	presignedURL, err := req.Presign(15 * time.Minute)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Pre-signed URL:", presignedURL)

	// Upload the file using the httplib package
	filePath := "/path/to/your/file.txt"
	rb := httplib.NewRequestBuilder("PUT", presignedURL)
	rb, err = rb.WithFile("file", filePath)
	if err != nil {
		log.Fatal(err)
	}

	response, err := rb.Send()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("File uploaded to S3:", response.StatusCode)
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExponentialBackoff

func ExponentialBackoff(retry int) time.Duration

ExponentialBackoff calculates the backoff duration for retries using exponential backoff strategy

Types

type AsyncResult

type AsyncResult struct {
	Response *http.Response
	Error    error
}

AsyncResult is the result of an asynchronous request

type BackoffFunc

type BackoffFunc func(retry int) time.Duration

BackoffFunc is a function that is used to calculate the backoff duration between retries

type MarshalError

type MarshalError struct {
	Err error
}

MarshalError is returned when there is an error marshaling the request body

func (*MarshalError) Error

func (e *MarshalError) Error() string

Error returns the error message

type Middleware

type Middleware func(*http.Request, *http.Response) error

Middleware is a function that is executed on the response before returning it to the caller

type RequestBuilder

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

RequestBuilder is used to build a request. Always use NewRequestBuilder to create a new instance

func NewRequestBuilder

func NewRequestBuilder(method, url string) *RequestBuilder

NewRequestBuilder creates a new request builder

func (*RequestBuilder) BodyAsBytes

func (rb *RequestBuilder) BodyAsBytes() ([]byte, error)

BodyAsBytes returns the request body as a byte array

func (*RequestBuilder) ReadJSONResponse

func (rb *RequestBuilder) ReadJSONResponse(response *http.Response, target interface{}) error

ReadJSONResponse reads the response body and unmarshals it into the target object

func (*RequestBuilder) Send

func (rb *RequestBuilder) Send() (*http.Response, error)

Send sends the request and returns the response

func (*RequestBuilder) SendAndChain

func (rb *RequestBuilder) SendAndChain(chain func(*http.Response) (*RequestBuilder, error)) (*http.Response, error)

SendAndChain sends the request and returns the response

func (*RequestBuilder) SendAsync

func (rb *RequestBuilder) SendAsync() <-chan AsyncResult

SendAsync sends the request asynchronously and returns a channel that will contain the response

func (*RequestBuilder) Use

func (rb *RequestBuilder) Use(middleware Middleware) *RequestBuilder

Use Add a function to register middleware

func (*RequestBuilder) WithClient

func (rb *RequestBuilder) WithClient(client *http.Client) *RequestBuilder

WithClient sets the http client to use for the request

func (*RequestBuilder) WithCustomBody

func (rb *RequestBuilder) WithCustomBody(body io.Reader) *RequestBuilder

WithCustomBody adds a custom body to the request

func (*RequestBuilder) WithFile

func (rb *RequestBuilder) WithFile(fieldName, filePath string) (*RequestBuilder, error)

WithFile adds a file to the request as a multipart form data

func (*RequestBuilder) WithHeader

func (rb *RequestBuilder) WithHeader(key, value string) *RequestBuilder

WithHeader adds a header to the request

func (*RequestBuilder) WithJSONBody

func (rb *RequestBuilder) WithJSONBody(data interface{}) (*RequestBuilder, error)

WithJSONBody adds a JSON body to the request

func (*RequestBuilder) WithRateLimiter

func (rb *RequestBuilder) WithRateLimiter(rps int, burst int) *RequestBuilder

WithRateLimiter sets the rate limiter for the request

func (*RequestBuilder) WithRetry

func (rb *RequestBuilder) WithRetry(retries int, backoff BackoffFunc) *RequestBuilder

WithRetry sets the number of retries and the backoff function for the request

func (*RequestBuilder) WithTimeout

func (rb *RequestBuilder) WithTimeout(timeout time.Duration) *RequestBuilder

WithTimeout sets the timeout for the request

type RequestError

type RequestError struct {
	Err error
}

RequestError returned when there is an error making the request

func (*RequestError) Error

func (e *RequestError) Error() string

Error returns the error message

type UnmarshalError

type UnmarshalError struct {
	Err error
}

UnmarshalError is returned when there is an error unmarshaling the response body

func (*UnmarshalError) Error

func (e *UnmarshalError) Error() string

Error returns the error message

Jump to

Keyboard shortcuts

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