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  }