github.com/ImPedro29/bor@v0.2.7/consensus/bor/rest.go (about) 1 package bor 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "net/url" 9 "sort" 10 "time" 11 12 "github.com/ethereum/go-ethereum/log" 13 ) 14 15 var ( 16 stateFetchLimit = 50 17 ) 18 19 // ResponseWithHeight defines a response object type that wraps an original 20 // response with a height. 21 type ResponseWithHeight struct { 22 Height string `json:"height"` 23 Result json.RawMessage `json:"result"` 24 } 25 26 type IHeimdallClient interface { 27 Fetch(path string, query string) (*ResponseWithHeight, error) 28 FetchWithRetry(path string, query string) (*ResponseWithHeight, error) 29 FetchStateSyncEvents(fromID uint64, to int64) ([]*EventRecordWithTime, error) 30 } 31 32 type HeimdallClient struct { 33 urlString string 34 client http.Client 35 } 36 37 func NewHeimdallClient(urlString string) (*HeimdallClient, error) { 38 h := &HeimdallClient{ 39 urlString: urlString, 40 client: http.Client{ 41 Timeout: time.Duration(5 * time.Second), 42 }, 43 } 44 return h, nil 45 } 46 47 func (h *HeimdallClient) FetchStateSyncEvents(fromID uint64, to int64) ([]*EventRecordWithTime, error) { 48 eventRecords := make([]*EventRecordWithTime, 0) 49 for { 50 queryParams := fmt.Sprintf("from-id=%d&to-time=%d&limit=%d", fromID, to, stateFetchLimit) 51 log.Info("Fetching state sync events", "queryParams", queryParams) 52 response, err := h.FetchWithRetry("clerk/event-record/list", queryParams) 53 if err != nil { 54 return nil, err 55 } 56 var _eventRecords []*EventRecordWithTime 57 if response.Result == nil { // status 204 58 break 59 } 60 if err := json.Unmarshal(response.Result, &_eventRecords); err != nil { 61 return nil, err 62 } 63 eventRecords = append(eventRecords, _eventRecords...) 64 if len(_eventRecords) < stateFetchLimit { 65 break 66 } 67 fromID += uint64(stateFetchLimit) 68 } 69 70 sort.SliceStable(eventRecords, func(i, j int) bool { 71 return eventRecords[i].ID < eventRecords[j].ID 72 }) 73 return eventRecords, nil 74 } 75 76 // Fetch fetches response from heimdall 77 func (h *HeimdallClient) Fetch(rawPath string, rawQuery string) (*ResponseWithHeight, error) { 78 u, err := url.Parse(h.urlString) 79 if err != nil { 80 return nil, err 81 } 82 83 u.Path = rawPath 84 u.RawQuery = rawQuery 85 86 return h.internalFetch(u) 87 } 88 89 // FetchWithRetry returns data from heimdall with retry 90 func (h *HeimdallClient) FetchWithRetry(rawPath string, rawQuery string) (*ResponseWithHeight, error) { 91 u, err := url.Parse(h.urlString) 92 if err != nil { 93 return nil, err 94 } 95 96 u.Path = rawPath 97 u.RawQuery = rawQuery 98 99 for { 100 res, err := h.internalFetch(u) 101 if err == nil && res != nil { 102 return res, nil 103 } 104 log.Info("Retrying again in 5 seconds for next Heimdall span", "path", u.Path) 105 time.Sleep(5 * time.Second) 106 } 107 } 108 109 // internal fetch method 110 func (h *HeimdallClient) internalFetch(u *url.URL) (*ResponseWithHeight, error) { 111 res, err := h.client.Get(u.String()) 112 if err != nil { 113 return nil, err 114 } 115 defer res.Body.Close() 116 117 // check status code 118 if res.StatusCode != 200 && res.StatusCode != 204 { 119 return nil, fmt.Errorf("Error while fetching data from Heimdall") 120 } 121 122 // unmarshall data from buffer 123 var response ResponseWithHeight 124 if res.StatusCode == 204 { 125 return &response, nil 126 } 127 128 // get response 129 body, err := ioutil.ReadAll(res.Body) 130 if err != nil { 131 return nil, err 132 } 133 134 if err := json.Unmarshal(body, &response); err != nil { 135 return nil, err 136 } 137 138 return &response, nil 139 }