xhttpclient

package module
v2.0.1 Latest Latest
Warning

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

Go to latest
Published: May 12, 2026 License: MIT Imports: 16 Imported by: 0

README

X-HTTP-Client

Install

go get github.com/electricbubble/xhttpclient/v2

Usage

func ExampleNewClient_query() {
	type Response struct {
		Args struct {
			Hello string `json:"hello"`
		} `json:"args"`
	}

	cli := NewClient()

	r := NewGet().
		Path("https://httpbin.org/get").
		QuerySet("hello", "world")
	var success Response
	resp, err := cli.Do(r, CodecJSON, &success, nil)
	if err != nil {
		log.Fatalln(err)
	}
	defer resp.Close()

	fmt.Println(success.Args.Hello)
	// Output:
	// world
}

func ExampleNewClient_notFound() {
	cli := NewClient()

	r := NewDelete().
		Path("https://httpbin.org/status/", "404")
	_, err := cli.Do(r, CodecJSON, nil, nil)
	fmt.Println(err)
	// Output:
	// decode: unhandled
	// Method: DELETE
	// URL: https://httpbin.org/status/404
	// StatusCode: 404
	// Body(Len: 0):
}

func ExampleNewClient_failure() {
	type GitHubError struct {
		Message          string `json:"message"`
		DocumentationUrl string `json:"documentation_url"`
		Status           string `json:"status"`
	}

	cli := NewClient().
		BaseURL("https://api.github.com")

	r := NewGet().
		Path("/markdown")
	var failure GitHubError
	resp, err := cli.Do(r, CodecJSON, nil, &failure)
	if err != nil {
		log.Fatalln(err)
	}
	defer resp.Close()

	fmt.Printf("%+v\n", failure)
	// Output:
	// {Message:Not Found DocumentationUrl:https://docs.github.com/rest Status:404}
}

func ExampleNewClient_multipartFormData() {
	tf, err := os.CreateTemp(os.TempDir(), "xhttpclient_testdata_*.json")
	if err != nil {
		log.Fatalln(err)
		return
	}
	tfName := tf.Name()
	defer func() {
		if err := os.Remove(tfName); err != nil {
			log.Fatalln(err)
			return
		}
	}()
	if _, err := tf.WriteString(`{"1","file"}`); err != nil {
		log.Fatalln(err)
		return
	}
	if err := tf.Close(); err != nil {
		log.Fatalln(err)
		return
	}

	type Response struct {
		Files struct {
			F string `json:"f"`
			R string `json:"r"`
		} `json:"files"`
		Form struct {
			Hello string `json:"hello"`
		} `json:"form"`
	}

	cli := NewClient()

	r := NewPost().
		Path("https://httpbin.org/post").
		Body([]func(*multipart.Writer) error{
			func(w *multipart.Writer) error {
				return w.WriteField("hello", "world")
			},
			func(w *multipart.Writer) (err error) {
				var pw io.Writer
				if pw, err = w.CreateFormFile("f", filepath.Base(tfName)); err != nil {
					return err
				}

				var file *os.File
				if file, err = os.Open(tfName); err != nil {
					return err
				}
				defer file.Close()

				_, err = io.Copy(pw, file)
				return
			},
			func(w *multipart.Writer) (err error) {
				var pw io.Writer
				if pw, err = w.CreateFormFile("r", "reader.json"); err != nil {
					return err
				}
				_, err = io.Copy(pw, bytes.NewBufferString(`{"2","reader"}`))
				return
			},
		})
	var success Response
	resp, err := cli.Do(r, CodecMultipartFormData, &success, nil)
	if err != nil {
		log.Fatalln(err)
	}
	defer resp.Close()

	fmt.Println(success.Form.Hello)
	fmt.Println(success.Files.F)
	fmt.Println(success.Files.R)
	// Output:
	// world
	// {"1","file"}
	// {"2","reader"}
}

func ExampleNewClient_download() {
	cli := NewClient()

	r := NewGet().
		Path("https://raw.githubusercontent.com/electricbubble/xhttpclient/main/LICENSE")
	resp, err := cli.Do(r, CodecNoop, nil, nil)
	if err != nil {
		log.Fatalln(err)
	}
	defer resp.Close()

	scanner := bufio.NewScanner(resp.Body())
	if !scanner.Scan() {
		log.Fatalln(scanner.Err())
	}

	fmt.Println(scanner.Text())
	// Output:
	// MIT License
}

Documentation

Index

Examples

Constants

View Source
const (
	HeaderKeyContentType   = "Content-Type"
	HeaderKeyContentLength = "Content-Length"

	HeaderValueJSON = "application/json; charset=utf-8"
)

Variables

View Source
var (
	ErrUnreachableResponseIsNil = errors.New("unreachable: *http.Response is nil")

	ErrUnhandled = errors.New("unhandled")
)

Functions

func DefaultTransport

func DefaultTransport() *http.Transport

Types

type Client

type Client interface {
	Doer(doer HTTPClientDoer) Client
	RequestTimeout(timeout time.Duration) Client
	BaseURL(baseURL string) Client
	Header(header http.Header) Client
	HeaderSet(key, value string) Client
	HeaderAdd(key, value string) Client
	HeaderSetBasicAuth(username, password string) Client

	Do(rb RequestBuilder, codec Codec, successV, failureV any) (ResponseHandle, error)
}

func NewClient

func NewClient() Client
Example (Download)
cli := NewClient()

r := NewGet().
	Path("https://raw.githubusercontent.com/electricbubble/xhttpclient/main/LICENSE")
resp, err := cli.Do(r, CodecNoop, nil, nil)
if err != nil {
	log.Fatalln(err)
}
defer resp.Close()

scanner := bufio.NewScanner(resp.Body())
if !scanner.Scan() {
	log.Fatalln(scanner.Err())
}

fmt.Println(scanner.Text())
Output:
MIT License
Example (Failure)
type GitHubError struct {
	Message          string `json:"message"`
	DocumentationUrl string `json:"documentation_url"`
	Status           string `json:"status"`
}

cli := NewClient().
	BaseURL("https://api.github.com")

r := NewGet().
	Path("/markdown")
var failure GitHubError
resp, err := cli.Do(r, CodecJSON, nil, &failure)
if err != nil {
	log.Fatalln(err)
}
defer resp.Close()

fmt.Printf("%+v\n", failure)
Output:
{Message:Not Found DocumentationUrl:https://docs.github.com/rest Status:404}
Example (MultipartFormData)
tf, err := os.CreateTemp(os.TempDir(), "xhttpclient_testdata_*.json")
if err != nil {
	log.Fatalln(err)
	return
}
tfName := tf.Name()
defer func() {
	if err := os.Remove(tfName); err != nil {
		log.Fatalln(err)
		return
	}
}()
if _, err := tf.WriteString(`{"1","file"}`); err != nil {
	log.Fatalln(err)
	return
}
if err := tf.Close(); err != nil {
	log.Fatalln(err)
	return
}

type Response struct {
	Files struct {
		F string `json:"f"`
		R string `json:"r"`
	} `json:"files"`
	Form struct {
		Hello string `json:"hello"`
	} `json:"form"`
}

cli := NewClient()

r := NewPost().
	Path("https://httpbin.org/post").
	Body([]func(*multipart.Writer) error{
		func(w *multipart.Writer) error {
			return w.WriteField("hello", "world")
		},
		func(w *multipart.Writer) (err error) {
			var pw io.Writer
			if pw, err = w.CreateFormFile("f", filepath.Base(tfName)); err != nil {
				return err
			}

			var file *os.File
			if file, err = os.Open(tfName); err != nil {
				return err
			}
			defer file.Close()

			_, err = io.Copy(pw, file)
			return
		},
		func(w *multipart.Writer) (err error) {
			var pw io.Writer
			if pw, err = w.CreateFormFile("r", "reader.json"); err != nil {
				return err
			}
			_, err = io.Copy(pw, bytes.NewBufferString(`{"2","reader"}`))
			return
		},
	})
var success Response
resp, err := cli.Do(r, CodecMultipartFormData, &success, nil)
if err != nil {
	log.Fatalln(err)
}
defer resp.Close()

fmt.Println(success.Form.Hello)
fmt.Println(success.Files.F)
fmt.Println(success.Files.R)
Output:
world
{"1","file"}
{"2","reader"}
Example (NotFound)
cli := NewClient()

r := NewDelete().
	Path("https://httpbin.org/status/", "404")
_, err := cli.Do(r, CodecJSON, nil, nil)
fmt.Println(err)
Output:
decode: unhandled
Method: DELETE
URL: https://httpbin.org/status/404
StatusCode: 404
Body(Len: 0):
Example (Query)
type Response struct {
	Args struct {
		Hello string `json:"hello"`
	} `json:"args"`
}

cli := NewClient()

r := NewGet().
	Path("https://httpbin.org/get").
	QuerySet("hello", "world")
var success Response
resp, err := cli.Do(r, CodecJSON, &success, nil)
if err != nil {
	log.Fatalln(err)
}
defer resp.Close()

fmt.Println(success.Args.Hello)
Output:
world

type Codec

type Codec interface {
	Encoder
	Decoder
}
var (
	CodecNoop              Codec = new(xCodecNoop)
	CodecJSON              Codec = new(xJSON)
	CodecMultipartFormData Codec = new(xMultipartFormData)
)

type Decoder

type Decoder interface {
	Decode(resp ResponseHandle, successV, failureV any) error
}

type DecoderJSON

type DecoderJSON struct{}

func (*DecoderJSON) Decode

func (c *DecoderJSON) Decode(resp ResponseHandle, successV, failureV any) (err error)

type Encoder

type Encoder interface {
	Encode(m RequestMutation) (body io.Reader, err error)
}

type EncoderJSON

type EncoderJSON struct{}

func (*EncoderJSON) Encode

func (c *EncoderJSON) Encode(m RequestMutation) (body io.Reader, err error)

type HTTPClientDoer

type HTTPClientDoer interface {
	Do(req *http.Request) (*http.Response, error)
}
var DefaultHTTPClientDoer HTTPClientDoer = &http.Client{
	Transport: DefaultTransport(),
	Timeout:   0,
}

type RequestBuilder

type RequestBuilder interface {
	Context(ctx context.Context) RequestBuilder
	Timeout(timeout time.Duration) RequestBuilder
	Path(elem ...string) RequestBuilder
	Query(query url.Values) RequestBuilder
	QuerySet(key, value string) RequestBuilder
	QueryAdd(key, value string) RequestBuilder
	Header(header http.Header) RequestBuilder
	HeaderSet(key, value string) RequestBuilder
	HeaderAdd(key, value string) RequestBuilder
	HeaderSetBasicAuth(username, password string) RequestBuilder
	Body(body any) RequestBuilder
	// contains filtered or unexported methods
}

func NewConnect

func NewConnect() RequestBuilder

func NewDelete

func NewDelete() RequestBuilder

func NewGet

func NewGet() RequestBuilder

func NewHead

func NewHead() RequestBuilder

func NewOptions

func NewOptions() RequestBuilder

func NewPatch

func NewPatch() RequestBuilder

func NewPost

func NewPost() RequestBuilder

func NewPut

func NewPut() RequestBuilder

func NewTrace

func NewTrace() RequestBuilder

type RequestMutation

type RequestMutation interface {
	GetBody() (v any, ok bool)
	GetHeader() http.Header

	Header(header http.Header) RequestMutation
	HeaderSet(key, value string) RequestMutation
	HeaderAdd(key, value string) RequestMutation
	HeaderSetBasicAuth(username, password string) RequestMutation
}

type ResponseHandle

type ResponseHandle interface {
	StatusCode() int
	Header() http.Header
	Body() io.ReadCloser
	RequestMethod() string
	RequestURL() *url.URL

	BodyReadAll() error
	BodyHasBeenRead() []byte

	Close() error

	Raw() *http.Response
	Cancel()
}

Jump to

Keyboard shortcuts

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