github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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  	// HTTPEndpointPattern is the URL path pattern registered with
    25  	// the API server. This includes wildcards (starting with ":") that
    26  	// are converted into URL query values by the pattern mux. Also see
    27  	// apiserver/apiserver.go.
    28  	HTTPEndpointPattern = "/applications/:application/resources/:resource"
    29  
    30  	// HTTPEndpointPath is the URL path, with substitutions, for
    31  	// a resource request.
    32  	HTTPEndpointPath = "/applications/%s/resources/%s"
    33  )
    34  
    35  const (
    36  	// ContentTypeRaw is the HTTP content-type value used for raw, unformattedcontent.
    37  	ContentTypeRaw = "application/octet-stream"
    38  
    39  	// ContentTypeJSON is the HTTP content-type value used for JSON content.
    40  	ContentTypeJSON = "application/json"
    41  )
    42  
    43  const (
    44  	// HeaderContentType is the header name for the type of a file upload.
    45  	HeaderContentType = "Content-Type"
    46  	// HeaderContentSha384 is the header name for the sha hash of a file upload.
    47  	HeaderContentSha384 = "Content-Sha384"
    48  	// HeaderContentLength is the header name for the length of a file upload.
    49  	HeaderContentLength = "Content-Length"
    50  	// HeaderContentDisposition is the header name for value that holds the filename.
    51  	// The params are formatted according to  RFC 2045 and RFC 2616 (see
    52  	// mime.ParseMediaType and mime.FormatMediaType).
    53  	HeaderContentDisposition = "Content-Disposition"
    54  )
    55  
    56  const (
    57  	// MediaTypeFormData is the media type for file uploads (see
    58  	// mime.FormatMediaType).
    59  	MediaTypeFormData = "form-data"
    60  	// QueryParamPendingID is the query parameter we use to send up the pending id.
    61  	QueryParamPendingID = "pendingid"
    62  )
    63  
    64  const (
    65  	// TODO(natefinch): remove this and use http.MethodPut when we upgrade to go1.5+.
    66  
    67  	// MethodPut is the common HTTP PUT method.
    68  	MethodPut = "PUT"
    69  )
    70  
    71  // NewEndpointPath returns the API URL path for the identified resource.
    72  func NewEndpointPath(service, name string) string {
    73  	return fmt.Sprintf(HTTPEndpointPath, service, name)
    74  }
    75  
    76  // ExtractEndpointDetails pulls the endpoint wildcard values from
    77  // the provided URL.
    78  func ExtractEndpointDetails(url *url.URL) (service, name string) {
    79  	service = url.Query().Get(":application")
    80  	name = url.Query().Get(":resource")
    81  	return service, name
    82  }
    83  
    84  // TODO(ericsnow) These are copied from apiserver/httpcontext.go...
    85  
    86  // SendHTTPError sends a JSON-encoded error response
    87  // for errors encountered during processing.
    88  func SendHTTPError(w http.ResponseWriter, err error) {
    89  	err1, statusCode := common.ServerErrorAndStatus(err)
    90  	logger.Debugf("sending error: %d %v", statusCode, err1)
    91  	SendHTTPStatusAndJSON(w, statusCode, &params.ErrorResult{
    92  		Error: err1,
    93  	})
    94  }
    95  
    96  // SendStatusAndJSON sends an HTTP status code and
    97  // a JSON-encoded response to a client.
    98  func SendHTTPStatusAndJSON(w http.ResponseWriter, statusCode int, response interface{}) {
    99  	body, err := json.Marshal(response)
   100  	if err != nil {
   101  		http.Error(w, errors.Annotatef(err, "cannot marshal JSON result %#v", response).Error(), 504)
   102  		return
   103  	}
   104  
   105  	if statusCode == http.StatusUnauthorized {
   106  		w.Header().Set("WWW-Authenticate", `Basic realm="juju"`)
   107  	}
   108  	w.Header().Set("Content-Type", params.ContentTypeJSON)
   109  	w.Header().Set("Content-Length", fmt.Sprint(len(body)))
   110  	w.WriteHeader(statusCode)
   111  	w.Write(body)
   112  }