mirror of
https://gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud
synced 2024-11-30 00:56:25 +00:00
216 lines
4.9 KiB
Go
216 lines
4.9 KiB
Go
|
package grequests
|
|||
|
|
|||
|
import (
|
|||
|
"bytes"
|
|||
|
"encoding/json"
|
|||
|
"encoding/xml"
|
|||
|
"io"
|
|||
|
"io/ioutil"
|
|||
|
"net/http"
|
|||
|
"os"
|
|||
|
)
|
|||
|
|
|||
|
// Response is what is returned to a user when they fire off a request
|
|||
|
type Response struct {
|
|||
|
|
|||
|
// Ok is a boolean flag that validates that the server returned a 2xx code
|
|||
|
Ok bool
|
|||
|
|
|||
|
// This is the Go error flag – if something went wrong within the request, this flag will be set.
|
|||
|
Error error
|
|||
|
|
|||
|
// We want to abstract (at least at the moment) the Go http.Response object away. So we are going to make use of it
|
|||
|
// internal but not give the user access
|
|||
|
RawResponse *http.Response
|
|||
|
|
|||
|
// StatusCode is the HTTP Status Code returned by the HTTP Response. Taken from resp.StatusCode
|
|||
|
StatusCode int
|
|||
|
|
|||
|
// Header is a net/http/Header structure
|
|||
|
Header http.Header
|
|||
|
|
|||
|
internalByteBuffer *bytes.Buffer
|
|||
|
}
|
|||
|
|
|||
|
func buildResponse(resp *http.Response, err error) (*Response, error) {
|
|||
|
// If the connection didn't succeed we just return a blank response
|
|||
|
if err != nil {
|
|||
|
return &Response{Error: err}, err
|
|||
|
}
|
|||
|
|
|||
|
goodResp := &Response{
|
|||
|
// If your code is within the 2xx range – the response is considered `Ok`
|
|||
|
Ok: resp.StatusCode >= 200 && resp.StatusCode < 300,
|
|||
|
Error: nil,
|
|||
|
RawResponse: resp,
|
|||
|
StatusCode: resp.StatusCode,
|
|||
|
Header: resp.Header,
|
|||
|
internalByteBuffer: bytes.NewBuffer([]byte{}),
|
|||
|
}
|
|||
|
// EnsureResponseFinalized(goodResp) This will come back in 1.0
|
|||
|
return goodResp, nil
|
|||
|
}
|
|||
|
|
|||
|
// Read is part of our ability to support io.ReadCloser if someone wants to make use of the raw body
|
|||
|
func (r *Response) Read(p []byte) (n int, err error) {
|
|||
|
|
|||
|
if r.Error != nil {
|
|||
|
return -1, r.Error
|
|||
|
}
|
|||
|
|
|||
|
return r.RawResponse.Body.Read(p)
|
|||
|
}
|
|||
|
|
|||
|
// Close is part of our ability to support io.ReadCloser if someone wants to make use of the raw body
|
|||
|
func (r *Response) Close() error {
|
|||
|
|
|||
|
if r.Error != nil {
|
|||
|
return r.Error
|
|||
|
}
|
|||
|
|
|||
|
io.Copy(ioutil.Discard, r)
|
|||
|
|
|||
|
return r.RawResponse.Body.Close()
|
|||
|
}
|
|||
|
|
|||
|
// DownloadToFile allows you to download the contents of the response to a file
|
|||
|
func (r *Response) DownloadToFile(fileName string) error {
|
|||
|
|
|||
|
if r.Error != nil {
|
|||
|
return r.Error
|
|||
|
}
|
|||
|
|
|||
|
fd, err := os.Create(fileName)
|
|||
|
|
|||
|
if err != nil {
|
|||
|
return err
|
|||
|
}
|
|||
|
|
|||
|
defer r.Close() // This is a noop if we use the internal ByteBuffer
|
|||
|
defer fd.Close()
|
|||
|
|
|||
|
if _, err := io.Copy(fd, r.getInternalReader()); err != nil && err != io.EOF {
|
|||
|
return err
|
|||
|
}
|
|||
|
|
|||
|
return nil
|
|||
|
}
|
|||
|
|
|||
|
// getInternalReader because we implement io.ReadCloser and optionally hold a large buffer of the response (created by
|
|||
|
// the user's request)
|
|||
|
func (r *Response) getInternalReader() io.Reader {
|
|||
|
|
|||
|
if r.internalByteBuffer.Len() != 0 {
|
|||
|
return r.internalByteBuffer
|
|||
|
}
|
|||
|
return r
|
|||
|
}
|
|||
|
|
|||
|
// XML is a method that will populate a struct that is provided `userStruct` with the XML returned within the
|
|||
|
// response body
|
|||
|
func (r *Response) XML(userStruct interface{}, charsetReader XMLCharDecoder) error {
|
|||
|
|
|||
|
if r.Error != nil {
|
|||
|
return r.Error
|
|||
|
}
|
|||
|
|
|||
|
xmlDecoder := xml.NewDecoder(r.getInternalReader())
|
|||
|
|
|||
|
if charsetReader != nil {
|
|||
|
xmlDecoder.CharsetReader = charsetReader
|
|||
|
}
|
|||
|
|
|||
|
defer r.Close()
|
|||
|
|
|||
|
if err := xmlDecoder.Decode(&userStruct); err != nil && err != io.EOF {
|
|||
|
return err
|
|||
|
}
|
|||
|
|
|||
|
return nil
|
|||
|
}
|
|||
|
|
|||
|
// JSON is a method that will populate a struct that is provided `userStruct` with the JSON returned within the
|
|||
|
// response body
|
|||
|
func (r *Response) JSON(userStruct interface{}) error {
|
|||
|
|
|||
|
if r.Error != nil {
|
|||
|
return r.Error
|
|||
|
}
|
|||
|
|
|||
|
jsonDecoder := json.NewDecoder(r.getInternalReader())
|
|||
|
defer r.Close()
|
|||
|
|
|||
|
if err := jsonDecoder.Decode(&userStruct); err != nil && err != io.EOF {
|
|||
|
return err
|
|||
|
}
|
|||
|
|
|||
|
return nil
|
|||
|
}
|
|||
|
|
|||
|
// createResponseBytesBuffer is a utility method that will populate the internal byte reader – this is largely used for .String()
|
|||
|
// and .Bytes()
|
|||
|
func (r *Response) populateResponseByteBuffer() {
|
|||
|
|
|||
|
// Have I done this already?
|
|||
|
if r.internalByteBuffer.Len() != 0 {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
defer r.Close()
|
|||
|
|
|||
|
// Is there any content?
|
|||
|
if r.RawResponse.ContentLength == 0 {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// Did the server tell us how big the response is going to be?
|
|||
|
if r.RawResponse.ContentLength > 0 {
|
|||
|
r.internalByteBuffer.Grow(int(r.RawResponse.ContentLength))
|
|||
|
}
|
|||
|
|
|||
|
if _, err := io.Copy(r.internalByteBuffer, r); err != nil && err != io.EOF {
|
|||
|
r.Error = err
|
|||
|
r.RawResponse.Body.Close()
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
// Bytes returns the response as a byte array
|
|||
|
func (r *Response) Bytes() []byte {
|
|||
|
|
|||
|
if r.Error != nil {
|
|||
|
return nil
|
|||
|
}
|
|||
|
|
|||
|
r.populateResponseByteBuffer()
|
|||
|
|
|||
|
// Are we still empty?
|
|||
|
if r.internalByteBuffer.Len() == 0 {
|
|||
|
return nil
|
|||
|
}
|
|||
|
return r.internalByteBuffer.Bytes()
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
// String returns the response as a string
|
|||
|
func (r *Response) String() string {
|
|||
|
if r.Error != nil {
|
|||
|
return ""
|
|||
|
}
|
|||
|
|
|||
|
r.populateResponseByteBuffer()
|
|||
|
|
|||
|
return r.internalByteBuffer.String()
|
|||
|
}
|
|||
|
|
|||
|
// ClearInternalBuffer is a function that will clear the internal buffer that we use to hold the .String() and .Bytes()
|
|||
|
// data. Once you have used these functions – you may want to free up the memory.
|
|||
|
func (r *Response) ClearInternalBuffer() {
|
|||
|
|
|||
|
if r == nil || r.internalByteBuffer == nil {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
r.internalByteBuffer.Reset()
|
|||
|
}
|