github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/httputil/http.go (about) 1 // Copyright 2014 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package httputil 12 13 import ( 14 "bytes" 15 "io/ioutil" 16 "net/http" 17 "strconv" 18 19 "github.com/cockroachdb/cockroach/pkg/util/protoutil" 20 "github.com/cockroachdb/errors" 21 "github.com/gogo/protobuf/jsonpb" 22 ) 23 24 const ( 25 // AcceptHeader is the canonical header name for accept. 26 AcceptHeader = "Accept" 27 // AcceptEncodingHeader is the canonical header name for accept encoding. 28 AcceptEncodingHeader = "Accept-Encoding" 29 // ContentEncodingHeader is the canonical header name for content type. 30 ContentEncodingHeader = "Content-Encoding" 31 // ContentTypeHeader is the canonical header name for content type. 32 ContentTypeHeader = "Content-Type" 33 // JSONContentType is the JSON content type. 34 JSONContentType = "application/json" 35 // AltJSONContentType is the alternate JSON content type. 36 AltJSONContentType = "application/x-json" 37 // ProtoContentType is the protobuf content type. 38 ProtoContentType = "application/x-protobuf" 39 // AltProtoContentType is the alternate protobuf content type. 40 AltProtoContentType = "application/x-google-protobuf" 41 // PlaintextContentType is the plaintext content type. 42 PlaintextContentType = "text/plain" 43 // GzipEncoding is the gzip encoding. 44 GzipEncoding = "gzip" 45 ) 46 47 // GetJSON uses the supplied client to GET the URL specified by the parameters 48 // and unmarshals the result into response. 49 // TODO(someone): make this context-aware, see client.go. 50 func GetJSON(httpClient http.Client, path string, response protoutil.Message) error { 51 req, err := http.NewRequest("GET", path, nil) 52 if err != nil { 53 return err 54 } 55 _, err = doJSONRequest(httpClient, req, response) 56 return err 57 } 58 59 // PostJSON uses the supplied client to POST request to the URL specified by 60 // the parameters and unmarshals the result into response. 61 // TODO(someone): make this context-aware, see client.go. 62 func PostJSON(httpClient http.Client, path string, request, response protoutil.Message) error { 63 // Hack to avoid upsetting TestProtoMarshal(). 64 marshalFn := (&jsonpb.Marshaler{}).Marshal 65 66 var buf bytes.Buffer 67 if err := marshalFn(&buf, request); err != nil { 68 return err 69 } 70 req, err := http.NewRequest("POST", path, &buf) 71 if err != nil { 72 return err 73 } 74 _, err = doJSONRequest(httpClient, req, response) 75 return err 76 } 77 78 // PostJSONWithRequest uses the supplied client to POST request to the URL 79 // specified by the parameters and unmarshals the result into response. 80 // 81 // The response is returned to the caller, though its body will have been 82 // closed. 83 // TODO(someone): make this context-aware, see client.go. 84 func PostJSONWithRequest( 85 httpClient http.Client, path string, request, response protoutil.Message, 86 ) (*http.Response, error) { 87 // Hack to avoid upsetting TestProtoMarshal(). 88 marshalFn := (&jsonpb.Marshaler{}).Marshal 89 90 var buf bytes.Buffer 91 if err := marshalFn(&buf, request); err != nil { 92 return nil, err 93 } 94 req, err := http.NewRequest("POST", path, &buf) 95 if err != nil { 96 return nil, err 97 } 98 99 return doJSONRequest(httpClient, req, response) 100 } 101 102 func doJSONRequest( 103 httpClient http.Client, req *http.Request, response protoutil.Message, 104 ) (*http.Response, error) { 105 if timeout := httpClient.Timeout; timeout > 0 { 106 req.Header.Set("Grpc-Timeout", strconv.FormatInt(timeout.Nanoseconds(), 10)+"n") 107 } 108 req.Header.Set(AcceptHeader, JSONContentType) 109 resp, err := httpClient.Do(req) 110 if err != nil { 111 return nil, err 112 } 113 defer resp.Body.Close() 114 if contentType := resp.Header.Get(ContentTypeHeader); !(resp.StatusCode == http.StatusOK && contentType == JSONContentType) { 115 b, err := ioutil.ReadAll(resp.Body) 116 return resp, errors.Errorf( 117 "status: %s, content-type: %s, body: %s, error: %v", resp.Status, contentType, b, err, 118 ) 119 } 120 return resp, jsonpb.Unmarshal(resp.Body, response) 121 }