package sendoverhttp import ( "fmt" "io/fs" "log" "net" "net/http" "os" "path/filepath" ) func preferredIP() (*net.IP, error) { ips, err := net.InterfaceAddrs() if err != nil { return nil, err } for _, addr := range ips { ipNet, ok := addr.(*net.IPNet) if !ok { continue } if ipNet.IP.IsLoopback() { continue } return &ipNet.IP, nil } return nil, nil } type Server struct { filepath string closeCh chan struct{} mr *MultiRunner } type SingleFileDir string func (s SingleFileDir) Open(name string) (http.File, error) { if name == "index.html" { return nil, fs.ErrNotExist } fp := string(s) stat, err := os.Stat(fp) if err != nil { return nil, err } if stat.IsDir() { return http.Dir(fp).Open(name) } switch name { case "/": return nil, os.ErrNotExist case "/" + filepath.Base(fp): return os.Open(fp) default: return nil, os.ErrNotExist } } func NewServer(fp string) *Server { return &Server{ closeCh: make(chan struct{}), filepath: fp, mr: NewMultiRunner(), } } func (s *Server) Start() error { ip, err := preferredIP() if err != nil { return err } prot := "http" port := "8081" url := fmt.Sprintf("%s://%s:%s", prot, ip, port) if stat, _ := os.Stat(s.filepath); !stat.IsDir() { url += "/" + filepath.Base(s.filepath) } qrCodeShower := NewTerminalQRShower(url) go s.mr.Run(qrCodeShower) httpRunner := NewRunner() httpSrv := &http.Server{ Addr: ":" + port, } var d http.FileSystem = http.Dir(s.filepath) if stat, _ := os.Stat(s.filepath); !stat.IsDir() { // Show user the name of the file d = SingleFileDir(s.filepath) } httpSrv.Handler = http.FileServer(d) httpRunner.OnStart = func() error { l, err := net.Listen("tcp4", ip.String()+":"+port) if err != nil { log.Fatal(err) } return httpSrv.Serve(l) } httpRunner.OnStop = func() { _ = httpSrv.Close() } go s.mr.Run(httpRunner) <-s.closeCh close(s.closeCh) return nil } func (s *Server) Stop() { s.mr.Stop() return }