aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSlack Coder <slackcoder@server.ky>2023-09-14 13:54:49 -0500
committerSlack Coder <slackcoder@server.ky>2023-09-14 15:52:56 -0500
commit33af09195b60fd7364e2190934717a9345ce6cad (patch)
tree947017bc407ff86040f0c4c162268089e8c8e6aa
parente4d26432d4ef366c9e4b969d4fdb8d4728b545b3 (diff)
downloadsend-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.md10
-rw-r--r--cmd/send-over-http/main.go21
-rw-r--r--qr_code.go2
-rw-r--r--send_over_http.go103
4 files changed, 95 insertions, 41 deletions
diff --git a/README.md b/README.md
index e48ac52..0e03ba6 100644
--- a/README.md
+++ b/README.md
@@ -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)
diff --git a/qr_code.go b/qr_code.go
index 13a4aa0..f21d01f 100644
--- a/qr_code.go
+++ b/qr_code.go
@@ -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() {