add new client
This commit is contained in:
188
providers/httpclient/cookiejar/serialize.go
Normal file
188
providers/httpclient/cookiejar/serialize.go
Normal file
@@ -0,0 +1,188 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cookiejar
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/retry.v1"
|
||||
|
||||
filelock "github.com/juju/go4/lock"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Save saves the cookies to the persistent cookie file.
|
||||
// Before the file is written, it reads any cookies that
|
||||
// have been stored from it and merges them into j.
|
||||
func (j *Jar) Save() error {
|
||||
if j.filename == "" {
|
||||
return nil
|
||||
}
|
||||
return j.save(time.Now())
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler by encoding all persistent cookies
|
||||
// currently in the jar.
|
||||
func (j *Jar) MarshalJSON() ([]byte, error) {
|
||||
j.mu.Lock()
|
||||
defer j.mu.Unlock()
|
||||
// Marshaling entries can never fail.
|
||||
data, _ := json.Marshal(j.allPersistentEntries())
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// save is like Save but takes the current time as a parameter.
|
||||
func (j *Jar) save(now time.Time) error {
|
||||
locked, err := lockFile(lockFileName(j.filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer locked.Close()
|
||||
f, err := os.OpenFile(j.filename, os.O_RDWR|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
// TODO optimization: if the file hasn't changed since we
|
||||
// loaded it, don't bother with the merge step.
|
||||
|
||||
j.mu.Lock()
|
||||
defer j.mu.Unlock()
|
||||
if err := j.mergeFrom(f); err != nil {
|
||||
// The cookie file is probably corrupt.
|
||||
log.Printf("cannot read cookie file to merge it; ignoring it: %v", err)
|
||||
}
|
||||
j.deleteExpired(now)
|
||||
if err := f.Truncate(0); err != nil {
|
||||
return errors.Wrap(err, "cannot truncate file")
|
||||
}
|
||||
if _, err := f.Seek(0, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
return j.writeTo(f)
|
||||
}
|
||||
|
||||
// load loads the cookies from j.filename. If the file does not exist,
|
||||
// no error will be returned and no cookies will be loaded.
|
||||
func (j *Jar) load() error {
|
||||
if _, err := os.Stat(filepath.Dir(j.filename)); os.IsNotExist(err) {
|
||||
// The directory that we'll store the cookie jar
|
||||
// in doesn't exist, so don't bother trying
|
||||
// to acquire the lock.
|
||||
return nil
|
||||
}
|
||||
locked, err := lockFile(lockFileName(j.filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer locked.Close()
|
||||
f, err := os.Open(j.filename)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
if err := j.mergeFrom(f); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mergeFrom reads all the cookies from r and stores them in the Jar.
|
||||
func (j *Jar) mergeFrom(r io.Reader) error {
|
||||
decoder := json.NewDecoder(r)
|
||||
// Cope with old cookiejar format by just discarding
|
||||
// cookies, but still return an error if it's invalid JSON.
|
||||
var data json.RawMessage
|
||||
if err := decoder.Decode(&data); err != nil {
|
||||
if err == io.EOF {
|
||||
// Empty file.
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
var entries []entry
|
||||
if err := json.Unmarshal(data, &entries); err != nil {
|
||||
log.Printf("warning: discarding cookies in invalid format (error: %v)", err)
|
||||
return nil
|
||||
}
|
||||
j.merge(entries)
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeTo writes all the cookies in the jar to w
|
||||
// as a JSON array.
|
||||
func (j *Jar) writeTo(w io.Writer) error {
|
||||
encoder := json.NewEncoder(w)
|
||||
entries := j.allPersistentEntries()
|
||||
if err := encoder.Encode(entries); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// allPersistentEntries returns all the entries in the jar, sorted by primarly by canonical host
|
||||
// name and secondarily by path length.
|
||||
func (j *Jar) allPersistentEntries() []entry {
|
||||
var entries []entry
|
||||
for _, submap := range j.entries {
|
||||
for _, e := range submap {
|
||||
if e.Persistent {
|
||||
entries = append(entries, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Sort(byCanonicalHost{entries})
|
||||
return entries
|
||||
}
|
||||
|
||||
func (j *Jar) KVData() map[string]string {
|
||||
pairs := make(map[string]string)
|
||||
|
||||
entries := j.allPersistentEntries()
|
||||
if len(entries) == 0 {
|
||||
return pairs
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
pairs[strings.ToLower(entry.Name)] = entry.Value
|
||||
}
|
||||
|
||||
return pairs
|
||||
}
|
||||
|
||||
// lockFileName returns the name of the lock file associated with
|
||||
// the given path.
|
||||
func lockFileName(path string) string {
|
||||
return path + ".lock"
|
||||
}
|
||||
|
||||
var attempt = retry.LimitTime(3*time.Second, retry.Exponential{
|
||||
Initial: 100 * time.Microsecond,
|
||||
Factor: 1.5,
|
||||
MaxDelay: 100 * time.Millisecond,
|
||||
})
|
||||
|
||||
func lockFile(path string) (io.Closer, error) {
|
||||
for a := retry.Start(attempt, nil); a.Next(); {
|
||||
locker, err := filelock.Lock(path)
|
||||
if err == nil {
|
||||
return locker, nil
|
||||
}
|
||||
if !a.More() {
|
||||
return nil, errors.Wrap(err, "file locked for too long; giving up")
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
Reference in New Issue
Block a user