|
6 | 6 | "crypto/ecdsa"
|
7 | 7 | "fmt"
|
8 | 8 | "math/big"
|
| 9 | + "net/url" |
| 10 | + "strings" |
9 | 11 | "time"
|
10 | 12 |
|
11 | 13 | "github.com/ava-labs/avalanche-cli/pkg/utils"
|
@@ -259,15 +261,80 @@ func SendTransaction(
|
259 | 261 | return err
|
260 | 262 | }
|
261 | 263 |
|
| 264 | +func FindOutScheme(rpcURL string) (ethclient.Client, string, error) { |
| 265 | + if b, err := HasScheme(rpcURL); err != nil { |
| 266 | + return nil, "", err |
| 267 | + } else if b { |
| 268 | + return nil, "", fmt.Errorf("url does have scheme") |
| 269 | + } |
| 270 | + notDeterminedErr := fmt.Errorf("url %s has no scheme and protocol could not be determined", rpcURL) |
| 271 | + // let's start with ws it always give same error for http/https/wss |
| 272 | + scheme := "ws://" |
| 273 | + ctx, cancel := utils.GetAPILargeContext() |
| 274 | + defer cancel() |
| 275 | + client, err := ethclient.DialContext(ctx, scheme+rpcURL) |
| 276 | + if err == nil { |
| 277 | + return client, scheme, nil |
| 278 | + } else if !strings.Contains(err.Error(), "websocket: bad handshake") { |
| 279 | + return nil, "", notDeterminedErr |
| 280 | + } |
| 281 | + // wss give specific errors for http/http |
| 282 | + scheme = "wss://" |
| 283 | + client, err = ethclient.DialContext(ctx, scheme+rpcURL) |
| 284 | + if err == nil { |
| 285 | + return client, scheme, nil |
| 286 | + } else if !strings.Contains(err.Error(), "websocket: bad handshake") && // may be https |
| 287 | + !strings.Contains(err.Error(), "first record does not look like a TLS handshake") { // may be http |
| 288 | + return nil, "", notDeterminedErr |
| 289 | + } |
| 290 | + // https/http discrimination based on sending a specific query |
| 291 | + scheme = "https://" |
| 292 | + client, err = ethclient.DialContext(ctx, scheme+rpcURL) |
| 293 | + if err == nil { |
| 294 | + _, err = client.ChainID(ctx) |
| 295 | + switch { |
| 296 | + case err == nil: |
| 297 | + return client, scheme, nil |
| 298 | + case strings.Contains(err.Error(), "server gave HTTP response to HTTPS client"): |
| 299 | + scheme = "http://" |
| 300 | + client, err = ethclient.DialContext(ctx, scheme+rpcURL) |
| 301 | + if err == nil { |
| 302 | + return client, scheme, nil |
| 303 | + } |
| 304 | + } |
| 305 | + } |
| 306 | + return nil, "", notDeterminedErr |
| 307 | +} |
| 308 | + |
| 309 | +func HasScheme(rpcURL string) (bool, error) { |
| 310 | + if parsedURL, err := url.Parse(rpcURL); err != nil { |
| 311 | + if !strings.Contains(err.Error(), "first path segment in URL cannot contain colon") { |
| 312 | + return false, err |
| 313 | + } |
| 314 | + return false, nil |
| 315 | + } else if parsedURL.Scheme == "" { |
| 316 | + return false, nil |
| 317 | + } |
| 318 | + return true, nil |
| 319 | +} |
| 320 | + |
262 | 321 | func GetClient(rpcURL string) (ethclient.Client, error) {
|
263 | 322 | var (
|
264 | 323 | client ethclient.Client
|
265 | 324 | err error
|
266 | 325 | )
|
| 326 | + hasScheme, err := HasScheme(rpcURL) |
| 327 | + if err != nil { |
| 328 | + return nil, err |
| 329 | + } |
267 | 330 | for i := 0; i < repeatsOnFailure; i++ {
|
268 | 331 | ctx, cancel := utils.GetAPILargeContext()
|
269 | 332 | defer cancel()
|
270 |
| - client, err = ethclient.DialContext(ctx, rpcURL) |
| 333 | + if hasScheme { |
| 334 | + client, err = ethclient.DialContext(ctx, rpcURL) |
| 335 | + } else { |
| 336 | + client, _, err = FindOutScheme(rpcURL) |
| 337 | + } |
271 | 338 | if err == nil {
|
272 | 339 | break
|
273 | 340 | }
|
|
0 commit comments