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, ¶ms.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 }