-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
161 lines (137 loc) · 4.56 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package main
import (
"encoding/json"
"flag"
"fmt"
"html/template"
"log"
"net"
"os"
"strings"
"time"
"internal/shortie"
"github.com/valyala/fasthttp"
"github.com/asaskevich/govalidator"
"github.com/fasthttp/router"
"github.com/patrickmn/go-cache"
)
const (
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" // Base strings for randStringBytesMaskImprSrc
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
appVersion = "1.3.0"
)
var (
out = log.New(os.Stdout, "", log.LstdFlags|log.Lmicroseconds)
)
func logger(r fasthttp.RequestHandler) fasthttp.RequestHandler {
return fasthttp.RequestHandler(func(ctx *fasthttp.RequestCtx) {
b := time.Now()
r(ctx)
e := time.Now()
out.Printf("[%v] %v | %s | %s %s - %v - %v | %s",
e.Format("2006/01/02 - 15:04:05"),
ctx.RemoteAddr(),
getHTTP(ctx),
ctx.Method(),
ctx.RequestURI(),
ctx.Response.Header.StatusCode(),
e.Sub(b),
ctx.UserAgent(),
)
})
}
func getHTTP(ctx *fasthttp.RequestCtx) string {
if ctx.Response.Header.IsHTTP11() {
return "HTTP/1.1"
}
return "HTTP/1.0"
}
func healthz() func(ctx *fasthttp.RequestCtx) {
r := struct {
Status string `json:"status"`
StatusCode int `json:"status_code"`
}{
Status: "ok",
StatusCode: 200,
}
t := time.Now()
shortie.Pool.Set("status", t.Unix(), -1)
s, f := shortie.Pool.Get("status")
if !f || s != t.Unix() {
r.Status = "error"
r.StatusCode = 500
}
return func(ctx *fasthttp.RequestCtx) {
ctx.Response.Header.SetCanonical([]byte("Content-Type"), []byte("application/json"))
ctx.Response.SetStatusCode(r.StatusCode)
if err := json.NewEncoder(ctx).Encode(r); err != nil {
ctx.Error(err.Error(), fasthttp.StatusInternalServerError)
}
}
}
func main() {
var (
addr = flag.String("addr", "localhost", "Address to listen for connections")
domain = flag.String("domain", "localhost", "Domain to write to the URLs")
path = flag.String("path", "", "Path to the base URL (https://localhost/PATH/...")
http = flag.Bool("http", false, "proto to the base URL (HTTPS://localhost/path/... no real https here just to set the url (for like a proxy offloading https")
port = flag.Int("port", 8080, "Port to listen for connections")
exp = flag.Int("exp", 240, "Default expiration time in hours, default 240")
cleanup = flag.Int("cleanup", 1, "Cleanup interval in hours, default 1")
version = flag.Bool("v", false, "prints current version")
listenAddr string
)
flag.StringVar(&shortie.DumpFile, "dumpFile", "Path to the file to dump the kv db", "urls.json")
flag.IntVar(&shortie.URLSize, "size", 10, "Define the size of the shortened String")
flag.IntVar(&shortie.Port, "urlPort", 443, "Port to use for building URLs")
flag.Parse()
if *version {
fmt.Println(appVersion)
os.Exit(0)
}
if shortie.Port > 65535 || shortie.Port < 1 {
log.Fatalln("Invalid port number")
}
if *port > 65535 || *port < 1 {
log.Fatalln("Invalid port number")
}
if *path != "" && !strings.HasSuffix(*path, "/") {
*path = *path + "/"
}
shortie.Path = *path
ip := net.ParseIP(*addr)
if ip != nil {
listenAddr = fmt.Sprintf("%s:%v", ip.String(), *port)
} else {
if govalidator.IsDNSName(*addr) {
listenAddr = fmt.Sprintf("%s:%v", *addr, *port)
} else {
log.Fatalln("Invalid ip address")
}
}
if !govalidator.IsDNSName(*domain) {
log.Fatalln("Invalid domain address")
}
if *http {
shortie.Proto = "http"
}
shortie.Domain = *domain
shortie.Exp = time.Duration(*exp) * time.Hour
shortie.Cleanup = time.Duration(*cleanup) * time.Hour
shortie.Pool = cache.New(shortie.Exp, shortie.Cleanup)
t := template.Must(template.ParseFiles("templates/response.html"))
r := router.New()
r.GET("/", shortie.IndexHandler(t))
r.POST("/", shortie.Short(t))
r.GET("/{key}", shortie.Redir(t))
r.GET("/healthz", healthz())
r.GET("/v1/toFile", shortie.ToFile(t))
r.GET("/v1/fromFile", shortie.FromFile(t))
r.GET("/v1/count", func(ctx *fasthttp.RequestCtx) { fmt.Fprintf(ctx, "%v", shortie.Pool.ItemCount()) })
r.GET("/v1/dump", shortie.Dump(t))
r.POST("/v1/fromPost", shortie.FromPost(t))
log.Printf("Domain: %s, URL Proto: %s, Listen Address: %s\n", shortie.Domain, shortie.Proto, listenAddr)
log.Fatal(fasthttp.ListenAndServe(listenAddr, logger(r.Handler)))
}