commit ea253cbfb6ba391fc1cd6803fb75557be35575dd Author: Milarin Date: Wed Sep 11 13:32:52 2024 +0200 initial commit diff --git a/do.go b/do.go new file mode 100644 index 0000000..337333e --- /dev/null +++ b/do.go @@ -0,0 +1,19 @@ +package ezhttp + +import ( + "encoding/json" + "io" + "net/http" +) + +func Do(req *http.Request) (*http.Response, error) { + return http.DefaultClient.Do(req) +} + +func ParseJsonResponse[T any](r io.Reader) (*T, error) { + res := new(T) + if err := json.NewDecoder(r).Decode(res); err != nil { + return nil, err + } + return res, nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ebb50df --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.milar.in/milarin/ezhttp + +go 1.23.0 diff --git a/request.go b/request.go new file mode 100644 index 0000000..80cc5b2 --- /dev/null +++ b/request.go @@ -0,0 +1,193 @@ +package ezhttp + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "io" + "net/http" + urlpkg "net/url" + "strings" +) + +type RequestModifier func(r *RequestBuilder) *RequestBuilder +type ValueProvider[T any] func() T +type AuthProvider ValueProvider[string] +type RequestBodyProvider func() (io.Reader, string) + +func Request(mods ...RequestModifier) (*http.Request, error) { + r := NewRequestBuilder() + for _, mod := range mods { + r = mod(r) + } + return r.Build() +} + +func Template(req *http.Request) RequestModifier { + return func(r *RequestBuilder) *RequestBuilder { + return NewRequestBuilderFromRequest(req) + } +} + +func URL(url string) RequestModifier { + parsedUrl, err := urlpkg.Parse(url) + if err != nil { + panic(err) + } + + return func(r *RequestBuilder) *RequestBuilder { + r.URL = parsedUrl + return r + //return Headers("Host", parsedUrl.Host)(r) + } +} + +func Scheme(scheme string) RequestModifier { + return func(r *RequestBuilder) *RequestBuilder { + r.URL.Scheme = scheme + return r + } +} + +func Host(host string) RequestModifier { + return func(r *RequestBuilder) *RequestBuilder { + r.URL.Host = host + return r + } +} + +func Path(path string) RequestModifier { + return func(r *RequestBuilder) *RequestBuilder { + r.URL.Path = path + return r + } +} + +func AppendPath(path ...string) RequestModifier { + return func(r *RequestBuilder) *RequestBuilder { + r.URL = r.URL.JoinPath(path...) + return r + } +} + +func Fragment(fragment string) RequestModifier { + return func(r *RequestBuilder) *RequestBuilder { + r.URL.Fragment = fragment + return r + } +} + +func Query(keyValuePairs ...string) RequestModifier { + if len(keyValuePairs)%2 != 0 { + panic("keyValuePairs must have an even length") + } + + return func(r *RequestBuilder) *RequestBuilder { + q := r.URL.Query() + for i := 0; i < len(keyValuePairs)-1; i += 2 { + q.Add(keyValuePairs[i], keyValuePairs[i+1]) + } + r.URL.RawQuery = q.Encode() + return r + } +} + +func QueryMap(query map[string]string) RequestModifier { + return func(r *RequestBuilder) *RequestBuilder { + q := r.URL.Query() + for key, value := range query { + q.Add(key, value) + } + r.URL.RawQuery = q.Encode() + return r + } +} + +func Method(method string) RequestModifier { + return func(r *RequestBuilder) *RequestBuilder { + r.Method = method + return r + } +} + +func Headers(keyValuePairs ...string) RequestModifier { + if len(keyValuePairs)%2 != 0 { + panic("keyValuePairs must have an even length") + } + + return func(r *RequestBuilder) *RequestBuilder { + for i := 0; i < len(keyValuePairs)-1; i += 2 { + r.Header.Add(keyValuePairs[i], keyValuePairs[i+1]) + } + return r + } +} + +func HeadersMap(headers map[string]string) RequestModifier { + return func(r *RequestBuilder) *RequestBuilder { + for key, value := range headers { + r.Header.Add(key, value) + } + return r + } +} + +func Auth(auth AuthProvider) RequestModifier { + return Headers("Authentication", auth()) +} + +func Basic(username, password string) AuthProvider { + return func() string { + return "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password)) + } +} + +func Bearer(token string) AuthProvider { + return func() string { + return "Bearer " + token + } +} + +func ContentType(contentType string) RequestModifier { + return Headers("Content-Type", contentType) +} + +func Body(bodyProvider RequestBodyProvider) RequestModifier { + return func(r *RequestBuilder) *RequestBuilder { + body, contentType := bodyProvider() + r.Body = body + if contentType != "" && r.Header.Get("Content-Type") != "" { + r.Header.Set("Content-Type", contentType) + } + return r + } +} + +func Reader(r io.Reader) RequestBodyProvider { + return func() (io.Reader, string) { + return r, "application/octet-stream" + } +} + +func String(s string) RequestBodyProvider { + return func() (io.Reader, string) { + return strings.NewReader(s), "text/plain" + } +} + +func Bytes(b []byte) RequestBodyProvider { + return func() (io.Reader, string) { + return bytes.NewReader(b), "application/octet-stream" + } +} + +func JSON[T any](value T) RequestBodyProvider { + buf := bytes.NewBuffer(make([]byte, 0, 1024)) + if err := json.NewEncoder(buf).Encode(value); err != nil { + panic(err) + } + + return func() (io.Reader, string) { + return buf, "application/json" + } +} diff --git a/request_builder.go b/request_builder.go new file mode 100644 index 0000000..3fbe140 --- /dev/null +++ b/request_builder.go @@ -0,0 +1,41 @@ +package ezhttp + +import ( + "io" + "net/http" + urlpkg "net/url" +) + +type RequestBuilder struct { + URL *urlpkg.URL + Method string + Header http.Header + Body io.Reader +} + +func NewRequestBuilder() *RequestBuilder { + return &RequestBuilder{ + URL: &urlpkg.URL{}, + Header: make(http.Header), + Body: nil, + } +} + +func NewRequestBuilderFromRequest(r *http.Request) *RequestBuilder { + return &RequestBuilder{ + URL: r.URL, + Method: r.Method, + Header: r.Header, + Body: r.Body, + } +} + +func (b *RequestBuilder) Build() (*http.Request, error) { + r, err := http.NewRequest(b.Method, b.URL.String(), b.Body) + if err != nil { + return nil, err + } + + r.Header = b.Header + return r, nil +}