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