summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2024-03-12 01:06:15 +0700
committerShulhan <ms@kilabit.info>2024-03-14 23:51:34 +0700
commitc7de9d3df399c7ee1c2db2c4c253869951487ab5 (patch)
treedea8152a693cafcfb147839cc95d1ed24fb9fcb8
parent9da27235f996db4b2be0ba29f4bcecae1511519b (diff)
downloadpakakeh.go-c7de9d3df399c7ee1c2db2c4c253869951487ab5.tar.xz
lib/http: refactor Client methods to use struct ClientRequest
-rw-r--r--api/slack/webhook_client.go6
-rw-r--r--api/telegram/bot/bot.go105
-rw-r--r--lib/http/client.go325
-rw-r--r--lib/http/client_request.go65
-rw-r--r--lib/http/endpoint_example_test.go13
-rw-r--r--lib/http/endpoint_response_example_test.go16
-rw-r--r--lib/http/server_example_test.go9
-rw-r--r--lib/http/server_test.go19
8 files changed, 367 insertions, 191 deletions
diff --git a/api/slack/webhook_client.go b/api/slack/webhook_client.go
index 5a749779..365aa151 100644
--- a/api/slack/webhook_client.go
+++ b/api/slack/webhook_client.go
@@ -53,8 +53,12 @@ func (wcl *WebhookClient) Post(msg *Message) (err error) {
}
var logp = `Post`
+ var req = libhttp.ClientRequest{
+ Path: wcl.webhookPath,
+ Params: msg,
+ }
- httpRes, resBody, err := wcl.Client.PostJSON(wcl.webhookPath, nil, msg) //nolint: bodyclose
+ httpRes, resBody, err := wcl.Client.PostJSON(req) //nolint: bodyclose
if err != nil {
return fmt.Errorf(`%s: %w`, logp, err)
}
diff --git a/api/telegram/bot/bot.go b/api/telegram/bot/bot.go
index 2ac3f0a0..557b824b 100644
--- a/api/telegram/bot/bot.go
+++ b/api/telegram/bot/bot.go
@@ -10,13 +10,13 @@ import (
"encoding/json"
"fmt"
"log"
- stdhttp "net/http"
+ "net/http"
"path"
"strconv"
"time"
"git.sr.ht/~shulhan/pakakeh.go/lib/errors"
- "git.sr.ht/~shulhan/pakakeh.go/lib/http"
+ libhttp "git.sr.ht/~shulhan/pakakeh.go/lib/http"
)
// List of message parse mode.
@@ -96,8 +96,8 @@ const (
// Bot for Telegram using webHook.
type Bot struct {
opts Options
- client *http.Client
- webhook *http.Server
+ client *libhttp.Client
+ webhook *libhttp.Server
user *User
err chan error
commands commands
@@ -110,12 +110,12 @@ func New(opts Options) (bot *Bot, err error) {
return nil, fmt.Errorf("bot.New: %w", err)
}
- var clientOpts = http.ClientOptions{
+ var clientOpts = libhttp.ClientOptions{
ServerURL: defURL + opts.Token + `/`,
}
bot = &Bot{
opts: opts,
- client: http.NewClient(clientOpts),
+ client: libhttp.NewClient(clientOpts),
}
fmt.Printf("Bot options: %+v\n", opts)
@@ -134,12 +134,19 @@ func New(opts Options) (bot *Bot, err error) {
// DeleteWebhook remove webhook integration if you decide to switch back to
// getUpdates. Returns True on success. Requires no parameters.
func (bot *Bot) DeleteWebhook() (err error) {
- _, resBody, err := bot.client.PostForm(methodDeleteWebhook, nil, nil) //nolint: bodyclose
+ var (
+ req = libhttp.ClientRequest{
+ Path: methodDeleteWebhook,
+ }
+ resBody []byte
+ )
+
+ _, resBody, err = bot.client.PostForm(req) //nolint: bodyclose
if err != nil {
return fmt.Errorf("DeleteWebhook: %w", err)
}
- res := &response{}
+ var res = &response{}
err = json.Unmarshal(resBody, res)
if err != nil {
return fmt.Errorf("DeleteWebhook: %w", err)
@@ -152,7 +159,14 @@ func (bot *Bot) DeleteWebhook() (err error) {
// Requires no parameters.
// Returns basic information about the bot in form of a User object.
func (bot *Bot) GetMe() (user *User, err error) {
- _, resBody, err := bot.client.Get(methodGetMe, nil, nil) //nolint: bodyclose
+ var (
+ req = libhttp.ClientRequest{
+ Path: methodGetMe,
+ }
+ resBody []byte
+ )
+
+ _, resBody, err = bot.client.Get(req) //nolint: bodyclose
if err != nil {
return nil, fmt.Errorf("GetMe: %w", err)
}
@@ -171,7 +185,14 @@ func (bot *Bot) GetMe() (user *User, err error) {
// GetMyCommands get the current list of the bot's commands.
func (bot *Bot) GetMyCommands() (cmds []Command, err error) {
- _, resBody, err := bot.client.Get(methodGetMyCommands, nil, nil) //nolint: bodyclose
+ var (
+ req = libhttp.ClientRequest{
+ Path: methodGetMyCommands,
+ }
+ resBody []byte
+ )
+
+ _, resBody, err = bot.client.Get(req) //nolint: bodyclose
if err != nil {
return nil, fmt.Errorf("GetMyCommands: %w", err)
}
@@ -192,7 +213,14 @@ func (bot *Bot) GetMyCommands() (cmds []Command, err error) {
// If the bot is using getUpdates, will return an object with the url field
// empty.
func (bot *Bot) GetWebhookInfo() (webhookInfo *WebhookInfo, err error) {
- _, resBody, err := bot.client.Get(methodGetWebhookInfo, nil, nil) //nolint: bodyclose
+ var (
+ req = libhttp.ClientRequest{
+ Path: methodGetWebhookInfo,
+ }
+ resBody []byte
+ )
+
+ _, resBody, err = bot.client.Get(req) //nolint: bodyclose
if err != nil {
return nil, fmt.Errorf("GetWebhookInfo: %w", err)
}
@@ -214,13 +242,20 @@ func (bot *Bot) GetWebhookInfo() (webhookInfo *WebhookInfo, err error) {
func (bot *Bot) SendMessage(parent *Message, parseMode, text string) (
msg *Message, err error,
) {
- req := messageRequest{
- ChatID: parent.Chat.ID,
- Text: text,
- ParseMode: parseMode,
- }
+ var (
+ params = messageRequest{
+ ChatID: parent.Chat.ID,
+ Text: text,
+ ParseMode: parseMode,
+ }
+ req = libhttp.ClientRequest{
+ Path: methodSendMessage,
+ Params: params,
+ }
+ resBody []byte
+ )
- _, resBody, err := bot.client.PostJSON(methodSendMessage, nil, req) //nolint: bodyclose
+ _, resBody, err = bot.client.PostJSON(req) //nolint: bodyclose
if err != nil {
return nil, fmt.Errorf("SendMessage: %w", err)
}
@@ -254,7 +289,15 @@ func (bot *Bot) SetMyCommands(cmds []Command) (err error) {
bot.commands.Commands = cmds
- _, resBody, err := bot.client.PostJSON(methodSetMyCommands, nil, &bot.commands) //nolint: bodyclose
+ var (
+ req = libhttp.ClientRequest{
+ Path: methodSetMyCommands,
+ Params: &bot.commands,
+ }
+ resBody []byte
+ )
+
+ _, resBody, err = bot.client.PostJSON(req) //nolint: bodyclose
if err != nil {
return fmt.Errorf("SetMyCommands: %w", err)
}
@@ -317,9 +360,15 @@ func (bot *Bot) setWebhook() (err error) {
params[paramNameAllowedUpdates] = allowedUpdates
}
- var resBody []byte
+ var (
+ req = libhttp.ClientRequest{
+ Path: methodSetWebhook,
+ Params: params,
+ }
+ resBody []byte
+ )
- _, resBody, err = bot.client.PostFormData(methodSetWebhook, nil, params) //nolint: bodyclose
+ _, resBody, err = bot.client.PostFormData(req) //nolint: bodyclose
if err != nil {
return fmt.Errorf(`%s: %w`, logp, err)
}
@@ -365,7 +414,7 @@ func (bot *Bot) startWebhook() (err error) {
// createServer start the HTTP server for receiving Update.
func (bot *Bot) createServer() (err error) {
- var serverOpts = http.ServerOptions{
+ var serverOpts = libhttp.ServerOptions{
Address: bot.opts.Webhook.ListenAddress,
}
@@ -377,22 +426,22 @@ func (bot *Bot) createServer() (err error) {
tlsConfig.Certificates,
*bot.opts.Webhook.ListenCertificate,
)
- serverOpts.Conn = &stdhttp.Server{
+ serverOpts.Conn = &http.Server{
TLSConfig: tlsConfig,
ReadHeaderTimeout: 5 * time.Second,
}
}
- bot.webhook, err = http.NewServer(serverOpts)
+ bot.webhook, err = libhttp.NewServer(serverOpts)
if err != nil {
return fmt.Errorf("createServer: %w", err)
}
- var epToken = http.Endpoint{
- Method: http.RequestMethodPost,
+ var epToken = libhttp.Endpoint{
+ Method: libhttp.RequestMethodPost,
Path: "/" + bot.opts.Token,
- RequestType: http.RequestTypeJSON,
- ResponseType: http.ResponseTypeNone,
+ RequestType: libhttp.RequestTypeJSON,
+ ResponseType: libhttp.ResponseTypeNone,
Call: bot.handleWebhook,
}
@@ -405,7 +454,7 @@ func (bot *Bot) createServer() (err error) {
}
// handleWebhook handle Updates from Webhook.
-func (bot *Bot) handleWebhook(epr *http.EndpointRequest) (resBody []byte, err error) {
+func (bot *Bot) handleWebhook(epr *libhttp.EndpointRequest) (resBody []byte, err error) {
var update Update
err = json.Unmarshal(epr.RequestBody, &update)
diff --git a/lib/http/client.go b/lib/http/client.go
index 78f3d65a..25a4cbc4 100644
--- a/lib/http/client.go
+++ b/lib/http/client.go
@@ -20,7 +20,6 @@ import (
"mime/multipart"
"net"
"net/http"
- "net/url"
"path"
"sort"
"strings"
@@ -84,14 +83,15 @@ func NewClient(opts ClientOptions) (client *Client) {
// Delete send the DELETE request to server using rpath as target endpoint
// and params as query parameters.
// On success, it will return the uncompressed response body.
-func (client *Client) Delete(rpath string, hdr http.Header, params url.Values) (
- res *http.Response, resBody []byte, err error,
-) {
- if params != nil {
- rpath += `?` + params.Encode()
+func (client *Client) Delete(req ClientRequest) (res *http.Response, resBody []byte, err error) {
+ var params = req.paramsAsURLEncoded()
+ if len(params) != 0 {
+ req.Path += `?` + params
}
- return client.doRequest(http.MethodDelete, hdr, rpath, ``, nil)
+ req.Method = RequestMethodDelete
+
+ return client.doRequest(req)
}
// Do overwrite the standard [http.Client.Do] to allow debugging request and
@@ -175,45 +175,34 @@ out:
return res, err
}
-// GenerateHTTPRequest generate [http.Request] from method, rpath,
-// rtype, hdr, and params.
+// GenerateHTTPRequest generate [http.Request] from [ClientRequest].
//
-// For HTTP method GET, CONNECT, DELETE, HEAD, OPTIONS, or TRACE; the params
-// value should be nil or [url.Values].
+// For HTTP method GET, CONNECT, DELETE, HEAD, OPTIONS, or TRACE; the
+// [ClientRequest.Params] value should be nil or [url.Values].
// If its [url.Values], then the params will be encoded as query parameters.
//
-// For HTTP method is PATCH, POST, or PUT; the params will converted based
-// on rtype rules below,
+// For HTTP method PATCH, POST, or PUT; the [ClientRequest.Params] will
+// converted based on [ClientRequest.Type] rules below,
//
-// - If rtype is [RequestTypeQuery] and params is [url.Values] it
-// will be added as query parameters in the rpath.
-// - If rtype is [RequestTypeForm] and params is [url.Values] it
-// will be added as URL encoded in the body.
-// - If rtype is [RequestTypeMultipartForm] and params type is
-// map[string][]byte, then it will be converted as multipart form in the
+// - If Type is [RequestTypeQuery] and Params is [url.Values] it
+// will be send as query parameters in the Path.
+// - If Type is [RequestTypeForm] and Params is [url.Values] it
+// will be send as URL encoded in the body.
+// - If Type is [RequestTypeMultipartForm] and Params type is
+// map[string][]byte, then it will send as multipart form in the
// body.
-// - If rtype is [RequestTypeJSON] and params is not nil, the params
-// will be encoded as JSON in body.
-func (client *Client) GenerateHTTPRequest(
- method RequestMethod,
- rpath string,
- rtype RequestType,
- hdr http.Header,
- params interface{},
-) (req *http.Request, err error) {
+// - If Type is [RequestTypeJSON] and Params is not nil, the Params
+// will be encoded as JSON in the body.
+func (client *Client) GenerateHTTPRequest(req ClientRequest) (httpReq *http.Request, err error) {
var (
- logp = `GenerateHTTPRequest`
- paramsAsURLValues url.Values
- isParamsURLValues bool
- paramsAsJSON []byte
- contentType = rtype.String()
- strBody string
- body io.Reader
- )
+ logp = `GenerateHTTPRequest`
+ contentType = req.Type.String()
+ paramsEncoded = req.paramsAsURLEncoded()
- paramsAsURLValues, isParamsURLValues = params.(url.Values)
+ body io.Reader
+ )
- switch method {
+ switch req.Method {
case RequestMethodGet,
RequestMethodConnect,
RequestMethodDelete,
@@ -221,237 +210,277 @@ func (client *Client) GenerateHTTPRequest(
RequestMethodOptions,
RequestMethodTrace:
- if isParamsURLValues {
- rpath += `?` + paramsAsURLValues.Encode()
+ if len(paramsEncoded) != 0 {
+ req.Path += `?` + paramsEncoded
}
case RequestMethodPatch,
RequestMethodPost,
RequestMethodPut:
- switch rtype {
+ switch req.Type {
case RequestTypeNone, RequestTypeXML:
// NOOP.
case RequestTypeQuery:
- if isParamsURLValues {
- rpath += `?` + paramsAsURLValues.Encode()
+ if len(paramsEncoded) != 0 {
+ req.Path += `?` + paramsEncoded
}
case RequestTypeForm:
- if isParamsURLValues {
- strBody = paramsAsURLValues.Encode()
- body = strings.NewReader(strBody)
+ if len(paramsEncoded) != 0 {
+ body = strings.NewReader(paramsEncoded)
}
case RequestTypeMultipartForm:
- paramsAsMultipart, ok := params.(map[string][]byte)
+ var (
+ paramsAsMultipart map[string][]byte
+ ok bool
+ )
+
+ paramsAsMultipart, ok = req.Params.(map[string][]byte)
if ok {
+ var strBody string
+
contentType, strBody, err = GenerateFormData(paramsAsMultipart)
if err != nil {
- return nil, fmt.Errorf("%s: %w", logp, err)
+ return nil, fmt.Errorf(`%s: %w`, logp, err)
}
body = strings.NewReader(strBody)
}
case RequestTypeJSON:
- if params != nil {
- paramsAsJSON, err = json.Marshal(params)
+ if req.Params != nil {
+ var paramsAsJSON []byte
+ paramsAsJSON, err = json.Marshal(req.Params)
if err != nil {
- return nil, fmt.Errorf("%s: %w", logp, err)
+ return nil, fmt.Errorf(`%s: %w`, logp, err)
}
body = bytes.NewReader(paramsAsJSON)
}
}
}
- rpath = path.Join(`/`, rpath)
+ req.Path = path.Join(`/`, req.Path)
var (
- fullURL = client.opts.ServerURL + rpath
+ fullURL = client.opts.ServerURL + req.Path
ctx = context.Background()
)
- req, err = http.NewRequestWithContext(ctx, method.String(), fullURL, body)
+ httpReq, err = http.NewRequestWithContext(ctx, req.Method.String(), fullURL, body)
if err != nil {
- return nil, fmt.Errorf("%s: %w", logp, err)
+ return nil, fmt.Errorf(`%s: %w`, logp, err)
}
- setHeaders(req, client.opts.Headers)
- setHeaders(req, hdr)
+ setHeaders(httpReq, client.opts.Headers)
+ setHeaders(httpReq, req.Header)
if len(contentType) > 0 {
- req.Header.Set(HeaderContentType, contentType)
+ httpReq.Header.Set(HeaderContentType, contentType)
}
- return req, nil
+ return httpReq, nil
}
-// Get send the GET request to server using rpath as target endpoint and
-// params as query parameters.
+// Get send the GET request to server using [ClientRequest.Path] as target
+// endpoint and [ClientRequest.Params] as query parameters.
// On success, it will return the uncompressed response body.
-func (client *Client) Get(rpath string, hdr http.Header, params url.Values) (
- res *http.Response, resBody []byte, err error,
-) {
- if params != nil {
- rpath += `?` + params.Encode()
+func (client *Client) Get(req ClientRequest) (res *http.Response, resBody []byte, err error) {
+ var params = req.paramsAsURLEncoded()
+ if len(params) != 0 {
+ req.Path += `?` + params
}
- return client.doRequest(http.MethodGet, hdr, rpath, ``, nil)
+ req.Method = RequestMethodGet
+
+ return client.doRequest(req)
}
// Head send the HEAD request to rpath endpoint, with optional hdr and
// params in query parameters.
// The returned resBody shoule be always nil.
-func (client *Client) Head(rpath string, hdr http.Header, params url.Values) (
- res *http.Response, resBody []byte, err error,
-) {
- if params != nil {
- rpath += `?` + params.Encode()
+func (client *Client) Head(req ClientRequest) (res *http.Response, resBody []byte, err error) {
+ var params = req.paramsAsURLEncoded()
+ if len(params) != 0 {
+ req.Path += `?` + params
}
- return client.doRequest(http.MethodHead, hdr, rpath, ``, nil)
+
+ req.Method = RequestMethodHead
+
+ return client.doRequest(req)
}
// Post send the POST request to rpath without setting "Content-Type".
// If the params is not nil, it will send as query parameters in the rpath.
-func (client *Client) Post(rpath string, hdr http.Header, params url.Values) (
- res *http.Response, resBody []byte, err error,
-) {
- if params != nil {
- rpath += `?` + params.Encode()
+func (client *Client) Post(req ClientRequest) (res *http.Response, resBody []byte, err error) {
+ var params = req.paramsAsURLEncoded()
+ if len(params) != 0 {
+ req.Path += `?` + params
}
- return client.doRequest(http.MethodPost, hdr, rpath, ``, nil)
+ req.Method = RequestMethodPost
+
+ return client.doRequest(req)
}
// PostForm send the POST request to rpath using
// "application/x-www-form-urlencoded".
-func (client *Client) PostForm(rpath string, hdr http.Header, params url.Values) (
- res *http.Response, resBody []byte, err error,
-) {
- body := strings.NewReader(params.Encode())
+func (client *Client) PostForm(req ClientRequest) (res *http.Response, resBody []byte, err error) {
+ var params = req.paramsAsURLEncoded()
+
+ req.Method = RequestMethodPost
+ req.contentType = ContentTypeForm
+ req.body = strings.NewReader(params)
- return client.doRequest(http.MethodPost, hdr, rpath, ContentTypeForm, body)
+ return client.doRequest(req)
}
-// PostFormData send the POST request to rpath with all parameters is send
+// PostFormData send the POST request to Path with all parameters is send
// using "multipart/form-data".
-func (client *Client) PostFormData(
- rpath string,
- hdr http.Header,
- params map[string][]byte,
-) (
- res *http.Response, resBody []byte, err error,
-) {
- contentType, strBody, err := GenerateFormData(params)
- if err != nil {
- return nil, nil, fmt.Errorf("http: PostFormData: %w", err)
+func (client *Client) PostFormData(req ClientRequest) (res *http.Response, resBody []byte, err error) {
+ var (
+ logp = `PostFormData`
+
+ params map[string][]byte
+ )
+
+ req.contentType = req.Type.String()
+
+ params = req.paramsAsMultipart()
+ if params == nil {
+ req.body = strings.NewReader(``)
+ } else {
+ var body string
+
+ req.contentType, body, err = GenerateFormData(params)
+ if err != nil {
+ return nil, nil, fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ req.body = strings.NewReader(body)
}
- body := strings.NewReader(strBody)
+ req.Method = RequestMethodPost
- return client.doRequest(http.MethodPost, hdr, rpath, contentType, body)
+ return client.doRequest(req)
}
// PostJSON send the POST request with content type set to "application/json"
-// and params encoded automatically to JSON.
-// The params must be a type than can be marshalled with [json.Marshal] or
+// and Params encoded automatically to JSON.
+// The Params must be a type than can be marshalled with [json.Marshal] or
// type that implement [json.Marshaler].
-func (client *Client) PostJSON(rpath string, hdr http.Header, params interface{}) (
- res *http.Response, resBody []byte, err error,
-) {
- paramsJSON, err := json.Marshal(params)
+func (client *Client) PostJSON(req ClientRequest) (res *http.Response, resBody []byte, err error) {
+ var (
+ logp = `PostJSON`
+
+ params []byte
+ )
+
+ params, err = json.Marshal(req.Params)
if err != nil {
- return nil, nil, fmt.Errorf("PostJSON: %w", err)
+ return nil, nil, fmt.Errorf(`%s: %w`, logp, err)
}
- body := bytes.NewReader(paramsJSON)
+ req.Method = RequestMethodPost
+ req.contentType = ContentTypeJSON
+ req.body = bytes.NewReader(params)
- return client.doRequest(http.MethodPost, hdr, rpath, ContentTypeJSON, body)
+ return client.doRequest(req)
}
// Put send the HTTP PUT request to rpath with optional, raw body.
// The Content-Type can be set in the hdr.
-func (client *Client) Put(rpath string, hdr http.Header, body []byte) (
- *http.Response, []byte, error,
-) {
- bodyReader := bytes.NewReader(body)
- return client.doRequest(http.MethodPut, hdr, rpath, ``, bodyReader)
+func (client *Client) Put(req ClientRequest) (*http.Response, []byte, error) {
+ var params = req.paramsAsBytes()
+
+ req.Method = RequestMethodPut
+ req.body = bytes.NewReader(params)
+
+ return client.doRequest(req)
}
// PutForm send the PUT request with params set in body using content type
// "application/x-www-form-urlencoded".
-func (client *Client) PutForm(rpath string, hdr http.Header, params url.Values) (
- res *http.Response, resBody []byte, err error,
-) {
- var body = strings.NewReader(params.Encode())
+func (client *Client) PutForm(req ClientRequest) (res *http.Response, resBody []byte, err error) {
+ var params = req.paramsAsURLEncoded()
+
+ req.Method = RequestMethodPut
+ req.contentType = ContentTypeForm
+ req.body = strings.NewReader(params)
- return client.doRequest(http.MethodPut, hdr, rpath, ContentTypeForm, body)
+ return client.doRequest(req)
}
// PutFormData send the PUT request with params set in body using content type
// "multipart/form-data".
-func (client *Client) PutFormData(rpath string, hdr http.Header, params map[string][]byte) (
- res *http.Response, resBody []byte, err error,
-) {
+func (client *Client) PutFormData(req ClientRequest) (res *http.Response, resBody []byte, err error) {
var (
- contentType string
- strBody string
- body *strings.Reader
+ logp = `PutFormData`
+ params map[string][]byte
)
- contentType, strBody, err = GenerateFormData(params)
- if err != nil {
- return nil, nil, fmt.Errorf(`http: PutFormData: %w`, err)
+ req.contentType = req.Type.String()
+
+ params = req.paramsAsMultipart()
+ if params == nil {
+ req.body = strings.NewReader(``)
+ } else {
+ var body string
+
+ req.contentType, body, err = GenerateFormData(params)
+ if err != nil {
+ return nil, nil, fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ req.body = strings.NewReader(body)
}
- body = strings.NewReader(strBody)
+ req.Method = RequestMethodPut
- return client.doRequest(http.MethodPut, hdr, rpath, contentType, body)
+ return client.doRequest(req)
}
// PutJSON send the PUT request with content type set to "application/json"
// and params encoded automatically to JSON.
-func (client *Client) PutJSON(rpath string, hdr http.Header, params interface{}) (
- res *http.Response, resBody []byte, err error,
-) {
- paramsJSON, err := json.Marshal(params)
+func (client *Client) PutJSON(req ClientRequest) (res *http.Response, resBody []byte, err error) {
+ var (
+ logp = `PutJSON`
+ params []byte
+ )
+
+ params, err = json.Marshal(params)
if err != nil {
- return nil, nil, fmt.Errorf("PutJSON: %w", err)
+ return nil, nil, fmt.Errorf(`%s: %w`, logp, err)
}
- body := bytes.NewReader(paramsJSON)
+ req.Method = RequestMethodPut
+ req.contentType = ContentTypeJSON
+ req.body = bytes.NewReader(params)
- return client.doRequest(http.MethodPut, hdr, rpath, ContentTypeJSON, body)
+ return client.doRequest(req)
}
-func (client *Client) doRequest(
- httpMethod string,
- hdr http.Header,
- rpath, contentType string,
- body io.Reader,
-) (
- res *http.Response, resBody []byte, err error,
-) {
- rpath = path.Join(`/`, rpath)
+func (client *Client) doRequest(req ClientRequest) (res *http.Response, resBody []byte, err error) {
+ req.Path = path.Join(`/`, req.Path)
var (
- fullURL = client.opts.ServerURL + rpath
+ fullURL = client.opts.ServerURL + req.Path
ctx = context.Background()
httpReq *http.Request
)
- httpReq, err = http.NewRequestWithContext(ctx, httpMethod, fullURL, body)
+ httpReq, err = http.NewRequestWithContext(ctx, req.Method.String(), fullURL, req.body)
if err != nil {
return nil, nil, err
}
setHeaders(httpReq, client.opts.Headers)
- setHeaders(httpReq, hdr)
+ setHeaders(httpReq, req.Header)
- if len(contentType) > 0 {
- httpReq.Header.Set(HeaderContentType, contentType)
+ if len(req.contentType) > 0 {
+ httpReq.Header.Set(HeaderContentType, req.contentType)
}
return client.Do(httpReq)
diff --git a/lib/http/client_request.go b/lib/http/client_request.go
index 808f38b3..9447576a 100644
--- a/lib/http/client_request.go
+++ b/lib/http/client_request.go
@@ -17,9 +17,9 @@ import (
// ClientRequest define the parameters for each Client methods.
type ClientRequest struct {
- // Headers additional header to be send on request.
+ // Header additional header to be send on request.
// This field is optional.
- Headers http.Header
+ Header http.Header
//
// Params define parameter to be send on request.
@@ -46,12 +46,16 @@ type ClientRequest struct {
// * If Type is RequestTypeJSON and Params is not nil, the params will
// be encoded as JSON in body using json.Encode().
//
- Params interface{}
+ Params any
+
+ body io.Reader
// The Path to resource on the server.
// This field is required, if its empty default to "/".
Path string
+ contentType string
+
// The HTTP method of request.
// This field is optional, if its empty default to RequestMethodGet
// (GET).
@@ -146,7 +150,7 @@ func (creq *ClientRequest) toHTTPRequest(client *Client) (httpReq *http.Request,
if client != nil {
setHeaders(httpReq, client.opts.Headers)
}
- setHeaders(httpReq, creq.Headers)
+ setHeaders(httpReq, creq.Header)
if len(contentType) > 0 {
httpReq.Header.Set(HeaderContentType, contentType)
@@ -154,3 +158,56 @@ func (creq *ClientRequest) toHTTPRequest(client *Client) (httpReq *http.Request,
return httpReq, nil
}
+
+// paramsAsURLEncoded convert the Params as [url.Values] and return the
+// [Encode]d value.
+// If Params is nil or Params is not [url.Values], it will return an empty
+// string.
+func (creq *ClientRequest) paramsAsURLEncoded() string {
+ if creq.Params == nil {
+ return ``
+ }
+
+ var (
+ params url.Values
+ ok bool
+ )
+ params, ok = creq.Params.(url.Values)
+ if !ok {
+ return ``
+ }
+ return params.Encode()
+}
+
+// paramsAsMultipart convert the Params as "map[string][]byte" and return the
+// content type and body.
+func (creq *ClientRequest) paramsAsMultipart() (params map[string][]byte) {
+ if creq.Params == nil {
+ return nil
+ }
+
+ var ok bool
+
+ params, ok = creq.Params.(map[string][]byte)
+ if !ok {
+ return nil
+ }
+
+ return params
+}
+
+// paramsAsBytes convert the Params as []byte.
+func (creq *ClientRequest) paramsAsBytes() (body []byte) {
+ if creq.Params == nil {
+ return nil
+ }
+
+ var ok bool
+
+ body, ok = creq.Params.([]byte)
+ if !ok {
+ return nil
+ }
+
+ return body
+}
diff --git a/lib/http/endpoint_example_test.go b/lib/http/endpoint_example_test.go
index 15b667d3..fbf4db7b 100644
--- a/lib/http/endpoint_example_test.go
+++ b/lib/http/endpoint_example_test.go
@@ -64,9 +64,15 @@ func ExampleEndpoint_errorHandler() {
client = NewClient(clientOpts)
)
- params := url.Values{}
+ var params = url.Values{}
params.Set("error", "400:error with status code")
- httpres, resbody, err := client.Get(`/`, nil, params) //nolint: bodyclose
+
+ var req = ClientRequest{
+ Path: `/`,
+ Params: params,
+ }
+
+ httpres, resbody, err := client.Get(req) //nolint: bodyclose
if err != nil {
log.Println(err)
return
@@ -74,7 +80,8 @@ func ExampleEndpoint_errorHandler() {
fmt.Printf("%d: %s\n", httpres.StatusCode, resbody)
params.Set("error", "error without status code")
- httpres, resbody, err = client.Get(`/`, nil, params) //nolint: bodyclose
+
+ httpres, resbody, err = client.Get(req) //nolint: bodyclose
if err != nil {
log.Println(err)
return
diff --git a/lib/http/endpoint_response_example_test.go b/lib/http/endpoint_response_example_test.go
index 2e78c66f..63dd1961 100644
--- a/lib/http/endpoint_response_example_test.go
+++ b/lib/http/endpoint_response_example_test.go
@@ -78,10 +78,14 @@ func ExampleEndpointResponse() {
}
cl = NewClient(clientOpts)
params = url.Values{}
+ req = ClientRequest{
+ Path: `/`,
+ Params: params,
+ }
)
// Test call endpoint without "id" parameter.
- _, resBody, err := cl.Get("/", nil, params) //nolint: bodyclose
+ _, resBody, err := cl.Get(req) //nolint: bodyclose
if err != nil {
log.Fatal(err)
}
@@ -89,16 +93,22 @@ func ExampleEndpointResponse() {
// Test call endpoint with "id" parameter set to "0", it should return
// HTTP status 500 with custom message.
+
params.Set("id", "0")
- _, resBody, err = cl.Get("/", nil, params) //nolint: bodyclose
+ req.Params = params
+
+ _, resBody, err = cl.Get(req) //nolint: bodyclose
if err != nil {
log.Fatal(err)
}
fmt.Printf("GET /?id=0 => %s\n", resBody)
// Test with "id" parameter is set.
+
params.Set("id", "1000")
- _, resBody, err = cl.Get("/", nil, params) //nolint: bodyclose
+ req.Params = params
+
+ _, resBody, err = cl.Get(req) //nolint: bodyclose
if err != nil {
log.Fatal(err)
}
diff --git a/lib/http/server_example_test.go b/lib/http/server_example_test.go
index 460a78fd..42571834 100644
--- a/lib/http/server_example_test.go
+++ b/lib/http/server_example_test.go
@@ -71,9 +71,15 @@ func ExampleServer_customHTTPStatusCode() {
ServerURL: `http://127.0.0.1:8123`,
}
client = NewClient(clientOpts)
+ req = ClientRequest{
+ Path: epCustom.Path,
+ }
+
+ httpRes *http.Response
+ resBody []byte
)
- httpRes, resBody, err := client.PostJSON(epCustom.Path, nil, nil) //nolint: bodyclose
+ httpRes, resBody, err = client.PostJSON(req) //nolint: bodyclose
if err != nil {
log.Println(err)
return
@@ -81,6 +87,7 @@ func ExampleServer_customHTTPStatusCode() {
fmt.Printf("%d\n", httpRes.StatusCode)
fmt.Printf("%s\n", resBody)
+
// Output:
// 400
// {"status":400}
diff --git a/lib/http/server_test.go b/lib/http/server_test.go
index 219df876..4071f201 100644
--- a/lib/http/server_test.go
+++ b/lib/http/server_test.go
@@ -1051,7 +1051,11 @@ func TestServer_handleRange(t *testing.T) {
header.Set(HeaderRange, string(headerRange))
- httpRes, resBody, err = cl.Get(`/index.html`, header, nil) //nolint: bodyclose
+ var req = ClientRequest{
+ Path: `/index.html`,
+ Header: header,
+ }
+ httpRes, resBody, err = cl.Get(req) //nolint: bodyclose
if err != nil {
t.Fatal(err)
}
@@ -1215,7 +1219,11 @@ func TestServerHandleRangeBig(t *testing.T) {
resBody []byte
)
- httpRes, resBody, err = cl.Head(pathBig, nil, nil) //nolint: bodyclose
+ var req = ClientRequest{
+ Path: pathBig,
+ }
+
+ httpRes, resBody, err = cl.Head(req) //nolint: bodyclose
if err != nil {
t.Fatal(err)
}
@@ -1231,7 +1239,12 @@ func TestServerHandleRangeBig(t *testing.T) {
headers.Set(HeaderRange, `bytes=0-`)
- httpRes, resBody, err = cl.Get(pathBig, headers, nil) //nolint: bodyclose
+ req = ClientRequest{
+ Path: pathBig,
+ Header: headers,
+ }
+
+ httpRes, resBody, err = cl.Get(req) //nolint: bodyclose
if err != nil {
t.Fatal(err)
}