-
Notifications
You must be signed in to change notification settings - Fork 0
/
ipinfo.go
141 lines (122 loc) · 3.37 KB
/
ipinfo.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
// Copyright 2024 Aleksandr Zaitsev <me@axv.email>.
// All rights reserved. Use of this source code is governed
// by a BSD-style license that can be found in the LICENSE file.
// Package main implements main method of IPINFO service.
package main
import (
"context"
"errors"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"runtime"
"strings"
"syscall"
"time"
_ "time/tzdata"
"github.com/z0rr0/ipinfo/conf"
"github.com/z0rr0/ipinfo/handle"
)
const (
// Name is a program name
Name = "IPINFO"
// Config is default configuration file name
Config = "config.json"
timeout = 30 * time.Second
)
var (
// Version is program git version
Version = ""
// Revision is revision number
Revision = ""
// BuildDate is build date
BuildDate = ""
// GoVersion is runtime Go language version
GoVersion = runtime.Version()
// internal logger
loggerInfo = log.New(os.Stdout, fmt.Sprintf("INFO [%v]: ", Name),
log.Ldate|log.Ltime|log.Lshortfile)
)
func main() {
defer func() {
if r := recover(); r != nil {
loggerInfo.Printf("abnormal termination [%v]: \n\t%v\n", Version, r)
}
}()
version := flag.Bool("version", false, "show version")
config := flag.String("config", Config, "configuration file")
flag.Parse()
buildInfo := &handle.BuildInfo{Version: Version, Revision: Revision, BuildDate: BuildDate, GoVersion: GoVersion}
if *version {
fmt.Println(buildInfo.String())
return
}
cfg, err := conf.New(*config)
if err != nil {
loggerInfo.Fatal(err)
}
srv := &http.Server{
Addr: cfg.Addr(),
Handler: http.DefaultServeMux,
ReadTimeout: timeout,
WriteTimeout: timeout,
MaxHeaderBytes: 1 << 20, // 1MB
ErrorLog: loggerInfo,
}
loggerInfo.Printf("\n%v\nlisten addr: %v\n", buildInfo.String(), srv.Addr)
handlers := map[string]func(http.ResponseWriter, *conf.IPInfo, *handle.BuildInfo) error{
"/short": handle.TextShortHandler,
"/compact": handle.TextCompactHandler,
"/json": handle.JSONHandler,
"/xml": handle.XMLHandler,
"/html": handle.HTMLHandler,
"/full": handle.FullHTMLHandler,
"/version": handle.VersionHandler,
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
start, code := time.Now(), http.StatusOK
defer func() {
loggerInfo.Printf("%-5v %v\t%-12v\t%v",
r.Method, code, time.Since(start), r.RemoteAddr,
)
}()
info, e := cfg.Info(r)
if e != nil {
loggerInfo.Println(e)
http.Error(w, "ERROR", http.StatusInternalServerError)
return
}
url := strings.TrimRight(r.URL.Path, "/ ")
if h, ok := handlers[url]; ok {
e = h(w, info, buildInfo)
} else {
e = handle.TextHandler(w, r, cfg, info)
}
if e != nil {
loggerInfo.Println(e)
http.Error(w, "ERROR", http.StatusInternalServerError)
code = http.StatusInternalServerError
}
})
idleConnsClosed := make(chan struct{})
go func() {
sigint := make(chan os.Signal, 1)
signal.Notify(sigint, os.Interrupt, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGQUIT))
<-sigint
if e := srv.Shutdown(context.Background()); e != nil {
loggerInfo.Printf("HTTP server shutdown error: %v", e)
}
close(idleConnsClosed)
}()
if err = srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
loggerInfo.Printf("HTTP server ListenAndServe error: %v", err)
}
<-idleConnsClosed
if err = cfg.Close(); err != nil {
loggerInfo.Printf("cfg close error: %v\n", err)
}
loggerInfo.Println("stopped")
}