github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/node/api/api.go (about)

     1  package api
     2  
     3  import (
     4  	"encoding/json"
     5  	"net/http"
     6  	"strings"
     7  
     8  	"SiaPrime/build"
     9  	"SiaPrime/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", "SiaPrime-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", "SiaPrime-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", "SiaPrime-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", "SiaPrime-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  	pool         modules.Pool
   100  	stratumminer modules.StratumMiner
   101  	index        modules.Index
   102  
   103  	router http.Handler
   104  }
   105  
   106  // api.ServeHTTP implements the http.Handler interface.
   107  func (api *API) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   108  	api.router.ServeHTTP(w, r)
   109  }
   110  
   111  // New creates a new Sia API from the provided modules.  The API will require
   112  // authentication using HTTP basic auth for certain endpoints of the supplied
   113  // password is not the empty string.  Usernames are ignored for authentication.
   114  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, p modules.Pool, sm modules.StratumMiner, index modules.Index) *API {
   115  	api := &API{
   116  		cs:           cs,
   117  		explorer:     e,
   118  		gateway:      g,
   119  		host:         h,
   120  		miner:        m,
   121  		renter:       r,
   122  		tpool:        tp,
   123  		wallet:       w,
   124  		pool:         p,
   125  		stratumminer: sm,
   126  		index:        index,
   127  	}
   128  
   129  	// Register API handlers
   130  	api.buildHTTPRoutes(requiredUserAgent, requiredPassword)
   131  
   132  	return api
   133  }
   134  
   135  // UnrecognizedCallHandler handles calls to unknown pages (404).
   136  func UnrecognizedCallHandler(w http.ResponseWriter, req *http.Request) {
   137  	WriteError(w, Error{"404 - Refer to API.md"}, http.StatusNotFound)
   138  }
   139  
   140  // WriteError an error to the API caller.
   141  func WriteError(w http.ResponseWriter, err Error, code int) {
   142  	w.Header().Set("Content-Type", "application/json; charset=utf-8")
   143  	w.WriteHeader(code)
   144  	encodingErr := json.NewEncoder(w).Encode(err)
   145  	if _, isJsonErr := encodingErr.(*json.SyntaxError); isJsonErr {
   146  		// Marshalling should only fail in the event of a developer error.
   147  		// Specifically, only non-marshallable types should cause an error here.
   148  		build.Critical("failed to encode API error response:", encodingErr)
   149  	}
   150  }
   151  
   152  // WriteJSON writes the object to the ResponseWriter. If the encoding fails, an
   153  // error is written instead. The Content-Type of the response header is set
   154  // accordingly.
   155  func WriteJSON(w http.ResponseWriter, obj interface{}) {
   156  	w.Header().Set("Content-Type", "application/json; charset=utf-8")
   157  	err := json.NewEncoder(w).Encode(obj)
   158  	if _, isJsonErr := err.(*json.SyntaxError); isJsonErr {
   159  		// Marshalling should only fail in the event of a developer error.
   160  		// Specifically, only non-marshallable types should cause an error here.
   161  		build.Critical("failed to encode API response:", err)
   162  	}
   163  }
   164  
   165  // WriteSuccess writes the HTTP header with status 204 No Content to the
   166  // ResponseWriter. WriteSuccess should only be used to indicate that the
   167  // requested action succeeded AND there is no data to return.
   168  func WriteSuccess(w http.ResponseWriter) {
   169  	w.WriteHeader(http.StatusNoContent)
   170  }