作者:kamolja
项目:camlistor
func parseConfig() {
if android.OnAndroid() {
return
}
configPath := osutil.UserClientConfigPath()
if _, err := os.Stat(configPath); os.IsNotExist(err) {
errMsg := fmt.Sprintf("Client configuration file %v does not exist. See 'camput init' to generate it.", configPath)
if keyId := serverKeyId(); keyId != "" {
hint := fmt.Sprintf("\nThe key id %v was found in the server config %v, so you might want:\n'camput init -gpgkey %v'", keyId, osutil.UserServerConfigPath(), keyId)
errMsg += hint
}
log.Fatal(errMsg)
}
conf, err := jsonconfig.ReadFile(configPath)
if err != nil {
log.Fatal(err.Error())
}
cfg := jsonconfig.Obj(conf)
config = &clientConfig{
auth: cfg.OptionalString("auth", ""),
server: cfg.OptionalString("server", ""),
identity: cfg.OptionalString("identity", ""),
identitySecretRing: cfg.OptionalString("identitySecretRing", osutil.IdentitySecretRing()),
trustedCerts: cfg.OptionalList("trustedCerts"),
ignoredFiles: cfg.OptionalList("ignoredFiles"),
}
if err := cfg.Validate(); err != nil {
printConfigChangeHelp(cfg)
log.Fatalf("Error in config file: %v", err)
}
}
作者:louisyo
项目:camlistor
func AddFlags() {
defaultPath := "/x/y/z/we're/in-a-test"
if !buildinfo.TestingLinked() {
defaultPath = osutil.UserClientConfigPath()
}
flag.StringVar(&flagServer, "server", "", "Camlistore server prefix. If blank, the default from the \"server\" field of "+defaultPath+" is used. Acceptable forms: https://you.example.com, example.com:1345 (https assumed), or http://you.example.com/alt-root")
osutil.AddSecretRingFlag()
}
作者:jayvansanto
项目:camlistor
func getSignerPublicKeyBlobref() (signerRef blob.Ref, armored string, ok bool) {
configOnce.Do(parseConfig)
key := "keyId"
keyId, ok := config[key].(string)
if !ok {
log.Printf("No key %q in JSON configuration file %q; have you run \"camput init\"?", key, osutil.UserClientConfigPath())
return
}
keyRing, hasKeyRing := config["secretRing"].(string)
if !hasKeyRing {
if fn := osutil.IdentitySecretRing(); fileExists(fn) {
keyRing = fn
} else if fn := jsonsign.DefaultSecRingPath(); fileExists(fn) {
keyRing = fn
} else {
log.Printf("Couldn't find keyId %q; no 'secretRing' specified in config file, and no standard secret ring files exist.")
return
}
}
entity, err := jsonsign.EntityFromSecring(keyId, keyRing)
if err != nil {
log.Printf("Couldn't find keyId %q in secret ring: %v", keyId, err)
return
}
armored, err = jsonsign.ArmoredPublicKey(entity)
if err != nil {
log.Printf("Error serializing public key: %v", err)
return
}
// TODO(mpl): integrate with getSelfPubKeyDir if possible.
selfPubKeyDir, ok := config["selfPubKeyDir"].(string)
if !ok {
selfPubKeyDir = osutil.KeyBlobsDir()
log.Printf("No 'selfPubKeyDir' defined in %q, defaulting to %v", osutil.UserClientConfigPath(), selfPubKeyDir)
}
fi, err := os.Stat(selfPubKeyDir)
if err != nil || !fi.IsDir() {
log.Printf("selfPubKeyDir of %q doesn't exist or not a directory", selfPubKeyDir)
return
}
br := blob.SHA1FromString(armored)
pubFile := filepath.Join(selfPubKeyDir, br.String()+".camli")
fi, err = os.Stat(pubFile)
if err != nil {
err = ioutil.WriteFile(pubFile, []byte(armored), 0644)
if err != nil {
log.Printf("Error writing public key to %q: %v", pubFile, err)
return
}
}
return br, armored, true
}
作者:kamolja
项目:camlistor
func serverOrDie() string {
if flagServer != "" {
return cleanServer(flagServer)
}
configOnce.Do(parseConfig)
server := cleanServer(config.server)
if server == "" {
log.Fatalf("Missing or invalid \"server\" in %q", osutil.UserClientConfigPath())
}
return server
}
作者:jayvansanto
项目:camlistor
func serverOrDie() string {
if flagServer != "" {
return cleanServer(flagServer)
}
configOnce.Do(parseConfig)
value, ok := config["server"]
var server string
if ok {
server = value.(string)
}
server = cleanServer(server)
if !ok || server == "" {
log.Fatalf("Missing or invalid \"server\" in %q", osutil.UserClientConfigPath())
}
return server
}
作者:stunt
项目:camlistor
func parseConfig() {
configPath := osutil.UserClientConfigPath()
if _, err := os.Stat(configPath); os.IsNotExist(err) {
errMsg := fmt.Sprintf("Client configuration file %v does not exist. See 'camput init' to generate it.", configPath)
if keyId := serverKeyId(); keyId != "" {
hint := fmt.Sprintf("\nThe key id %v was found in the server config %v, so you might want:\n'camput init -gpgkey %v'", keyId, osutil.UserServerConfigPath(), keyId)
errMsg += hint
}
log.Fatal(errMsg)
}
var err error
if config, err = jsonconfig.ReadFile(configPath); err != nil {
log.Fatal(err.Error())
return
}
}
作者:rfistma
项目:camlistor
func (c *initCmd) writeConfig(cc *clientconfig.Config) error {
configFilePath := osutil.UserClientConfigPath()
if _, err := os.Stat(configFilePath); err == nil {
return fmt.Errorf("Config file %q already exists; quitting without touching it.", configFilePath)
}
if err := os.MkdirAll(filepath.Dir(configFilePath), 0700); err != nil {
return err
}
jsonBytes, err := json.MarshalIndent(cc, "", " ")
if err != nil {
log.Fatalf("JSON serialization error: %v", err)
}
if err := ioutil.WriteFile(configFilePath, jsonBytes, 0600); err != nil {
return fmt.Errorf("could not write client config file %v: %v", configFilePath, err)
}
log.Printf("Wrote %q; modify as necessary.", configFilePath)
return nil
}
作者:rn2d
项目:camlistor
// serverOrDie returns the server's URL found either as a command-line flag,
// or as the default server in the config file.
func serverOrDie() string {
if s := os.Getenv("CAMLI_SERVER"); s != "" {
return cleanServer(s)
}
if flagServer != "" {
if !isURLOrHostPort(flagServer) {
configOnce.Do(parseConfig)
serverConf, ok := config.Servers[flagServer]
if ok {
return serverConf.Server
}
log.Printf("%q looks like a server alias, but no such alias found in config.", flagServer)
} else {
return cleanServer(flagServer)
}
}
server := defaultServer()
if server == "" {
log.Fatalf("No valid server defined with CAMLI_SERVER, or with -server, or in %q", osutil.UserClientConfigPath())
}
return cleanServer(server)
}
作者:kamolja
项目:camlistor
func AddFlags() {
defaultPath := osutil.UserClientConfigPath()
flag.StringVar(&flagServer, "server", "", "Camlistore server prefix. If blank, the default from the \"server\" field of "+defaultPath+" is used. Acceptable forms: https://you.example.com, example.com:1345 (https assumed), or http://you.example.com/alt-root")
flag.StringVar(&flagSecretRing, "secret-keyring", "", "GnuPG secret keyring file to use.")
}
作者:kamolja
项目:camlistor
func (c *Client) initSignerPublicKeyBlobref() {
configOnce.Do(parseConfig)
keyId := config.identity
if keyId == "" {
log.Fatalf("No 'identity' key in JSON configuration file %q; have you run \"camput init\"?", osutil.UserClientConfigPath())
}
keyRing := c.SecretRingFile()
if !fileExists(keyRing) {
log.Fatalf("Could not find keyId %q, because secret ring file %q does not exist.", keyId, keyRing)
}
entity, err := jsonsign.EntityFromSecring(keyId, keyRing)
if err != nil {
log.Fatalf("Couldn't find keyId %q in secret ring %v: %v", keyId, keyRing, err)
}
armored, err := jsonsign.ArmoredPublicKey(entity)
if err != nil {
log.Fatalf("Error serializing public key: %v", err)
}
// TODO(mpl): completely get rid of it if possible
// http://camlistore.org/issue/377
selfPubKeyDir := osutil.KeyBlobsDir()
fi, err := os.Stat(selfPubKeyDir)
if err != nil || !fi.IsDir() {
log.Fatalf("selfPubKeyDir as %q doesn't exist or not a directory", selfPubKeyDir)
}
br := blob.SHA1FromString(armored)
pubFile := filepath.Join(selfPubKeyDir, br.String()+".camli")
fi, err = os.Stat(pubFile)
if err != nil {
if !os.IsNotExist(err) {
log.Fatalf("Could not stat %q: %v", pubFile, err)
}
err = ioutil.WriteFile(pubFile, []byte(armored), 0644)
if err != nil {
log.Fatalf("Error writing public key to %q: %v", pubFile, err)
}
}
c.signerPublicKeyRef = br
c.publicKeyArmored = armored
}
作者:rn2d
项目:camlistor
func (c *initCmd) RunCommand(args []string) error {
if len(args) > 0 {
return cmdmain.ErrUsage
}
if c.newKey && c.gpgkey != "" {
log.Fatal("--newkey and --gpgkey are mutually exclusive")
}
blobDir := osutil.KeyBlobsDir()
if err := os.MkdirAll(blobDir, 0700); err != nil {
return err
}
var keyId string
var err error
secRing := osutil.IdentitySecretRing()
if c.newKey {
keyId, err = jsonsign.GenerateNewSecRing(secRing)
if err != nil {
return err
}
} else {
keyId, err = c.keyId(secRing)
if err != nil {
return err
}
}
pubArmor, err := c.getPublicKeyArmored(keyId)
if err != nil {
return err
}
bref := blob.SHA1FromString(string(pubArmor))
keyBlobPath := path.Join(blobDir, bref.String()+".camli")
if err = ioutil.WriteFile(keyBlobPath, pubArmor, 0644); err != nil {
log.Fatalf("Error writing public key blob to %q: %v", keyBlobPath, err)
}
if ok, err := jsonsign.VerifyPublicKeyFile(keyBlobPath, keyId); !ok {
log.Fatalf("Error verifying public key at %q: %v", keyBlobPath, err)
}
log.Printf("Your Camlistore identity (your GPG public key's blobref) is: %s", bref.String())
if c.noconfig {
return nil
}
configFilePath := osutil.UserClientConfigPath()
_, err = os.Stat(configFilePath)
if err == nil {
log.Fatalf("Config file %q already exists; quitting without touching it.", configFilePath)
}
if f, err := os.OpenFile(configFilePath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600); err == nil {
defer f.Close()
m := &clientconfig.Config{
Servers: map[string]*clientconfig.Server{
"localhost": {
Server: "http://localhost:3179",
IsDefault: true,
Auth: "localhost",
},
},
Identity: keyId,
IgnoredFiles: []string{".DS_Store"},
}
jsonBytes, err := json.MarshalIndent(m, "", " ")
if err != nil {
log.Fatalf("JSON serialization error: %v", err)
}
_, err = f.Write(jsonBytes)
if err != nil {
log.Fatalf("Error writing to %q: %v", configFilePath, err)
}
log.Printf("Wrote %q; modify as necessary.", configFilePath)
}
return nil
}
作者:rfistma
项目:camlistor
// New returns a new Camlistore Client.
// The provided server is either "host:port" (assumed http, not https) or a URL prefix, with or without a path, or a server alias from the client configuration file. A server alias should not be confused with a hostname, therefore it cannot contain any colon or period.
// Errors are not returned until subsequent operations.
func New(server string, opts ...ClientOption) *Client {
if !isURLOrHostPort(server) {
configOnce.Do(parseConfig)
serverConf, ok := config.Servers[server]
if !ok {
log.Fatalf("%q looks like a server alias, but no such alias found in config at %v", server, osutil.UserClientConfigPath())
}
server = serverConf.Server
}
return newClient(server, auth.None{}, opts...)
}
作者:rn2d
项目:camlistor
// New returns a new Camlistore Client.
// The provided server is either "host:port" (assumed http, not https) or a URL prefix, with or without a path, or a server alias from the client configuration file. A server alias should not be confused with a hostname, therefore it cannot contain any colon or period.
// Errors are not returned until subsequent operations.
func New(server string) *Client {
if !isURLOrHostPort(server) {
configOnce.Do(parseConfig)
serverConf, ok := config.Servers[server]
if !ok {
log.Fatalf("%q looks like a server alias, but no such alias found in config at %v", server, osutil.UserClientConfigPath())
}
server = serverConf.Server
}
return &Client{
server: server,
httpClient: http.DefaultClient,
reqGate: make(chan bool, maxParallelHTTP),
haveCache: noHaveCache{},
log: log.New(os.Stderr, "", log.Ldate|log.Ltime),
authMode: auth.None{},
}
}
作者:kristofe
项目:camlistor
func (c *initCmd) RunCommand(args []string) error {
if len(args) > 0 {
return cmdmain.ErrUsage
}
var err error
if c.dumpJSON {
type jsonConfig struct {
Identity_secring *jsonsign.IdentitySecring
Client_config *clientconfig.Config
Server_config *serverconfig.Config
}
var config jsonConfig
// generate a new secring struct
config.Identity_secring, err = jsonsign.GenerateNewSecRingStruct()
if err != nil {
return err
}
c.keyId = config.Identity_secring.KeyId
// generate a new server config struct
config.Server_config = GenerateServerConfig(c.keyId)
// generate a new client config struct
config.Client_config = GenerateClientConfig(c.keyId)
jsonBytes, err := json.MarshalIndent(config, "", " ")
if err != nil {
log.Fatalf("JSON serialization error: %v", err)
}
//log.Printf("%+#v\n", string(jsonBytes))
_, err = os.Stdout.Write(jsonBytes)
return err
}
if c.newKey && c.keyId != "" {
log.Fatal("--newkey and --gpgkey are mutually exclusive")
}
if c.newKey {
c.secretRing = osutil.DefaultSecretRingFile()
c.keyId, err = jsonsign.GenerateNewSecRing(c.secretRing)
if err != nil {
return err
}
} else {
if err := c.initSecretRing(); err != nil {
return err
}
if err := c.initKeyId(); err != nil {
return err
}
}
pubArmor, err := c.getPublicKeyArmored()
if err != nil {
return err
}
bref := blob.SHA1FromString(string(pubArmor))
log.Printf("Your Camlistore identity (your GPG public key's blobref) is: %s", bref.String())
if c.noconfig {
return nil
}
configFilePath := osutil.UserClientConfigPath()
_, err = os.Stat(configFilePath)
if err == nil {
log.Fatalf("Config file %q already exists; quitting without touching it.", configFilePath)
}
if err := os.MkdirAll(filepath.Dir(configFilePath), 0700); err != nil {
return err
}
if f, err := os.OpenFile(configFilePath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600); err == nil {
defer f.Close()
// refactored to a service routine
m := GenerateClientConfig(c.keyId)
jsonBytes, err := json.MarshalIndent(m, "", " ")
if err != nil {
log.Fatalf("JSON serialization error: %v", err)
}
_, err = f.Write(jsonBytes)
if err != nil {
log.Fatalf("Error writing to %q: %v", configFilePath, err)
}
log.Printf("Wrote %q; modify as necessary.", configFilePath)
} else {
return fmt.Errorf("could not write client config file %v: %v", configFilePath, err)
}
return nil
}
作者:rfistma
项目:camlistor
package camtypes
import (
"fmt"
"log"
"camlistore.org/pkg/osutil"
)
// TODO(mpl): move pkg/camerrors stuff in here
var camErrors = map[string]*camErr{}
var (
ErrClientNoServer = addCamError("client-no-server", funcStr(func() string {
return fmt.Sprintf("No valid server defined. It can be set with the CAMLI_SERVER environment variable, or the --server flag, or in the \"servers\" section of %q (see https://camlistore.org/docs/client-config).", osutil.UserClientConfigPath())
}))
ErrClientNoPublicKey = addCamError("client-no-public-key", str("No public key configured: see 'camput init'."))
)
type str string
func (s str) String() string { return string(s) }
type funcStr func() string
func (f funcStr) String() string { return f() }
type camErr struct {
key string
des fmt.Stringer
作者:louisyo
项目:camlistor
// printConfigChangeHelp checks if conf contains obsolete keys,
// and prints additional help in this case.
func printConfigChangeHelp(conf jsonconfig.Obj) {
// rename maps from old key names to the new ones.
// If there is no new one, the value is the empty string.
rename := map[string]string{
"keyId": "identity",
"publicKeyBlobref": "",
"selfPubKeyDir": "",
"secretRing": "identitySecretRing",
}
oldConfig := false
configChangedMsg := fmt.Sprintf("The client configuration file (%s) keys have changed.\n", osutil.UserClientConfigPath())
for _, unknown := range conf.UnknownKeys() {
v, ok := rename[unknown]
if ok {
if v != "" {
configChangedMsg += fmt.Sprintf("%q should be renamed %q.\n", unknown, v)
} else {
configChangedMsg += fmt.Sprintf("%q should be removed.\n", unknown)
}
oldConfig = true
}
}
if oldConfig {
configChangedMsg += "Please see http://camlistore.org/docs/client-config, or use camput init to recreate a default one."
log.Print(configChangedMsg)
}
}
作者:louisyo
项目:camlistor
func (c *Client) initSignerPublicKeyBlobref() {
if c.paramsOnly {
log.Print("client: paramsOnly set; cannot get public key from config or env vars.")
return
}
keyId := os.Getenv("CAMLI_KEYID")
if keyId == "" {
configOnce.Do(parseConfig)
keyId = config.Identity
if keyId == "" {
log.Fatalf("No 'identity' key in JSON configuration file %q; have you run \"camput init\"?", osutil.UserClientConfigPath())
}
}
keyRing := c.SecretRingFile()
if !fileExists(keyRing) {
log.Fatalf("Could not find keyId %q, because secret ring file %q does not exist.", keyId, keyRing)
}
entity, err := jsonsign.EntityFromSecring(keyId, keyRing)
if err != nil {
log.Fatalf("Couldn't find keyId %q in secret ring %v: %v", keyId, keyRing, err)
}
armored, err := jsonsign.ArmoredPublicKey(entity)
if err != nil {
log.Fatalf("Error serializing public key: %v", err)
}
c.signerPublicKeyRef = blob.SHA1FromString(armored)
c.publicKeyArmored = armored
}
作者:stunt
项目:camlistor
func (c *initCmd) RunCommand(args []string) error {
if len(args) > 0 {
return cmdmain.ErrUsage
}
if c.newKey && c.gpgkey != "" {
log.Fatal("--newkey and --gpgkey are mutually exclusive")
}
blobDir := path.Join(osutil.CamliConfigDir(), "keyblobs")
os.Mkdir(osutil.CamliConfigDir(), 0700)
os.Mkdir(blobDir, 0700)
var keyId string
var err error
secRing := osutil.IdentitySecretRing()
if c.newKey {
keyId, err = jsonsign.GenerateNewSecRing(secRing)
if err != nil {
return err
}
} else {
keyId, err = c.keyId(secRing)
if err != nil {
return err
}
}
if os.Getenv("GPG_AGENT_INFO") == "" {
log.Printf("No GPG_AGENT_INFO found in environment; you should setup gnupg-agent. camput might be annoying otherwise, if your private key is encrypted.")
}
pubArmor, err := c.getPublicKeyArmored(keyId)
if err != nil {
return err
}
bref := blobref.SHA1FromString(string(pubArmor))
keyBlobPath := path.Join(blobDir, bref.String()+".camli")
if err = ioutil.WriteFile(keyBlobPath, pubArmor, 0644); err != nil {
log.Fatalf("Error writing public key blob to %q: %v", keyBlobPath, err)
}
if ok, err := jsonsign.VerifyPublicKeyFile(keyBlobPath, keyId); !ok {
log.Fatalf("Error verifying public key at %q: %v", keyBlobPath, err)
}
log.Printf("Your Camlistore identity (your GPG public key's blobref) is: %s", bref.String())
configFilePath := osutil.UserClientConfigPath()
_, err = os.Stat(configFilePath)
if err == nil {
log.Fatalf("Config file %q already exists; quitting without touching it.", configFilePath)
}
if f, err := os.OpenFile(configFilePath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600); err == nil {
defer f.Close()
m := make(map[string]interface{})
m["keyId"] = keyId // TODO(bradfitz): make this 'identity' to match server config?
m["publicKeyBlobref"] = bref.String() // TODO(bradfitz): not used anymore?
m["server"] = "http://localhost:3179/"
m["selfPubKeyDir"] = blobDir
m["auth"] = "localhost"
jsonBytes, err := json.MarshalIndent(m, "", " ")
if err != nil {
log.Fatalf("JSON serialization error: %v", err)
}
_, err = f.Write(jsonBytes)
if err != nil {
log.Fatalf("Error writing to %q: %v", configFilePath, err)
}
log.Printf("Wrote %q; modify as necessary.", configFilePath)
}
return nil
}
作者:louisyo
项目:camlistor
// lazy config parsing when there's a known client already.
// The client c may be nil.
func (c *Client) parseConfig() {
if android.OnAndroid() {
panic("parseConfig should never have been called on Android")
}
if configDisabled {
panic("parseConfig should never have been called with CAMLI_DISABLE_CLIENT_CONFIG_FILE set")
}
configPath := osutil.UserClientConfigPath()
if _, err := wkfs.Stat(configPath); os.IsNotExist(err) {
if c != nil && c.isSharePrefix {
return
}
errMsg := fmt.Sprintf("Client configuration file %v does not exist. See 'camput init' to generate it.", configPath)
if keyId := serverKeyId(); keyId != "" {
hint := fmt.Sprintf("\nThe key id %v was found in the server config %v, so you might want:\n'camput init -gpgkey %v'", keyId, osutil.UserServerConfigPath(), keyId)
errMsg += hint
}
log.Fatal(errMsg)
}
// TODO: instead of using jsonconfig, we could read the file, and unmarshall into the structs that we now have in pkg/types/clientconfig. But we'll have to add the old fields (before the name changes, and before the multi-servers change) to the structs as well for our gracefull conversion/error messages to work.
conf, err := osutil.NewJSONConfigParser().ReadFile(configPath)
if err != nil {
log.Fatal(err.Error())
}
cfg := jsonconfig.Obj(conf)
if singleServerAuth := cfg.OptionalString("auth", ""); singleServerAuth != "" {
newConf, err := convertToMultiServers(cfg)
if err != nil {
log.Print(err)
} else {
cfg = newConf
}
}
config = &clientconfig.Config{
Identity: cfg.OptionalString("identity", ""),
IdentitySecretRing: cfg.OptionalString("identitySecretRing", ""),
IgnoredFiles: cfg.OptionalList("ignoredFiles"),
}
serversList := make(map[string]*clientconfig.Server)
servers := cfg.OptionalObject("servers")
for alias, vei := range servers {
// An alias should never be confused with a host name,
// so we forbid anything looking like one.
if isURLOrHostPort(alias) {
log.Fatalf("Server alias %q looks like a hostname; \".\" or \";\" are not allowed.", alias)
}
serverMap, ok := vei.(map[string]interface{})
if !ok {
log.Fatalf("entry %q in servers section is a %T, want an object", alias, vei)
}
serverConf := jsonconfig.Obj(serverMap)
server := &clientconfig.Server{
Server: cleanServer(serverConf.OptionalString("server", "")),
Auth: serverConf.OptionalString("auth", ""),
IsDefault: serverConf.OptionalBool("default", false),
TrustedCerts: serverConf.OptionalList("trustedCerts"),
}
if err := serverConf.Validate(); err != nil {
log.Fatalf("Error in servers section of config file for server %q: %v", alias, err)
}
serversList[alias] = server
}
config.Servers = serversList
if err := cfg.Validate(); err != nil {
printConfigChangeHelp(cfg)
log.Fatalf("Error in config file: %v", err)
}
}
作者:pg
项目:camlistor
// New returns a new Camlistore Client.
// The provided server is either "host:port" (assumed http, not https) or a URL prefix, with or without a path, or a server alias from the client configuration file. A server alias should not be confused with a hostname, therefore it cannot contain any colon or period.
// Errors are not returned until subsequent operations.
func New(server string) *Client {
if !isURLOrHostPort(server) {
configOnce.Do(parseConfig)
serverConf, ok := config.Servers[server]
if !ok {
log.Fatalf("%q looks like a server alias, but no such alias found in config at %v", server, osutil.UserClientConfigPath())
}
server = serverConf.Server
}
httpClient := &http.Client{
Transport: &http.Transport{
MaxIdleConnsPerHost: maxParallelHTTP,
},
}
return &Client{
server: server,
httpClient: httpClient,
httpGate: syncutil.NewGate(maxParallelHTTP),
haveCache: noHaveCache{},
log: log.New(os.Stderr, "", log.Ldate|log.Ltime),
authMode: auth.None{},
}
}