github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/resource/api/http.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package api
     5  
     6  // TODO(ericsnow) Eliminate the apiserver dependencies, if possible.
     7  
     8  import (
     9  	"encoding/json"
    10  	"fmt"
    11  	"net/http"
    12  	"net/url"
    13  
    14  	"github.com/juju/errors"
    15  	"github.com/juju/loggo"
    16  
    17  	"github.com/juju/juju/apiserver/common"
    18  	"github.com/juju/juju/apiserver/params"
    19  )
    20  
    21  var logger = loggo.GetLogger("juju.resource.api")
    22  
    23  const (
    24  	// HTTPEndpointPath is the URL path, with substitutions, for
    25  	// a resource request.
    26  	HTTPEndpointPath = "/applications/%s/resources/%s"
    27  )
    28  
    29  const (
    30  	// ContentTypeRaw is the HTTP content-type value used for raw, unformattedcontent.
    31  	ContentTypeRaw = "application/octet-stream"
    32  
    33  	// ContentTypeJSON is the HTTP content-type value used for JSON content.
    34  	ContentTypeJSON = "application/json"
    35  )
    36  
    37  const (
    38  	// HeaderContentType is the header name for the type of a file upload.
    39  	HeaderContentType = "Content-Type"
    40  	// HeaderContentSha384 is the header name for the sha hash of a file upload.
    41  	HeaderContentSha384 = "Content-Sha384"
    42  	// HeaderContentLength is the header name for the length of a file upload.
    43  	HeaderContentLength = "Content-Length"
    44  	// HeaderContentDisposition is the header name for value that holds the filename.
    45  	// The params are formatted according to  RFC 2045 and RFC 2616 (see
    46  	// mime.ParseMediaType and mime.FormatMediaType).
    47  	HeaderContentDisposition = "Content-Disposition"
    48  )
    49  
    50  const (
    51  	// MediaTypeFormData is the media type for file uploads (see
    52  	// mime.FormatMediaType).
    53  	MediaTypeFormData = "form-data"
    54  	// QueryParamPendingID is the query parameter we use to send up the pending id.
    55  	QueryParamPendingID = "pendingid"
    56  )
    57  
    58  // NewEndpointPath returns the API URL path for the identified resource.
    59  func NewEndpointPath(application, name string) string {
    60  	return fmt.Sprintf(HTTPEndpointPath, application, name)
    61  }
    62  
    63  // ExtractEndpointDetails pulls the endpoint wildcard values from
    64  // the provided URL.
    65  func ExtractEndpointDetails(url *url.URL) (application, name string) {
    66  	application = url.Query().Get(":application")
    67  	name = url.Query().Get(":resource")
    68  	return application, name
    69  }
    70  
    71  // TODO(ericsnow) These are copied from apiserver/httpcontext.go...
    72  
    73  // SendHTTPError sends a JSON-encoded error response
    74  // for errors encountered during processing.
    75  func SendHTTPError(w http.ResponseWriter, err error) {
    76  	err1, statusCode := common.ServerErrorAndStatus(err)
    77  	logger.Debugf("sending error: %d %v", statusCode, err1)
    78  	SendHTTPStatusAndJSON(w, statusCode, &params.ErrorResult{
    79  		Error: err1,
    80  	})
    81  }
    82  
    83  // SendHTTPStatusAndJSON sends an HTTP status code and
    84  // a JSON-encoded response to a client.
    85  func SendHTTPStatusAndJSON(w http.ResponseWriter, statusCode int, response interface{}) {
    86  	body, err := json.Marshal(response)
    87  	if err != nil {
    88  		http.Error(w, errors.Annotatef(err, "cannot marshal JSON result %#v", response).Error(), 504)
    89  		return
    90  	}
    91  
    92  	if statusCode == http.StatusUnauthorized {
    93  		w.Header().Set("WWW-Authenticate", `Basic realm="juju"`)
    94  	}
    95  	w.Header().Set("Content-Type", params.ContentTypeJSON)
    96  	w.Header().Set("Content-Length", fmt.Sprint(len(body)))
    97  	w.WriteHeader(statusCode)
    98  	w.Write(body)
    99  }