diff options
author | Slack Coder <slackcoder@server.ky> | 2023-09-14 13:54:49 -0500 |
---|---|---|
committer | Slack Coder <slackcoder@server.ky> | 2023-09-14 15:52:56 -0500 |
commit | 33af09195b60fd7364e2190934717a9345ce6cad (patch) | |
tree | 947017bc407ff86040f0c4c162268089e8c8e6aa | |
parent | e4d26432d4ef366c9e4b969d4fdb8d4728b545b3 (diff) | |
download | send-over-http-33af09195b60fd7364e2190934717a9345ce6cad.tar.xz |
Allow user to set network address
Give the user the ability to set which network address to listen on and
the network type (IP 4 or 6 etc.). Randomly choose an available port to
avoid conflicting with other services listening on a predefined one.
-rw-r--r-- | README.md | 10 | ||||
-rw-r--r-- | cmd/send-over-http/main.go | 21 | ||||
-rw-r--r-- | qr_code.go | 2 | ||||
-rw-r--r-- | send_over_http.go | 103 |
4 files changed, 95 insertions, 41 deletions
@@ -19,10 +19,16 @@ Inspired by the android app [Share Via HTTP](https://github.com/marcosdiez/share ``` send-over-http --help -# Usage of send-over-http [target] +# Usage send-over-http <options> [target] # -# target - file or directory to share (default: .) +# target: file or directory to share (default: .) # +# Options: +# +# -address string +# network address to accept connections (127.0.0.1:0) +# -net string +# network type to listen on (tcp, tcp4, tcp6) (default "tcp") ``` ## Build Requirements diff --git a/cmd/send-over-http/main.go b/cmd/send-over-http/main.go index 3c587ff..4bd9750 100644 --- a/cmd/send-over-http/main.go +++ b/cmd/send-over-http/main.go @@ -17,27 +17,34 @@ func exitOnErr(err error) { } func usage() { - fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s [target]\n", os.Args[0]) + fmt.Fprintf(flag.CommandLine.Output(), "Usage %s <options> [target]\n", os.Args[0]) fmt.Fprintln(flag.CommandLine.Output()) - fmt.Fprintf(flag.CommandLine.Output(), "\ttarget - file or directory to share (default: .)\n") + fmt.Fprintf(flag.CommandLine.Output(), " target: file or directory to share (default: .)\n") + fmt.Fprintln(flag.CommandLine.Output()) + fmt.Fprintln(flag.CommandLine.Output(), "Options:") + fmt.Fprintln(flag.CommandLine.Output()) + flag.PrintDefaults() fmt.Fprintln(flag.CommandLine.Output()) } func main() { + config := sendoverhttp.Config{} + + flag.StringVar(&config.Network, "net", "tcp", "network type to listen on (tcp, tcp4, tcp6)") + flag.StringVar(&config.ListenAddress, "address", "", "network address to accept connections (example: 127.0.0.1:1234)") flag.Usage = usage flag.Parse() - var fp string if len(os.Args) == 2 { - fp = os.Args[1] + config.FilePath = os.Args[1] } - if fp == "" { + if config.FilePath == "" { v, err := os.Getwd() exitOnErr(err) - fp = v + config.FilePath = v } - s := sendoverhttp.NewServer(fp) + s := sendoverhttp.NewServer(config) go s.Start() time.Sleep(250 * time.Millisecond) @@ -25,7 +25,7 @@ func NewTerminalQRShower(str string) *TerminalQRShower { func (s *TerminalQRShower) Start() error { qrterminal.Generate(s.str, qrterminal.L, os.Stdout) fmt.Println() - fmt.Printf("serving on '%s'", s.str) + fmt.Printf("Hosting file at %s", s.str) fmt.Println() fmt.Println() s.waiter.Wait() diff --git a/send_over_http.go b/send_over_http.go index 98b7e6e..2af7890 100644 --- a/send_over_http.go +++ b/send_over_http.go @@ -1,7 +1,7 @@ package sendoverhttp import ( - "fmt" + "errors" "io/fs" "log" "net" @@ -9,32 +9,73 @@ import ( "net/url" "os" "path/filepath" - "strconv" ) -// port is the IP port address to listen on. -const port = 8081 +// Config contains the configuration parameters for the service. +type Config struct { + // FilePath is the file to serve. + FilePath string + // ListenAddress is the network address to listen for connections. + ListenAddress string + // Network type to listen on. + Network string +} + +// Source a single listening address to listen on. +func getListenAddress(config Config) (string, error) { + switch config.Network { + case "tcp", "tcp4", "tcp6": + default: + return config.ListenAddress, nil + } + + addr, port, err := net.SplitHostPort(config.ListenAddress) + if err != nil { + addr = config.ListenAddress + port = "0" + } + + if addr != "" { + ip := net.ParseIP(addr) + if ip == nil { + return config.ListenAddress, nil + } + if !ip.IsUnspecified() { + return config.ListenAddress, nil + } + } -func preferredIP() (*net.IP, error) { ips, err := net.InterfaceAddrs() if err != nil { - return nil, err + return "", err } + + // Valid fallback if no other interfaces available + var loopback *net.IP = nil + for _, addr := range ips { ipNet, ok := addr.(*net.IPNet) if !ok { continue } + if config.Network == "tcp6" && ipNet.IP.To4() != nil { + continue + } if ipNet.IP.IsLoopback() { + loopback = &ipNet.IP continue } - return &ipNet.IP, nil + return net.JoinHostPort(ipNet.IP.String(), port), nil + } + + if loopback != nil { + return net.JoinHostPort((*loopback).String(), port), nil } - return nil, nil + return "", errors.New("no valid network address could be found") } type Server struct { - filepath string + config Config closeCh chan struct{} mr *MultiRunner @@ -67,48 +108,48 @@ func (s SingleFileDir) Open(name string) (http.File, error) { } } -func NewServer(fp string) *Server { +func NewServer(config Config) *Server { return &Server{ - closeCh: make(chan struct{}), - filepath: fp, - mr: NewMultiRunner(), + config: config, + + closeCh: make(chan struct{}), + mr: NewMultiRunner(), } } func (s *Server) Start() error { - ip, err := preferredIP() + listenAddr, err := getListenAddress(s.config) if err != nil { - return err + log.Fatal(err) + } + + l, err := net.Listen(s.config.Network, listenAddr) + if err != nil { + log.Fatal(err) } u := url.URL{ Scheme: "http", - Host: fmt.Sprintf("%s:%d", ip, port), + Host: l.Addr().String(), } - if stat, _ := os.Stat(s.filepath); !stat.IsDir() { - u.Path = filepath.Base(s.filepath) + if stat, _ := os.Stat(s.config.FilePath); !stat.IsDir() { + u.Path = filepath.Base(s.config.FilePath) } qrCodeShower := NewTerminalQRShower(u.String()) go s.mr.Run(qrCodeShower) - httpRunner := NewRunner() - httpSrv := &http.Server{ - Addr: ":" + strconv.Itoa(port), - } - - var d http.FileSystem = http.Dir(s.filepath) - if stat, _ := os.Stat(s.filepath); !stat.IsDir() { + var d http.FileSystem = http.Dir(s.config.FilePath) + if stat, _ := os.Stat(s.config.FilePath); !stat.IsDir() { // Show user the name of the file - d = SingleFileDir(s.filepath) + d = SingleFileDir(s.config.FilePath) + } + httpSrv := &http.Server{ + Handler: http.FileServer(d), } - httpSrv.Handler = http.FileServer(d) + httpRunner := NewRunner() httpRunner.OnStart = func() error { - l, err := net.Listen("tcp4", u.Host) - if err != nil { - log.Fatal(err) - } return httpSrv.Serve(l) } httpRunner.OnStop = func() { |