1
// Copyright 2015 The Go Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
15
filelock "github.com/juju/go4/lock"
19
// Save saves the cookies to the persistent cookie file.
20
// Before the file is written, it reads any cookies that
21
// have been stored from it and merges them into j.
22
func (j *Jar) Save() error {
23
return j.save(time.Now())
26
// save is like Save but takes the current time as a parameter.
27
func (j *Jar) save(now time.Time) error {
28
locked, err := lockFile(lockFileName(j.filename))
30
return errgo.Mask(err)
33
f, err := os.OpenFile(j.filename, os.O_RDWR|os.O_CREATE, 0600)
35
return errgo.Mask(err)
38
// TODO optimization: if the file hasn't changed since we
39
// loaded it, don't bother with the merge step.
43
if err := j.mergeFrom(f); err != nil {
44
// The cookie file is probably corrupt.
45
log.Printf("cannot read cookie file to merge it; ignoring it: %v", err)
48
if err := f.Truncate(0); err != nil {
49
return errgo.Notef(err, "cannot truncate file")
51
if _, err := f.Seek(0, 0); err != nil {
52
return errgo.Mask(err)
57
// load loads the cookies from j.filename. If the file does not exist,
58
// no error will be returned and no cookies will be loaded.
59
func (j *Jar) load() error {
60
locked, err := lockFile(lockFileName(j.filename))
62
return errgo.Mask(err)
65
f, err := os.Open(j.filename)
67
if os.IsNotExist(err) {
73
if err := j.mergeFrom(f); err != nil {
74
return errgo.Mask(err)
79
// mergeFrom reads all the cookies from r and stores them in the Jar.
80
func (j *Jar) mergeFrom(r io.Reader) error {
81
decoder := json.NewDecoder(r)
82
// Cope with old cookiejar format by just discarding
83
// cookies, but still return an error if it's invalid JSON.
84
var data json.RawMessage
85
if err := decoder.Decode(&data); err != nil {
93
if err := json.Unmarshal(data, &entries); err != nil {
94
log.Printf("warning: discarding cookies in invalid format (error: %v)", err)
101
// writeTo writes all the cookies in the jar to w
103
func (j *Jar) writeTo(w io.Writer) error {
104
encoder := json.NewEncoder(w)
105
entries := j.allPersistentEntries()
106
if err := encoder.Encode(entries); err != nil {
112
// allPersistentEntries returns all the entries in the jar, sorted by primarly by canonical host
113
// name and secondarily by path length.
114
func (j *Jar) allPersistentEntries() []entry {
116
for _, submap := range j.entries {
117
for _, e := range submap {
119
entries = append(entries, e)
123
sort.Sort(byCanonicalHost{entries})
127
// lockFileName returns the name of the lock file associated with
129
func lockFileName(path string) string {
130
return path + ".lock"
133
const maxRetryDuration = 1 * time.Second
135
func lockFile(path string) (io.Closer, error) {
136
retry := 100 * time.Microsecond
137
startTime := time.Now()
139
locker, err := filelock.Lock(path)
143
total := time.Since(startTime)
144
if total > maxRetryDuration {
145
return nil, errgo.Notef(err, "file locked for too long; giving up")
147
// Always have at least one try at the end of the interval.
148
if remain := maxRetryDuration - total; retry > remain {