forked from OffchainLabs/nitro
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrest_server_list.go
142 lines (125 loc) · 3.65 KB
/
rest_server_list.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
package das
import (
"bufio"
"context"
"fmt"
"net/http"
"strings"
"time"
"github.com/ethereum/go-ethereum/log"
)
const initialMaxRecurseDepth uint16 = 8
// RestfulServerURLsFromList reads a list of Restful server URLs from a remote URL.
// The contents at the remote URL are parsed into a series of whitespace-separated words.
// Each word is interpreted as the URL of a Restful server, except that if a word is "LIST"
// (case-insensitive) then the following word is interpreted as the URL of another list,
// which is recursively fetched. The depth of recursion is limited to initialMaxRecurseDepth.
func RestfulServerURLsFromList(ctx context.Context, listUrl string) ([]string, error) {
client := &http.Client{}
urls, err := restfulServerURLsFromList(ctx, client, listUrl, initialMaxRecurseDepth, make(map[string]bool))
if err != nil {
return nil, err
}
// deduplicate the list of URL strings
seen := make(map[string]bool)
dedupedUrls := []string{}
for _, url := range urls {
if !seen[url] {
seen[url] = true
dedupedUrls = append(dedupedUrls, url)
}
}
return dedupedUrls, nil
}
func restfulServerURLsFromList(
ctx context.Context,
client *http.Client,
listUrl string,
maxRecurseDepth uint16,
visitedSoFar map[string]bool,
) ([]string, error) {
if visitedSoFar[listUrl] {
return []string{}, nil
}
visitedSoFar[listUrl] = true
urls := []string{}
request, err := http.NewRequestWithContext(ctx, http.MethodGet, listUrl, nil)
if err != nil {
return nil, err
}
resp, err := client.Do(request)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("recieved error response (%d) fetching online-url-list at %s", resp.StatusCode, listUrl)
}
scanner := bufio.NewScanner(resp.Body)
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
word := scanner.Text()
if strings.ToLower(word) == "list" {
if maxRecurseDepth > 0 && scanner.Scan() {
word = scanner.Text()
subUrls, err := restfulServerURLsFromList(ctx, client, word, maxRecurseDepth-1, visitedSoFar)
if err != nil {
return nil, err
}
urls = append(urls, subUrls...)
}
} else {
urls = append(urls, word)
}
}
return urls, nil
}
const maxListFetchTime = time.Minute
func StartRestfulServerListFetchDaemon(ctx context.Context, listUrl string, updatePeriod time.Duration) <-chan []string {
updateChan := make(chan []string)
if listUrl == "" {
log.Info("Trying to start RestfulServerListFetchDaemon with empty online-url-list, not starting.")
return updateChan
}
if updatePeriod == 0 {
panic("RestfulServerListFetchDaemon started with zero updatePeriod")
}
downloadAndSend := func() error { // download and send once
subCtx, subCtxCancel := context.WithTimeout(ctx, maxListFetchTime)
defer subCtxCancel()
urls, err := RestfulServerURLsFromList(subCtx, listUrl)
if err != nil {
return err
}
select {
case updateChan <- urls:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
go func() {
defer close(updateChan)
// send the first result immediately
err := downloadAndSend()
if err != nil {
log.Warn("Couldn't download data availability online-url-list, will retry immediately", "err", err)
}
// now send periodically
ticker := time.NewTicker(updatePeriod)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
err := downloadAndSend()
if err != nil {
log.Warn(fmt.Sprintf("Couldn't download data availability online-url-list, will retry in %s", updatePeriod), "err", err)
}
}
}
}()
return updateChan
}