4
\$\begingroup\$

I just re-wrote a library to easily retrieve the proxy settings of Internet Explorer (self answered SO question: https://stackoverflow.com/q/41764614/3207406).

Git repository

GoDoc (with an example)

package ieproxy
import (
 "os"
 "strings"
 "sync"
 "golang.org/x/sys/windows/registry"
)
// StaticProxyConf containes the Windows configuration for static proxy
type StaticProxyConf struct {
 // Is the proxy active?
 Active bool
 // Proxy address for each scheme (http, https)
 // "" (empty string) is the fallback proxy
 Protocols map[string]string
 // Addresses not to be browsed via the proxy (comma-separated, like linux)
 NoProxy string
}
// AutomaticProxyConf contains the Windows configuration for automatic proxy
type AutomaticProxyConf struct {
 URL string // url of the .pac file
}
// WindowsProxyConf gathers the Windows configuration for proxy
type WindowsProxyConf struct {
 Static StaticProxyConf // static configuration
 Automatic AutomaticProxyConf // automatic configuration
}
type regeditValues struct {
 ProxyServer string
 ProxyOverride string
 ProxyEnable uint64
 AutoConfigURL string
}
var once sync.Once
var windowsProxyConf WindowsProxyConf
// GetConf retrieves the proxy configuration from the Windows Regedit
func getConf() WindowsProxyConf {
 once.Do(parseRegedit)
 return windowsProxyConf
}
// OverrideEnvWithStaticProxy writes new values to the
// http_proxy, https_proxy and no_proxy environment variables.
// The values are taken from the Windows Regedit (should be called in init() function)
func overrideEnvWithStaticProxy() {
 conf := getConf()
 if conf.Static.Active {
 for _, scheme := range []string{"http", "https"} {
 url, ok := conf.Static.Protocols[scheme]
 if !ok {
 url, ok = conf.Static.Protocols[""] // fallback conf
 }
 if ok {
 os.Setenv(scheme+"_proxy", url)
 }
 }
 if conf.Static.NoProxy != "" {
 os.Setenv("no_proxy", conf.Static.NoProxy)
 }
 }
}
func parseRegedit() {
 regedit, _ := readRegedit()
 protocol := make(map[string]string)
 for _, s := range strings.Split(regedit.ProxyServer, ";") {
 if s == "" {
 continue
 }
 pair := strings.SplitN(s, "=", 2)
 if len(pair) > 1 {
 protocol[pair[0]] = pair[1]
 } else {
 protocol[""] = pair[0]
 }
 }
 windowsProxyConf.Static = StaticProxyConf{
 Active: regedit.ProxyEnable > 0,
 Protocols: protocol,
 NoProxy: strings.Replace(regedit.ProxyOverride, ";", ",", -1), // to match linux style
 }
 windowsProxyConf.Automatic = AutomaticProxyConf{
 URL: regedit.AutoConfigURL,
 }
}
func readRegedit() (values regeditValues, err error) {
 k, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.QUERY_VALUE)
 if err != nil {
 return
 }
 defer k.Close()
 values.ProxyServer, _, err = k.GetStringValue("ProxyServer")
 if err != nil && err != registry.ErrNotExist {
 return
 }
 values.ProxyOverride, _, err = k.GetStringValue("ProxyOverride")
 if err != nil && err != registry.ErrNotExist {
 return
 }
 values.ProxyEnable, _, err = k.GetIntegerValue("ProxyEnable")
 if err != nil && err != registry.ErrNotExist {
 return
 }
 values.AutoConfigURL, _, err = k.GetStringValue("AutoConfigURL")
 if err != nil && err != registry.ErrNotExist {
 return
 }
 err = nil
 return
}

I'm interested to know if the "go-style" can be improved and any other suggestions!

200_success
146k22 gold badges190 silver badges479 bronze badges
asked Jan 20, 2017 at 13:26
\$\endgroup\$
4
  • \$\begingroup\$ cough-cough .... does this work? getConf vs GetConf .... hmmm \$\endgroup\$ Commented Jan 20, 2017 at 13:34
  • 1
    \$\begingroup\$ @rolfl: actually yes, because GetConf calls getConf (for platform independency), but you are right that it isn't nice to read! \$\endgroup\$ Commented Jan 20, 2017 at 13:37
  • 1
    \$\begingroup\$ GetConf is/was not defined in the code anywhere... so now it works after your edit, but I see that I had to go to the git repository to find that other file with the GetConf declaration. \$\endgroup\$ Commented Jan 20, 2017 at 13:45
  • \$\begingroup\$ Yes, sorry about that. I also included the struct definitions in my question (which are in the platform independent file as well) \$\endgroup\$ Commented Jan 20, 2017 at 13:48

2 Answers 2

2
\$\begingroup\$

As is, the (my) code is really hard to test (needs actual registry modifications or env variable edition).

To ease the testing, one could change some functions:

func overrideEnvWithStaticProxy(conf ProxyConf, setenv envSetter) {
 ...
}
type envSetter func(string, string) error
func parseRegedit(regedit regeditValues) ProxyConf {
 ...
}

With this change overrideEnvWithStaticProxy and parseRegedit become self contained and can be easily tested!

This incurs a minor rewrite of getConf (which does only some plumbing):

func getConf() ProxyConf {
 once.Do(writeConf)
 return windowsProxyConf
}
func writeConf() {
 regedit, _ := readRegedit()
 windowsProxyConf = parseRegedit(regedit)
}
answered Jan 23, 2017 at 15:07
\$\endgroup\$
1
\$\begingroup\$

The field StaticProxyConf.NoProxy should not be a single "comma-separated string", but rather a string slice. That makes the code easier that uses this field.

In readRegedit, the err = nil is not necessary.

answered Aug 23, 2019 at 5:33
\$\endgroup\$
1
  • \$\begingroup\$ If StaticProxyConf.NoProxy becomes a slice, then it has to be joined when the no_proxyenv variable is set. Without err = nil, if the last error is registry.ErrNotExist, then this error will be returned (instead of nil). A better approach would be to return nil at the end (and remove all unreadable naked returns) \$\endgroup\$ Commented Aug 25, 2019 at 19:40

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.