github.com/MetalBlockchain/metalgo@v1.11.9/utils/rpc/json.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package rpc
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"fmt"
    10  	"net/http"
    11  	"net/url"
    12  
    13  	rpc "github.com/gorilla/rpc/v2/json2"
    14  )
    15  
    16  func SendJSONRequest(
    17  	ctx context.Context,
    18  	uri *url.URL,
    19  	method string,
    20  	params interface{},
    21  	reply interface{},
    22  	options ...Option,
    23  ) error {
    24  	requestBodyBytes, err := rpc.EncodeClientRequest(method, params)
    25  	if err != nil {
    26  		return fmt.Errorf("failed to encode client params: %w", err)
    27  	}
    28  
    29  	ops := NewOptions(options)
    30  	uri.RawQuery = ops.queryParams.Encode()
    31  
    32  	request, err := http.NewRequestWithContext(
    33  		ctx,
    34  		http.MethodPost,
    35  		uri.String(),
    36  		bytes.NewBuffer(requestBodyBytes),
    37  	)
    38  	if err != nil {
    39  		return fmt.Errorf("failed to create request: %w", err)
    40  	}
    41  
    42  	request.Header = ops.headers
    43  	request.Header.Set("Content-Type", "application/json")
    44  
    45  	resp, err := http.DefaultClient.Do(request)
    46  	if err != nil {
    47  		return fmt.Errorf("failed to issue request: %w", err)
    48  	}
    49  
    50  	// Return an error for any non successful status code
    51  	if resp.StatusCode < 200 || resp.StatusCode > 299 {
    52  		// Drop any error during close to report the original error
    53  		_ = resp.Body.Close()
    54  		return fmt.Errorf("received status code: %d", resp.StatusCode)
    55  	}
    56  
    57  	if err := rpc.DecodeClientResponse(resp.Body, reply); err != nil {
    58  		// Drop any error during close to report the original error
    59  		_ = resp.Body.Close()
    60  		return fmt.Errorf("failed to decode client response: %w", err)
    61  	}
    62  	return resp.Body.Close()
    63  }