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  }