github.com/network-quality/goresponsiveness@v0.0.0-20240129151524-343954285090/config/config.go (about) 1 /* 2 * This file is part of Go Responsiveness. 3 * 4 * Go Responsiveness is free software: you can redistribute it and/or modify it under 5 * the terms of the GNU General Public License as published by the Free Software Foundation, 6 * either version 2 of the License, or (at your option) any later version. 7 * Go Responsiveness is distributed in the hope that it will be useful, but WITHOUT ANY 8 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 9 * PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 * 11 * You should have received a copy of the GNU General Public License along 12 * with Go Responsiveness. If not, see <https://www.gnu.org/licenses/>. 13 */ 14 15 package config 16 17 import ( 18 "crypto/tls" 19 "encoding/json" 20 "fmt" 21 "io" 22 "net/http" 23 "net/url" 24 "strings" 25 26 "github.com/network-quality/goresponsiveness/utilities" 27 ) 28 29 type ConfigUrls struct { 30 SmallUrl string `json:"small_https_download_url"` 31 LargeUrl string `json:"large_https_download_url"` 32 UploadUrl string `json:"https_upload_url"` 33 } 34 35 type Config struct { 36 Version int 37 Urls ConfigUrls `json:"urls"` 38 Source string 39 ConnectToAddr string `json:"test_endpoint"` 40 } 41 42 func (c *Config) Get(configHost string, configPath string, insecureSkipVerify bool, keyLogger io.Writer) error { 43 configTransport := &http.Transport{ 44 TLSClientConfig: &tls.Config{ 45 InsecureSkipVerify: insecureSkipVerify, 46 }, 47 Proxy: http.ProxyFromEnvironment, 48 } 49 if !utilities.IsInterfaceNil(keyLogger) { 50 configTransport.TLSClientConfig.KeyLogWriter = keyLogger 51 } 52 53 utilities.OverrideHostTransport(configTransport, c.ConnectToAddr) 54 55 configClient := &http.Client{Transport: configTransport} 56 57 // Extraneous /s in URLs is normally okay, but the Apple CDN does not 58 // like them. Make sure that we put exactly one (1) / between the host 59 // and the path. 60 if !strings.HasPrefix(configPath, "/") { 61 configPath = "/" + configPath 62 } 63 64 c.Source = fmt.Sprintf("https://%s%s", configHost, configPath) 65 req, err := http.NewRequest("GET", c.Source, nil) 66 if err != nil { 67 return fmt.Errorf( 68 "Error: Could not create request for configuration host %s: %v", 69 configHost, 70 err, 71 ) 72 } 73 74 req.Header.Set("User-Agent", utilities.UserAgent()) 75 76 resp, err := configClient.Do(req) 77 if err != nil { 78 return fmt.Errorf( 79 "Error: could not connect to configuration host %s: %v", 80 configHost, 81 err, 82 ) 83 } 84 defer resp.Body.Close() 85 86 if resp.StatusCode != 200 { 87 return fmt.Errorf( 88 "Error: Configuration host %s returned %d for config request", 89 configHost, 90 resp.StatusCode, 91 ) 92 } 93 94 jsonConfig, err := io.ReadAll(resp.Body) 95 if err != nil { 96 return fmt.Errorf( 97 "Error: Could not read configuration content downloaded from %s: %v", 98 c.Source, 99 err, 100 ) 101 } 102 103 err = json.Unmarshal(jsonConfig, c) 104 if err != nil { 105 return fmt.Errorf( 106 "could not parse configuration returned from %s: %v", 107 c.Source, 108 err, 109 ) 110 } 111 112 return nil 113 } 114 115 func (c *Config) String() string { 116 return fmt.Sprintf( 117 "Version: %d\nSmall URL: %s\nLarge URL: %s\nUpload URL: %s\nEndpoint: %s\n", 118 c.Version, 119 c.Urls.SmallUrl, 120 c.Urls.LargeUrl, 121 c.Urls.UploadUrl, 122 c.ConnectToAddr, 123 ) 124 } 125 126 func (c *Config) IsValid() error { 127 if parsedUrl, err := url.ParseRequestURI(c.Urls.LargeUrl); err != nil || 128 parsedUrl.Scheme != "https" { 129 return fmt.Errorf( 130 "configuration url large_https_download_url is invalid: %s", 131 utilities.Conditional( 132 len(c.Urls.LargeUrl) != 0, 133 c.Urls.LargeUrl, 134 "Missing", 135 ), 136 ) 137 } 138 if parsedUrl, err := url.ParseRequestURI(c.Urls.SmallUrl); err != nil || 139 parsedUrl.Scheme != "https" { 140 return fmt.Errorf( 141 "configuration url small_https_download_url is invalid: %s", 142 utilities.Conditional( 143 len(c.Urls.SmallUrl) != 0, 144 c.Urls.SmallUrl, 145 "Missing", 146 ), 147 ) 148 } 149 if parsedUrl, err := url.ParseRequestURI(c.Urls.UploadUrl); err != nil || 150 parsedUrl.Scheme != "https" { 151 return fmt.Errorf( 152 "configuration url https_upload_url is invalid: %s", 153 utilities.Conditional( 154 len(c.Urls.UploadUrl) != 0, 155 c.Urls.UploadUrl, 156 "Missing", 157 ), 158 ) 159 } 160 return nil 161 }