package main import ( "context" "errors" "flag" "fmt" "log" "net/http" "os" "os/signal" "strconv" "syscall" "time" ) var errorLog *log.Logger = log.New(os.Stderr, "", log.LstdFlags) // const ( // DEFAULT_RULES_FILE string = "/var/lib/gocustomurls/rules.json" // DEFAULT_LOG_FILE string = "/var/log/gocustomurls/app.log" // ) // flagsSet returns a set of all the flags what were actually set on the // command line. func flagsSet(flags *flag.FlagSet) map[string]bool { s := make(map[string]bool) flags.Visit(func(f *flag.Flag) { s[f.Name] = true }) return s } // generateDefaults - generate the default values for the rules.json and log files // func generateDefaults(rulesfp string, logfp string) (string, string, error) { // var newlogfp, newrulesfp string // var err error // newlogfp = logfp // newrulesfp = rulesfp // if len(newrulesfp) == 0 { // dir, err := os.UserConfigDir() // if err != nil { // return newrulesfp, newlogfp, err // } // newrulesfp = filepath.Join(dir, "gocustomcurls", "rules.json") // } // if len(newlogfp) == 0 { // dir, err := os.UserHomeDir() // if err != nil { // return newrulesfp, newlogfp, err // } // newlogfp = filepath.Join(dir, ".gocustomurls", "logs", "app.log") // } // return newrulesfp, newlogfp, err // } // isValidPort returns true if the port is valid // following the RFC https://datatracker.ietf.org/doc/html/rfc6056#section-2.1 func isValidPort(port int) bool { return port > 0 && port < 65535 } func main() { programName := os.Args[0] // errorLog = log.New(os.Stderr, "", log.LstdFlags) flags := flag.NewFlagSet(os.Args[0], flag.ExitOnError) flags.Usage = func() { out := flags.Output() fmt.Fprintf(out, "Usage: %v [flags]\n\n", programName) fmt.Fprint(out, " This utility serves vanity urls for the go get/install command.\n") fmt.Fprint(out, " By default, the server listens on localhost:7070.\n") flags.PrintDefaults() } confFlag := flags.String("conf", "", "Required. Contains all the configurations options") // portFlag := flags.String("port", "7070", "Optional. Default port is 7070. Port to listen to") // rulesFileFlag := flags.String("rules", "", "Optional. Contains go-import mapping") // logFileFlag := flags.String("logfile", "", "Optional. Default log file") flags.Parse(os.Args[1:]) if len(flags.Args()) > 1 { errorLog.Println("Error: too many command-line arguments") flags.Usage() os.Exit(1) } allSetFlags := flagsSet(flags) if !allSetFlags["conf"] { errorLog.Println("Error: conf arguments must be set") flags.Usage() os.Exit(1) } // TODO: Use only one flag conf with a conf file that // contains the following configuration, port, logfile, rulesfile, sizeofRotation conf := *confFlag c := &Config{} pConf, err := c.LoadMainConfigFile(conf) if err != nil { errorLog.Println(err) os.Exit(1) } p, err := strconv.Atoi(pConf.Port) if err != nil { errorLog.Println(err) os.Exit(1) } if !isValidPort(p) { errorLog.Println(fmt.Errorf("provided port (%d) is not valid", p)) os.Exit(1) } err = c.LoadMappingFile(pConf.RulesFp) if err != nil { errorLog.Println(err) os.Exit(1) } l, err := newFileLogger(pConf.LogFp, pConf.SizeToRotate, pConf.Compression) if err != nil { errorLog.Println(err) os.Exit(1) } // var rulesFile string // if allSetFlags["config"] { // rulesFile = *rulesFileFlag // } // var logFile string // if allSetFlags["logFile"] { // logFile = *logFileFlag // } // rFile, lFile, err := generateDefaults(logFile, rulesFile) // if err != nil { // errorLog.Println(err) // os.Exit(1) // } // // load rules mapping // c := &Config{} // err = c.LoadMappingFile(rFile) // if err != nil { // errorLog.Println(err) // os.Exit(1) // } // l, err := newFileLogger(lFile) // if err != nil { // errorLog.Println(err) // os.Exit(1) // } app := &Application{ Config: c, Log: l, } srv := app.Setup(pConf.Port) // For graceful shutdowns go func() { app.Log.logger.Printf("%s Starting\n", getCurrentDate()) err := srv.ListenAndServe() if !errors.Is(err, http.ErrServerClosed) { errorLog.Printf("HTTP Server error: %+v\n", err) os.Exit(1) } app.Log.logger.Printf("%s Stopped serving new connections.\n", getCurrentDate()) }() sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) <-sigChan shutdownCtx, shutdownRelease := context.WithTimeout(context.Background(), 10*time.Second) defer shutdownRelease() if err := srv.Shutdown(shutdownCtx); err != nil { errorLog.Printf("HTTP shutdown error: %+v\n", err) os.Exit(1) } app.Log.logger.Printf("%s Graceful shutdown complete.\n", getCurrentDate()) }