diff options
| author | Shulhan <ms@kilabit.info> | 2024-03-12 01:06:15 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2024-03-14 23:51:34 +0700 |
| commit | c7de9d3df399c7ee1c2db2c4c253869951487ab5 (patch) | |
| tree | dea8152a693cafcfb147839cc95d1ed24fb9fcb8 | |
| parent | 9da27235f996db4b2be0ba29f4bcecae1511519b (diff) | |
| download | pakakeh.go-c7de9d3df399c7ee1c2db2c4c253869951487ab5.tar.xz | |
lib/http: refactor Client methods to use struct ClientRequest
| -rw-r--r-- | api/slack/webhook_client.go | 6 | ||||
| -rw-r--r-- | api/telegram/bot/bot.go | 105 | ||||
| -rw-r--r-- | lib/http/client.go | 325 | ||||
| -rw-r--r-- | lib/http/client_request.go | 65 | ||||
| -rw-r--r-- | lib/http/endpoint_example_test.go | 13 | ||||
| -rw-r--r-- | lib/http/endpoint_response_example_test.go | 16 | ||||
| -rw-r--r-- | lib/http/server_example_test.go | 9 | ||||
| -rw-r--r-- | lib/http/server_test.go | 19 |
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) } |
