| 1 | // pollEndpoint is a helper utility that waits for a http endpoint to be reachable and return with http.StatusOK |
| 2 | package main |
| 3 | |
| 4 | import ( |
| 5 | "context" |
| 6 | "flag" |
| 7 | "io" |
| 8 | "net" |
| 9 | "net/http" |
| 10 | "os" |
| 11 | "time" |
| 12 | |
| 13 | logging "github.com/ipfs/go-log/v2" |
| 14 | ma "github.com/multiformats/go-multiaddr" |
| 15 | manet "github.com/multiformats/go-multiaddr/net" |
| 16 | ) |
| 17 | |
| 18 | var ( |
| 19 | host = flag.String("host", "/ip4/127.0.0.1/tcp/5001", "the multiaddr host to dial on") |
| 20 | tries = flag.Int("tries", 10, "how many tries to make before failing") |
| 21 | timeout = flag.Duration("tout", time.Second, "how long to wait between attempts") |
| 22 | httpURL = flag.String("http-url", "", "HTTP URL to fetch") |
| 23 | httpOut = flag.Bool("http-out", false, "Print the HTTP response body to stdout") |
| 24 | verbose = flag.Bool("v", false, "verbose logging") |
| 25 | ) |
| 26 | |
| 27 | var log = logging.Logger("pollEndpoint") |
| 28 | |
| 29 | func main() { |
| 30 | flag.Parse() |
| 31 | |
| 32 | // extract address from host flag |
| 33 | addr, err := ma.NewMultiaddr(*host) |
| 34 | if err != nil { |
| 35 | log.Fatal("NewMultiaddr() failed: ", err) |
| 36 | } |
| 37 | |
| 38 | if *verbose { // lower log level |
| 39 | logging.SetDebugLogging() |
| 40 | } |
| 41 | |
| 42 | // show what we got |
| 43 | start := time.Now() |
| 44 | log.Debugf("starting at %s, tries: %d, timeout: %s, addr: %s", start, *tries, *timeout, addr) |
| 45 | |
| 46 | connTries := *tries |
| 47 | for connTries > 0 { |
| 48 | c, err := manet.Dial(addr) |
| 49 | if err == nil { |
| 50 | log.Debugf("ok - endpoint reachable with %d tries remaining, took %s", *tries, time.Since(start)) |
| 51 | c.Close() |
| 52 | break |
| 53 | } |
| 54 | log.Debug("connect failed: ", err) |
| 55 | time.Sleep(*timeout) |
| 56 | connTries-- |
| 57 | } |
| 58 | |
| 59 | if err != nil { |
| 60 | goto Fail |
| 61 | } |
| 62 | |
| 63 | if *httpURL != "" { |
| 64 | dialer := &connDialer{addr: addr} |
| 65 | httpClient := http.Client{Transport: &http.Transport{ |
| 66 | DialContext: dialer.DialContext, |
| 67 | }} |
| 68 | reqTries := *tries |
| 69 | for reqTries > 0 { |
| 70 | try := (*tries - reqTries) + 1 |
| 71 | log.Debugf("trying HTTP req %d: '%s'", try, *httpURL) |
| 72 | if tryHTTPGet(&httpClient, *httpURL) { |
| 73 | log.Debugf("HTTP req %d to '%s' succeeded", try, *httpURL) |
| 74 | goto Success |
| 75 | } |
| 76 | log.Debugf("HTTP req %d to '%s' failed", try, *httpURL) |
| 77 | time.Sleep(*timeout) |
| 78 | reqTries-- |
| 79 | } |
| 80 | goto Fail |
| 81 | } |
| 82 | |
| 83 | Success: |
| 84 | os.Exit(0) |
| 85 | |
| 86 | Fail: |
| 87 | log.Error("failed") |
| 88 | os.Exit(1) |
| 89 | } |
| 90 | |
| 91 | func tryHTTPGet(client *http.Client, url string) bool { |
| 92 | resp, err := client.Get(*httpURL) |
| 93 | if resp != nil && resp.Body != nil { |
| 94 | defer resp.Body.Close() |
| 95 | } |
| 96 | if err != nil { |
| 97 | return false |
| 98 | } |
| 99 | if resp.StatusCode != http.StatusOK { |
| 100 | return false |
| 101 | } |
| 102 | if *httpOut { |
| 103 | _, err := io.Copy(os.Stdout, resp.Body) |
| 104 | if err != nil { |
| 105 | panic(err) |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | return true |
| 110 | } |
| 111 | |
| 112 | type connDialer struct { |
| 113 | addr ma.Multiaddr |
| 114 | } |
| 115 | |
| 116 | func (d connDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { |
| 117 | return (&manet.Dialer{}).DialContext(ctx, d.addr) |
| 118 | } |