grokify/mogo

View on GitHub
net/http/httputilmore/http_response.go

Summary

Maintainability
A
2 hrs
Test Coverage
package httputilmore

import (
    "errors"
    "fmt"
    "io"
    "net/http"
    "strings"
    "time"

    "github.com/grokify/mogo/encoding/jsonutil"
    "github.com/grokify/mogo/errors/errorsutil"
)

func ResponseIsContentType(ct string, r *http.Response) bool {
    if r == nil {
        return false
    }
    ct = strings.ToLower(strings.TrimSpace(ct))
    ctv := strings.ToLower(r.Header.Get(HeaderContentType))
    if ct == "" {
        if ct == ctv {
            return true
        } else {
            return false
        }
    }
    return strings.Index(ctv, ct) == 0
}

// ProxyResponse copies the information from a `*http.Response` to a
// `http.ResponseWriter`.
func ProxyResponse(w http.ResponseWriter, resp *http.Response) ([]byte, error) {
    if resp == nil {
        return nil, errors.New("E_NIL_HTTP_RESPONSE")
    }
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }
    _, err = w.Write(body)
    if err != nil {
        return body, err
    }
    headers := []string{
        HeaderContentEncoding,
        HeaderContentLanguage,
        HeaderContentTransferEncoding,
        HeaderContentType}
    for _, header := range headers {
        if len(resp.Header.Get(header)) > 0 {
            w.Header().Add(header, resp.Header.Get(header))
        }
    }
    w.WriteHeader(resp.StatusCode)
    return body, nil
}

func CondenseResponseNot2xxToError(resp *http.Response, err error, msg string) error {
    if err != nil {
        if len(msg) > 0 {
            return errorsutil.Wrap(err, msg)
        } else {
            return err
        }
    } else if resp == nil {
        return errors.New("*http.Response_is_nil")
    } else if resp.StatusCode < 200 || resp.StatusCode > 299 {
        if len(msg) > 0 {
            msg += ": "
        }
        more := []string{}
        body, err := io.ReadAll(resp.Body)
        if err != nil {
            more = append(more, err.Error())
        } else {
            more = append(more, string(body))
        }
        moreString := ""
        jsn, err := json.Marshal(more)
        if err != nil {
            moreString = err.Error()
        } else {
            moreString = string(jsn)
        }
        return fmt.Errorf("non_2xx_status_code [%d] [%s] [%s]", resp.StatusCode, msg, moreString)
    }
    return nil
}

// ResponseInfo is a generic struct to handle response info.
type ResponseInfo struct {
    Name       string            `json:"name,omitempty"` // to distinguish from other requests
    Method     string            `json:"method,omitempty"`
    URL        string            `json:"url,omitempty"`
    StatusCode int               `json:"statusCode,omitempty"`
    Time       time.Time         `json:"time,omitempty"`
    Headers    map[string]string `json:"headers,omitempty"`
    Body       string            `json:"body,omitempty"`
}

// ToJSON returns ResponseInfo as a JSON byte array, embedding json.Marshal
// errors if encountered.
func (resIn *ResponseInfo) ToJSON() []byte {
    bytes, err := json.Marshal(resIn)
    if err != nil {
        resIn2 := ResponseInfo{StatusCode: 500, Body: err.Error()}
        bytes, _ := json.Marshal(resIn2)
        return bytes
    }
    return bytes
}

func ResponseWriterWriteJSON(w http.ResponseWriter, statusCode int, body any, prefix, indent string) error {
    if w == nil {
        return errors.New("nil response writer")
    }
    wroteStatus := false
    var errs []error
    if body != nil {
        bytes, err := jsonutil.MarshalSimple(body, "", "  ")
        if err != nil {
            errs = append(errs, err)
            gr := ResponseInfo{
                Body: err.Error()}
            bytes, err = jsonutil.MarshalSimple(gr, "", "  ")
            if err != nil {
                errs = append(errs, err)
            }
            w.WriteHeader(500)
            _, err2 := w.Write(bytes)
            errs = append(errs, err2)
            return errorsutil.Join(false, errs...)
        }
        _, err = w.Write(bytes)
        if err != nil {
            errs = append(errs, err)
            gr := ResponseInfo{
                Body: err.Error()}
            bytes, err = jsonutil.MarshalSimple(gr, "", "  ")
            if err != nil {
                errs = append(errs, err)
            }
            w.WriteHeader(500)
            _, err2 := w.Write(bytes)
            errs = append(errs, err2)
            return errorsutil.Join(false, errs...)
        }
    }
    if !wroteStatus && statusCode != 0 {
        w.WriteHeader(statusCode)
    }
    return nil
}