gitlab.com/jokerrs1/Sia@v1.3.2/node/api/api.go (about)

     1  package api
     2  
     3  import (
     4  	"encoding/json"
     5  	"net/http"
     6  	"strings"
     7  
     8  	"github.com/NebulousLabs/Sia/build"
     9  	"github.com/NebulousLabs/Sia/modules"
    10  )
    11  
    12  // Error is a type that is encoded as JSON and returned in an API response in
    13  // the event of an error. Only the Message field is required. More fields may
    14  // be added to this struct in the future for better error reporting.
    15  type Error struct {
    16  	// Message describes the error in English. Typically it is set to
    17  	// `err.Error()`. This field is required.
    18  	Message string `json:"message"`
    19  
    20  	// TODO: add a Param field with the (omitempty option in the json tag)
    21  	// to indicate that the error was caused by an invalid, missing, or
    22  	// incorrect parameter. This is not trivial as the API does not
    23  	// currently do parameter validation itself. For example, the
    24  	// /gateway/connect endpoint relies on the gateway.Connect method to
    25  	// validate the netaddress. However, this prevents the API from knowing
    26  	// whether an error returned by gateway.Connect is because of a
    27  	// connection error or an invalid netaddress parameter. Validating
    28  	// parameters in the API is not sufficient, as a parameter's value may
    29  	// be valid or invalid depending on the current state of a module.
    30  }
    31  
    32  // Error implements the error interface for the Error type. It returns only the
    33  // Message field.
    34  func (err Error) Error() string {
    35  	return err.Message
    36  }
    37  
    38  // HttpGET is a utility function for making http get requests to sia with a
    39  // whitelisted user-agent. A non-2xx response does not return an error.
    40  func HttpGET(url string) (resp *http.Response, err error) {
    41  	req, err := http.NewRequest("GET", url, nil)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	req.Header.Set("User-Agent", "Sia-Agent")
    46  	return http.DefaultClient.Do(req)
    47  }
    48  
    49  // HttpGETAuthenticated is a utility function for making authenticated http get
    50  // requests to sia with a whitelisted user-agent and the supplied password. A
    51  // non-2xx response does not return an error.
    52  func HttpGETAuthenticated(url string, password string) (resp *http.Response, err error) {
    53  	req, err := http.NewRequest("GET", url, nil)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	req.Header.Set("User-Agent", "Sia-Agent")
    58  	req.SetBasicAuth("", password)
    59  	return http.DefaultClient.Do(req)
    60  }
    61  
    62  // HttpPOST is a utility function for making post requests to sia with a
    63  // whitelisted user-agent. A non-2xx response does not return an error.
    64  func HttpPOST(url string, data string) (resp *http.Response, err error) {
    65  	req, err := http.NewRequest("POST", url, strings.NewReader(data))
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	req.Header.Set("User-Agent", "Sia-Agent")
    70  	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    71  	return http.DefaultClient.Do(req)
    72  }
    73  
    74  // HttpPOSTAuthenticated is a utility function for making authenticated http
    75  // post requests to sia with a whitelisted user-agent and the supplied
    76  // password. A non-2xx response does not return an error.
    77  func HttpPOSTAuthenticated(url string, data string, password string) (resp *http.Response, err error) {
    78  	req, err := http.NewRequest("POST", url, strings.NewReader(data))
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	req.Header.Set("User-Agent", "Sia-Agent")
    83  	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    84  	req.SetBasicAuth("", password)
    85  	return http.DefaultClient.Do(req)
    86  }
    87  
    88  // API encapsulates a collection of modules and implements a http.Handler
    89  // to access their methods.
    90  type API struct {
    91  	cs       modules.ConsensusSet
    92  	explorer modules.Explorer
    93  	gateway  modules.Gateway
    94  	host     modules.Host
    95  	miner    modules.Miner
    96  	renter   modules.Renter
    97  	tpool    modules.TransactionPool
    98  	wallet   modules.Wallet
    99  
   100  	router http.Handler
   101  }
   102  
   103  // api.ServeHTTP implements the http.Handler interface.
   104  func (api *API) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   105  	api.router.ServeHTTP(w, r)
   106  }
   107  
   108  // New creates a new Sia API from the provided modules.  The API will require
   109  // authentication using HTTP basic auth for certain endpoints of the supplied
   110  // password is not the empty string.  Usernames are ignored for authentication.
   111  func New(requiredUserAgent string, requiredPassword string, cs modules.ConsensusSet, e modules.Explorer, g modules.Gateway, h modules.Host, m modules.Miner, r modules.Renter, tp modules.TransactionPool, w modules.Wallet) *API {
   112  	api := &API{
   113  		cs:       cs,
   114  		explorer: e,
   115  		gateway:  g,
   116  		host:     h,
   117  		miner:    m,
   118  		renter:   r,
   119  		tpool:    tp,
   120  		wallet:   w,
   121  	}
   122  
   123  	// Register API handlers
   124  	api.buildHTTPRoutes(requiredUserAgent, requiredPassword)
   125  
   126  	return api
   127  }
   128  
   129  // UnrecognizedCallHandler handles calls to unknown pages (404).
   130  func UnrecognizedCallHandler(w http.ResponseWriter, req *http.Request) {
   131  	WriteError(w, Error{"404 - Refer to API.md"}, http.StatusNotFound)
   132  }
   133  
   134  // WriteError an error to the API caller.
   135  func WriteError(w http.ResponseWriter, err Error, code int) {
   136  	w.Header().Set("Content-Type", "application/json; charset=utf-8")
   137  	w.WriteHeader(code)
   138  	encodingErr := json.NewEncoder(w).Encode(err)
   139  	if _, isJsonErr := encodingErr.(*json.SyntaxError); isJsonErr {
   140  		// Marshalling should only fail in the event of a developer error.
   141  		// Specifically, only non-marshallable types should cause an error here.
   142  		build.Critical("failed to encode API error response:", encodingErr)
   143  	}
   144  }
   145  
   146  // WriteJSON writes the object to the ResponseWriter. If the encoding fails, an
   147  // error is written instead. The Content-Type of the response header is set
   148  // accordingly.
   149  func WriteJSON(w http.ResponseWriter, obj interface{}) {
   150  	w.Header().Set("Content-Type", "application/json; charset=utf-8")
   151  	err := json.NewEncoder(w).Encode(obj)
   152  	if _, isJsonErr := err.(*json.SyntaxError); isJsonErr {
   153  		// Marshalling should only fail in the event of a developer error.
   154  		// Specifically, only non-marshallable types should cause an error here.
   155  		build.Critical("failed to encode API response:", err)
   156  	}
   157  }
   158  
   159  // WriteSuccess writes the HTTP header with status 204 No Content to the
   160  // ResponseWriter. WriteSuccess should only be used to indicate that the
   161  // requested action succeeded AND there is no data to return.
   162  func WriteSuccess(w http.ResponseWriter) {
   163  	w.WriteHeader(http.StatusNoContent)
   164  }