Compare commits

..

No commits in common. "add7cb1005a35c20319c56d824be735c4ec1d4d3" and "cdcac469d6a0044fbfed8dddc3fc2ea0991ee176" have entirely different histories.

7 changed files with 38 additions and 170 deletions

20
.gitignore vendored
View File

@ -1,20 +0,0 @@
# Binaries for programs and plugins
artifacts
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work
go.work.sum

View File

@ -1,32 +1,6 @@
# GocustomUrls
# Testing
This is a homespun implementation of the practice of [using custom import paths for go modules](https://pkg.go.dev/cmd/go#hdr-Remote_import_paths).
This package basically returns a html document with a prefilled meta tag
```html
<meta name="go-import" content="{package} git {url location of the package}">
```
There are ways to do this with [nginx](https://www.nirenjan.com/2019/golang-vanity-urls-in-nginx/) or [hugo](https://blog.jbowen.dev/2020/07/using-go-vanity-urls-with-hugo/) but I wanted to:
* host on a subdomain
* not muck up my ngnix config
So I used golang for this project.
## Testing
You can test with
(a) [httpie](https://httpie.io/)
```sh
$ http --body "https://{domain.name}/{package}?go-get=1"
...truncated output
```
(b) With the go-get command
With the go-get command
```sh
$ go get -v -u jbowen.dev/cereal
@ -34,8 +8,9 @@ get "jbowen.dev/cereal": found meta tag get.metaImport{Prefix:"jbowen.dev/cereal
jbowen.dev/cereal (download)
```
## TODOs
With the httpie command
* [x] Fix permission errors around opening the app.log and rules.json.
* [x] Make the flags (config, rules) required instead of optional.
* [ ] Figure how to use logrotate (a linux utility)
```sh
$ http --body "https://gopkg.in/yaml.v3?go-get=1"
...
```

View File

@ -41,10 +41,8 @@ func (c *Config) LoadMappingFile(fp string) error {
if len(c.MappingFilePath) == 0 {
ok := isFile(mappingFilePath)
if !ok {
return fmt.Errorf("mappingfile %s is not found", mappingFilePath)
return fmt.Errorf("%s is not found", mappingFilePath)
}
} else {
mappingFilePath = c.MappingFilePath
}
mappingFile, err := os.Open(mappingFilePath)
if err != nil {

View File

@ -5,26 +5,15 @@ import (
"html/template"
)
// https://stackoverflow.com/questions/70193820/why-isnt-go-embedding-files
//
//go:embed templates/*
// go:embed templates/*
var tmpls embed.FS
func GetServeHtml() (*template.Template, error) {
var t *template.Template
data, err := tmpls.ReadFile("templates/success.html")
if err != nil {
return t, err
}
return template.New("").Parse(string(data))
func GetServeHtml() *template.Template {
data, _ := tmpls.ReadFile("success.html")
return template.Must(template.New("main").Parse(string(data)))
}
func GetDefaultHtml() ([]byte, error) {
var data []byte
var err error
data, err = tmpls.ReadFile("templates/default.html")
if err != nil {
return data, err
}
return data, nil
func GetDefaultHtml() []byte {
data, _ := tmpls.ReadFile("default.html")
return data
}

View File

@ -2,7 +2,6 @@ package main
import (
"bytes"
"fmt"
"net/http"
"strings"
)
@ -15,9 +14,8 @@ func reloadRules(c *Config) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
err := c.LoadMappingFile("")
if err != nil {
e := fmt.Errorf("annot reload rules: %+v", err)
// errorLog.Printf("Cannot reload rules: %+v", err)
http.Error(w, e.Error(), http.StatusInternalServerError)
errorLog.Printf("Cannot reload rules: %+v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write([]byte("ok"))
@ -33,12 +31,7 @@ func serveRules(c *Config) http.HandlerFunc {
// if go-get param is absent, return nothing
if r.FormValue("go-get") != "1" {
data, err := GetDefaultHtml()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(data)
w.Write(GetDefaultHtml())
return
}
@ -46,9 +39,12 @@ func serveRules(c *Config) http.HandlerFunc {
var vanityUrl, proto, repoUrl string
for _, rule := range c.MappingRules.Mappings {
if strings.HasPrefix(strings.ToLower(rule.VanityUrl+"/"), strings.Trim(strings.ToLower(nameOfPkg), " ")) {
vanityUrl = rule.VanityUrl
repoUrl = rule.RealUrl
if strings.HasPrefix(strings.ToLower(nameOfPkg), strings.ToLower(rule.VanityUrl+"/")) {
repo := strings.Replace(strings.ToLower(nameOfPkg), strings.ToLower(rule.VanityUrl), "", -1)
repo = strings.Split(repo, "/")[1]
vanityUrl = rule.VanityUrl + "/" + repo
repoUrl = rule.RealUrl + "/" + repo
proto = rule.Protocol
break
@ -60,14 +56,10 @@ func serveRules(c *Config) http.HandlerFunc {
Proto: proto,
RepoUrl: repoUrl,
}
tmpl, err := GetServeHtml()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
tmpl := GetServeHtml()
var buf bytes.Buffer
err = tmpl.Execute(&buf, &d)
err := tmpl.Execute(&buf, &d)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return

View File

@ -5,9 +5,7 @@ import (
"log"
"net/http"
"os"
"path/filepath"
"strings"
"time"
)
// some headers not worth logging
@ -52,13 +50,7 @@ type LogFileRec struct {
}
func newFileLogger(path string) (*LogFile, error) {
requestedFile := filepath.Clean(filepath.Join("/", path))
parentDir := filepath.Dir(requestedFile)
err := os.MkdirAll(parentDir, 0777)
if err != nil {
return nil, err
}
f, err := os.OpenFile(requestedFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
if err != nil {
return nil, err
}
@ -131,11 +123,6 @@ func shouldLogHeader(s string) bool {
return !hdrsToNotLogMap[s]
}
func getCurrentDate() string {
dt := time.Now()
return dt.Format(time.RFC3339Nano)
}
func (f *LogFile) WriteLog(r *http.Request) error {
if f == nil {
return nil
@ -145,7 +132,6 @@ func (f *LogFile) WriteLog(r *http.Request) error {
rec["requestUri"] = r.RequestURI
rec["Host"] = r.Host
rec["ipAddr"] = extractIpAddress(r)
rec["requestDate"] = getCurrentDate()
if !canSkipExtraHeaders(r) {
for key, val := range r.Header {
if shouldLogHeader(key) && len(val) > 0 {

74
main.go
View File

@ -9,18 +9,16 @@ import (
"net/http"
"os"
"os/signal"
"path/filepath"
"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"
// )
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.
@ -32,35 +30,6 @@ func flagsSet(flags *flag.FlagSet) map[string]bool {
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)
@ -74,9 +43,9 @@ func main() {
flags.PrintDefaults()
}
portFlag := flags.String("port", "7070", "Optional. Default port is 7070. Port to listen to")
rulesFileFlag := flags.String("config", "", "Optional. Contains go-import mapping")
logFileFlag := flags.String("logfile", "", "Optional. Default log file")
portFlag := flags.String("port", "7070", "port to listen to")
rulesFileFlag := flags.String("config", DEFAULT_RULES_FILE, "contains go-import mapping")
logFileFlag := flags.String("logfile", DEFAULT_LOG_FILE, "default log file")
flags.Parse(os.Args[1:])
if len(flags.Args()) > 1 {
@ -90,23 +59,9 @@ func main() {
var port string
if allSetFlags["port"] {
port = *portFlag
} else {
port = "7070"
}
p, err := strconv.Atoi(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)
}
var rulesFile string
if allSetFlags["config"] {
rulesFile = *rulesFileFlag
}
@ -116,20 +71,14 @@ func main() {
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)
err := c.LoadMappingFile(rulesFile)
if err != nil {
errorLog.Println(err)
os.Exit(1)
}
l, err := newFileLogger(lFile)
l, err := newFileLogger(logFile)
if err != nil {
errorLog.Println(err)
os.Exit(1)
@ -143,13 +92,12 @@ func main() {
// 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())
app.Log.logger.Println("Stopped serving new connections.")
}()
sigChan := make(chan os.Signal, 1)
@ -163,5 +111,5 @@ func main() {
errorLog.Printf("HTTP shutdown error: %+v\n", err)
os.Exit(1)
}
app.Log.logger.Printf("%s Graceful shutdown complete.\n", getCurrentDate())
app.Log.logger.Println("Graceful shutdown complete.")
}