// Copyright 2016 . All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build ignore package main import ( "flag" "log" "net" "net/http" "os/exec" "runtime" "time" ) var ( flagRootDir = flag.String("dir", ".", "root dir") flagHttpAddr = flag.String("http", ":8080", "HTTP service address") flagNoCache = flag.Bool("no-cache", true, "disable browser cache") flagOpenBrowser = flag.Bool("openbrowser", true, "open browser automatically") ) func main() { flag.Parse() host, port, err := net.SplitHostPort(*flagHttpAddr) if err != nil { log.Fatal(err) } if host == "" { host = getLocalIP() } httpAddr := host + ":" + port url := "http://" + httpAddr + "/_book" go func() { log.Printf("dir: %s\n", *flagRootDir) if waitServer(url) && *flagOpenBrowser && startBrowser(url) { log.Printf("A browser window should open. If not, please visit %s", url) } else { log.Printf("Please open your web browser and visit %s", url) } log.Printf("Hit CTRL-C to stop the server\n") }() mainHandler := http.FileServer(http.Dir(*flagRootDir)) if *flagNoCache { mainHandler = NoCache(mainHandler) } log.Fatal(http.ListenAndServe(httpAddr, mainHandler)) } // waitServer waits some time for the http Server to start // serving url. The return value reports whether it starts. func waitServer(url string) bool { tries := 20 for tries > 0 { resp, err := http.Get(url) if err == nil { resp.Body.Close() return true } time.Sleep(100 * time.Millisecond) tries-- } return false } func getLocalIP() string { addrs, err := net.InterfaceAddrs() if err != nil { return "127.0.0.1" } for _, address := range addrs { if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { if ipnet.IP.To4() != nil { return ipnet.IP.String() } } } return "127.0.0.1" } // startBrowser tries to open the URL in a browser, and returns // whether it succeed. func startBrowser(url string) bool { // try to start the browser var args []string switch runtime.GOOS { case "darwin": args = []string{"open"} case "windows": args = []string{"cmd", "/c", "start"} default: args = []string{"xdg-open"} } cmd := exec.Command(args[0], append(args[1:], url)...) return cmd.Start() == nil } var epoch = time.Unix(0, 0).Format(time.RFC1123) var noCacheHeaders = map[string]string{ "Expires": epoch, "Cache-Control": "no-cache, private, max-age=0", "Pragma": "no-cache", "X-Accel-Expires": "0", } var etagHeaders = []string{ "ETag", "If-Modified-Since", "If-Match", "If-None-Match", "If-Range", "If-Unmodified-Since", } func NoCache(h http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { // Delete any ETag headers that may have been set for _, v := range etagHeaders { if r.Header.Get(v) != "" { r.Header.Del(v) } } // Set our NoCache headers for k, v := range noCacheHeaders { w.Header().Set(k, v) } h.ServeHTTP(w, r) } return http.HandlerFunc(fn) }