github.com/Cloud-Foundations/Dominator@v0.3.4/lib/url/urlutil/watchUrlCached.go (about) 1 package urlutil 2 3 import ( 4 "io" 5 "os" 6 "syscall" 7 "time" 8 9 "github.com/Cloud-Foundations/Dominator/lib/log" 10 ) 11 12 const ( 13 privateFilePerms = syscall.S_IRUSR | syscall.S_IWUSR 14 ) 15 16 func handleReadClosers(cachedReadCloserChannel chan<- *CachedReadCloser, 17 readCloserChannel <-chan io.ReadCloser, cacheFilename string, 18 initialTimeout time.Duration, logger log.Logger) { 19 timer := time.NewTimer(initialTimeout) 20 select { 21 case <-timer.C: 22 if file, err := os.Open(cacheFilename); err == nil { 23 cachedReadCloserChannel <- &CachedReadCloser{rawReadCloser: file} 24 } 25 case readCloser := <-readCloserChannel: 26 timer.Stop() 27 cachedReadCloserChannel <- newCachedReadCloser(cacheFilename, 28 readCloser, logger) 29 } 30 for readCloser := range readCloserChannel { 31 cachedReadCloserChannel <- newCachedReadCloser(cacheFilename, 32 readCloser, logger) 33 } 34 } 35 36 func newCachedReadCloser(cacheFilename string, 37 readCloser io.ReadCloser, logger log.Logger) *CachedReadCloser { 38 file, err := os.OpenFile(cacheFilename+"~", os.O_CREATE|os.O_WRONLY, 39 privateFilePerms) 40 if err != nil { 41 logger.Println(err) 42 } 43 rc := &CachedReadCloser{ 44 cache: file, 45 cacheFilename: cacheFilename, 46 rawReadCloser: readCloser, 47 } 48 return rc 49 } 50 51 func (rc *CachedReadCloser) close() error { 52 if rc.cache != nil { 53 rc.cache.Close() 54 os.Remove(rc.cacheFilename + "~") 55 } 56 return rc.rawReadCloser.Close() 57 } 58 59 func (rc *CachedReadCloser) read(p []byte) (int, error) { 60 if rc.cache == nil { 61 return rc.rawReadCloser.Read(p) 62 } 63 if nRead, err := rc.rawReadCloser.Read(p); err != nil && err != io.EOF { 64 rc.cache.Close() 65 rc.cache = nil 66 os.Remove(rc.cacheFilename + "~") 67 return nRead, err 68 } else { 69 if nWritten, err := rc.cache.Write(p[:nRead]); err != nil { 70 rc.cache.Close() 71 rc.cache = nil 72 os.Remove(rc.cacheFilename + "~") 73 } else if nWritten < nRead { 74 rc.cache.Close() 75 rc.cache = nil 76 os.Remove(rc.cacheFilename + "~") 77 } 78 return nRead, err 79 } 80 } 81 82 func (rc *CachedReadCloser) saveCache() error { 83 if rc.cache == nil { 84 return nil 85 } 86 err := rc.cache.Close() 87 rc.cache = nil 88 if err != nil { 89 os.Remove(rc.cacheFilename + "~") 90 return err 91 } 92 return os.Rename(rc.cacheFilename+"~", rc.cacheFilename) 93 } 94 95 func watchUrlWithCache(rawurl string, checkInterval time.Duration, 96 cacheFilename string, initialTimeout time.Duration, 97 logger log.DebugLogger) (<-chan *CachedReadCloser, error) { 98 readCloserChannel, err := watchUrl(rawurl, checkInterval, logger) 99 if err != nil { 100 return nil, err 101 } 102 cachedReadCloserChannel := make(chan *CachedReadCloser, 1) 103 go handleReadClosers(cachedReadCloserChannel, readCloserChannel, 104 cacheFilename, initialTimeout, logger) 105 return cachedReadCloserChannel, nil 106 }