Watch cookie file, so we can refresh cookies with limited expiry
times.
Change-Id: I2d692e7552d53c390a1ba6126cc8dd30c5c959f3
diff --git a/cmd/slothfs-expand-manifest/main.go b/cmd/slothfs-expand-manifest/main.go
index 294c43c..642798d 100644
--- a/cmd/slothfs-expand-manifest/main.go
+++ b/cmd/slothfs-expand-manifest/main.go
@@ -20,7 +20,6 @@
"log"
"os"
- "github.com/google/slothfs/cookie"
"github.com/google/slothfs/gitiles"
"github.com/google/slothfs/manifest"
@@ -42,16 +41,11 @@
opts := gitiles.Options{
UserAgent: *agent,
}
- if *cookieJarPath != "" {
- var err error
- opts.CookieJar, err = cookie.NewJar(*cookieJarPath)
- if err != nil {
- log.Fatal(err)
- }
+
+ if err := opts.LoadCookieJar(*cookieJarPath); err != nil {
+ log.Fatalf("LoadCookieJar(%s): %v", *cookieJarPath, err)
}
- // SustainedQPS is a little high, but since this is a one-shot
- // program let's try to get away with it.
service, err := gitiles.NewService(*gitilesURL, opts)
if err != nil {
log.Fatalf("NewService: %v", err)
diff --git a/cmd/slothfs-gitiles-test/main.go b/cmd/slothfs-gitiles-test/main.go
index 092acb8..4262998 100644
--- a/cmd/slothfs-gitiles-test/main.go
+++ b/cmd/slothfs-gitiles-test/main.go
@@ -25,7 +25,6 @@
"os"
"sync"
- "github.com/google/slothfs/cookie"
"github.com/google/slothfs/gitiles"
)
@@ -46,12 +45,8 @@
opts := gitiles.Options{
UserAgent: *agent,
}
- if *cookieJarPath != "" {
- var err error
- opts.CookieJar, err = cookie.NewJar(*cookieJarPath)
- if err != nil {
- log.Fatal(err)
- }
+ if err := opts.LoadCookieJar(*cookieJarPath); err != nil {
+ log.Fatalf("LoadCookieJar(%s): %v", *cookieJarPath, err)
}
service, err := gitiles.NewService(*gitilesURL, opts)
diff --git a/cmd/slothfs-multifs/main.go b/cmd/slothfs-multifs/main.go
index 1c25fcb..03e9214 100644
--- a/cmd/slothfs-multifs/main.go
+++ b/cmd/slothfs-multifs/main.go
@@ -23,7 +23,6 @@
"time"
"github.com/google/slothfs/cache"
- "github.com/google/slothfs/cookie"
"github.com/google/slothfs/fs"
"github.com/google/slothfs/gitiles"
"github.com/hanwen/go-fuse/fuse/nodefs"
@@ -59,12 +58,8 @@
gitilesOpts := gitiles.Options{
UserAgent: *agent,
}
- if *cookieJarPath != "" {
- var err error
- gitilesOpts.CookieJar, err = cookie.NewJar(*cookieJarPath)
- if err != nil {
- log.Fatal(err)
- }
+ if err := gitilesOpts.LoadCookieJar(*cookieJarPath); err != nil {
+ log.Fatalf("LoadCookieJar(%s): %v", *cookieJarPath, err)
}
service, err := gitiles.NewService(*gitilesURL, gitilesOpts)
diff --git a/cookie/cookie.go b/cookie/cookie.go
index d5c8e5c..f25e884 100644
--- a/cookie/cookie.go
+++ b/cookie/cookie.go
@@ -19,13 +19,17 @@
"bufio"
"fmt"
"io"
+ "log"
"net/http"
"net/http/cookiejar"
"net/url"
"os"
+ "path/filepath"
"strconv"
"strings"
"time"
+
+ "github.com/fsnotify/fsnotify"
)
// ParseCookieJar parses a cURL/Mozilla/Netscape cookie jar text file.
@@ -74,21 +78,56 @@
return result, nil
}
-func NewJar(path string) (http.CookieJar, error) {
+// WatchJar starts watching the given path for changes, and loads new
+// data from the file whenever it is available.
+func WatchJar(jar http.CookieJar, path string) error {
+ w, err := fsnotify.NewWatcher()
+ if err != nil {
+ return err
+ }
+
+ // We watch the dir, so we catch creation + rename events too.
+ if err := w.Add(filepath.Dir(path)); err != nil {
+ return err
+ }
+
+ go func() {
+ var lastMod time.Time
+ for {
+ select {
+ case <-w.Events:
+ fi, err := os.Stat(path)
+ if err != nil {
+ log.Printf("Stat(%s): %v", path, err)
+ continue
+ }
+ if fi.ModTime().Equal(lastMod) {
+ continue
+ }
+ lastMod = fi.ModTime()
+ if err := updateJar(jar, path); err != nil {
+ log.Printf("updateJar(%s): %v", path, err)
+ }
+
+ case <-w.Errors:
+ log.Printf("notify: %v", path, err)
+ }
+ }
+ }()
+ return nil
+}
+
+// updateJar reads the path into the jar.
+func updateJar(jar http.CookieJar, path string) error {
f, err := os.Open(path)
if err != nil {
- return nil, err
+ return err
}
defer f.Close()
cs, err := ParseCookieJar(f)
if err != nil {
- return nil, err
- }
-
- jar, err := cookiejar.New(nil)
- if err != nil {
- return nil, err
+ return err
}
for _, c := range cs {
@@ -98,5 +137,20 @@
}, []*http.Cookie{c})
}
+ return nil
+}
+
+// NewJar reads cookies in the Mozilla/Netscape cookie file format,
+// and returns them as a CookieJar
+func NewJar(path string) (http.CookieJar, error) {
+ jar, err := cookiejar.New(nil)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := updateJar(jar, path); err != nil {
+ return nil, err
+ }
+
return jar, nil
}
diff --git a/gitiles/client.go b/gitiles/client.go
index 2cf859f..5da2ea4 100644
--- a/gitiles/client.go
+++ b/gitiles/client.go
@@ -26,6 +26,7 @@
"net/url"
"path"
+ "github.com/google/slothfs/cookie"
"golang.org/x/net/context"
"golang.org/x/time/rate"
)
@@ -55,6 +56,23 @@
UserAgent string
}
+func (o *Options) LoadCookieJar(nm string) error {
+ if nm == "" {
+ return nil
+ }
+
+ jar, err := cookie.NewJar(nm)
+ if err != nil {
+ return err
+ }
+ if err := cookie.WatchJar(jar, nm); err != nil {
+ return err
+ }
+
+ o.CookieJar = jar
+ return nil
+}
+
// NewService returns a new Gitiles JSON client.
func NewService(addr string, opts Options) (*Service, error) {
if opts.BurstQPS == 0 {