func (i *Info) ReadFile(filename string) error {
	*i = Info{}
	r, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer r.Close()
	if err := json.NewDecoder(r).Decode(i); err != nil {
		return err
	}
	return initInfo(i)
}
ReadFile loads the configuraiton info from the given file.
package config
import (
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"os"
	"strings"
)
type OAuthInfo struct {
	Provider string `json:"provider"`
	ClientID     string `json:"client-id"`
	ClientSecret string `json:"client-secret"`
	Domain string `json:"domain"`
	BaseURL string `json:"base-url"`
}
type RouteInfo struct {
	From string
	To string
	toURL *url.URL
	AllowedGroups []string `json:"allowed-groups"`
}
func (r *RouteInfo) ToURL() *url.URL {
	return r.toURL
}
type Info struct {
	Host string
	Oauth OAuthInfo
	AddSecurityHeaders bool `json:"use-strict-security-headers"`
	Certs []struct {
		Crt string
		Key string
	}
	Groups map[string][]string
	Routes []*RouteInfo
}
func (i *Info) HasCerts() bool {
	return len(i.Certs) > 0
}
func (i *Info) HasGroups() bool {
	return len(i.Groups) > 0
}
func (i *Info) Scheme() string {
	if len(i.Certs) > 0 {
		return "https"
	}
	return "http"
}
func initRoute(r *RouteInfo) error {
	toURL, err := url.Parse(r.To)
	if err != nil {
		return err
	}
	r.toURL = toURL
	return nil
}
func initInfo(n *Info) error {
	if n.Oauth.BaseURL != "" {
		n.Oauth.BaseURL = strings.TrimRight(n.Oauth.BaseURL, "/")
	}
	if n.Oauth.ClientID == "" {
		return errors.New("oauth.client-id is required")
	}
	if n.Oauth.ClientSecret == "" {
		return errors.New("oauth.client-secret is required")
	}
	for _, route := range n.Routes {
		if err := initRoute(route); err != nil {
			return fmt.Errorf("Route %s has invalid To URL: %s",
				route.From,
				err)
		}
	}
	return nil
}