summaryrefslogtreecommitdiff
path: root/internal/http.go
diff options
context:
space:
mode:
authorjwijenbergh <jeroenwijenbergh@protonmail.com>2022-04-22 16:29:59 +0200
committerjwijenbergh <jeroenwijenbergh@protonmail.com>2022-04-22 16:29:59 +0200
commitb1d92b395322f2164ccfb44b0f7caebbaece6b62 (patch)
tree2133e4045b4af4d07a98674b7ae3a234670f0305 /internal/http.go
parent3a4ae2942b43923ff98fd2eca8878c3cf145686c (diff)
Refactor: Restructure project
- Add an internal folder where all the internal code lives - Make a state.go and state_test.go for the public interface This gives a more clear separation between functions and modules. It also makes this a more typical Go project setup.
Diffstat (limited to 'internal/http.go')
-rw-r--r--internal/http.go172
1 files changed, 172 insertions, 0 deletions
diff --git a/internal/http.go b/internal/http.go
new file mode 100644
index 0000000..8ca8cb9
--- /dev/null
+++ b/internal/http.go
@@ -0,0 +1,172 @@
+package internal
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strings"
+)
+
+type HTTPResourceError struct {
+ URL string
+ Err error
+}
+
+func (e *HTTPResourceError) Error() string {
+ return fmt.Sprintf("failed obtaining HTTP resource %s with error %v", e.URL, e.Err)
+}
+
+type HTTPStatusError struct {
+ URL string
+ Status int
+}
+
+func (e *HTTPStatusError) Error() string {
+ return fmt.Sprintf("failed obtaining HTTP resource %s as it gave an unsuccesful status code %d", e.URL, e.Status)
+}
+
+type HTTPReadError struct {
+ URL string
+ Err error
+}
+
+func (e *HTTPReadError) Error() string {
+ return fmt.Sprintf("failed reading HTTP resource %s with error %v", e.URL, e.Err)
+}
+
+type HTTPParseJsonError struct {
+ URL string
+ Body string
+ Err error
+}
+
+func (e *HTTPParseJsonError) Error() string {
+ return fmt.Sprintf("failed parsing json %s for HTTP resource %s with error %v", e.Body, e.URL, e.Err)
+}
+
+type HTTPRequestCreateError struct {
+ URL string
+ Err error
+}
+
+func (e *HTTPRequestCreateError) Error() string {
+ return fmt.Sprintf("failed to create HTTP request with url %s and error %v", e.URL, e.Err)
+}
+
+type URLParameters map[string]string
+
+type HTTPOptionalParams struct {
+ Headers http.Header
+ URLParameters URLParameters
+ Body url.Values
+}
+
+// Construct an URL including on parameters
+func HTTPConstructURL(baseURL string, parameters URLParameters) (string, error) {
+ url, err := url.Parse(baseURL)
+ if err != nil {
+ return "", err
+ }
+
+ q := url.Query()
+
+ for parameter, value := range parameters {
+ q.Set(parameter, value)
+ }
+ url.RawQuery = q.Encode()
+ return url.String(), nil
+}
+
+// Convenience functions
+func HTTPGet(url string) (http.Header, []byte, error) {
+ return HTTPMethodWithOpts(http.MethodGet, url, nil)
+}
+
+func HTTPPost(url string, body url.Values) (http.Header, []byte, error) {
+ return HTTPMethodWithOpts(http.MethodGet, url, &HTTPOptionalParams{Body: body})
+}
+
+func HTTPGetWithOpts(url string, opts *HTTPOptionalParams) (http.Header, []byte, error) {
+ return HTTPMethodWithOpts(http.MethodGet, url, opts)
+}
+
+func HTTPPostWithOpts(url string, opts *HTTPOptionalParams) (http.Header, []byte, error) {
+ return HTTPMethodWithOpts(http.MethodPost, url, opts)
+}
+
+func httpOptionalURL(url string, opts *HTTPOptionalParams) (string, error) {
+ if opts != nil {
+ url, urlErr := HTTPConstructURL(url, opts.URLParameters)
+
+ if urlErr != nil {
+ return url, &HTTPRequestCreateError{URL: url, Err: urlErr}
+ }
+ return url, nil
+ }
+ return url, nil
+}
+
+func httpOptionalHeaders(req *http.Request, opts *HTTPOptionalParams) {
+ // Add headers
+ if opts != nil && req != nil {
+ for k, v := range opts.Headers {
+ req.Header.Add(k, v[0])
+ }
+ }
+}
+
+func httpOptionalBodyReader(opts *HTTPOptionalParams) io.Reader {
+ if opts != nil && opts.Body != nil {
+ return strings.NewReader(opts.Body.Encode())
+ }
+ return nil
+}
+
+func HTTPMethodWithOpts(method string, url string, opts *HTTPOptionalParams) (http.Header, []byte, error) {
+ // Make sure the url contains all the parameters
+ // This can return an error,
+ // it already has the right error so so we don't wrap it further
+ url, urlErr := httpOptionalURL(url, opts)
+ if urlErr != nil {
+ return nil, nil, urlErr
+ }
+
+ // Create a client
+ client := &http.Client{}
+
+ // Create request object with the body reader generated from the optional arguments
+ req, reqErr := http.NewRequest(method, url, httpOptionalBodyReader(opts))
+ if reqErr != nil {
+ return nil, nil, &HTTPRequestCreateError{URL: url, Err: reqErr}
+ }
+
+ // See https://stackoverflow.com/questions/17714494/golang-http-request-results-in-eof-errors-when-making-multiple-requests-successi
+ req.Close = true
+
+ // Make sure the headers contain all the parameters
+ httpOptionalHeaders(req, opts)
+
+ // Do request
+ resp, respErr := client.Do(req)
+ if respErr != nil {
+ return nil, nil, &HTTPResourceError{URL: url, Err: respErr}
+ }
+
+ // Request successful, make sure body is closed at the end
+ defer resp.Body.Close()
+
+ // Return a string
+ body, readErr := ioutil.ReadAll(resp.Body)
+ if readErr != nil {
+ return resp.Header, nil, &HTTPReadError{URL: url, Err: readErr}
+ }
+
+ if resp.StatusCode < 200 || resp.StatusCode > 299 {
+ return resp.Header, body, &HTTPStatusError{URL: url, Status: resp.StatusCode}
+ }
+
+ // Return the body in bytes and signal the status error if there was one
+ return resp.Header, body, nil
+}