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 }