navigator/main.go

177 lines
4.7 KiB
Go
Raw Permalink Normal View History

2019-10-25 17:16:24 +08:00
package main
import (
"encoding/json"
"flag"
"fmt"
2019-10-28 23:29:21 +08:00
"log"
2019-10-25 17:16:24 +08:00
"net"
"net/http"
_ "net/http/pprof"
"strings"
"git.eve.moe/jackyyf/navigator/ipgeo"
"git.eve.moe/jackyyf/navigator/mapping"
2019-10-25 17:16:24 +08:00
"github.com/ipipdotnet/ipdb-go"
)
const (
errIPv4Only = "Navigator works for valid IPv4 only :)"
remoteAddrHeader = "X-NAV-REMOTE-IP"
2019-10-25 17:16:24 +08:00
)
type errorMessage struct {
Error string `json:error`
}
2019-10-28 23:29:21 +08:00
var (
listen_spec = flag.String("bind", "127.0.0.1:8086", "http server bind spec")
)
2019-10-25 17:16:24 +08:00
func responseWithError(resp http.ResponseWriter, statusCode int, message string) {
resp.Header().Set("Content-Type", "text/plain")
resp.WriteHeader(statusCode)
resp.Write([]byte("error: " + message))
}
func responseWithJsonError(resp http.ResponseWriter, statusCode int, message string) {
resp.Header().Set("Content-Type", "application/json")
resp.WriteHeader(statusCode)
encoder := json.NewEncoder(resp)
err := encoder.Encode(&errorMessage{
2019-10-25 17:16:24 +08:00
Error: message,
})
if err != nil {
// This should never happen
panic("json marshal failed, check code")
}
}
func getRemoteIP(req *http.Request) string {
if addr := req.Header.Get(remoteAddrHeader); addr != "" {
if net.ParseIP(addr).To4() == nil {
return ""
}
return addr
}
host, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil {
return ""
}
if net.ParseIP(host).To4() == nil {
return ""
}
return host
}
2019-10-25 17:16:24 +08:00
func buildLocation(info *ipdb.CityInfo) string {
ret := ""
if info.CountryName != "" {
ret += info.CountryName + " "
}
if info.RegionName != "" {
ret += info.RegionName + " "
}
if info.CityName != "" {
ret += info.CityName + " "
}
return strings.TrimSpace(ret)
}
func main() {
flag.Parse()
ipgeo.Initialize()
mapping.Initialize()
2019-10-25 17:16:24 +08:00
http.HandleFunc("/healthz", func(resp http.ResponseWriter, req *http.Request) {
resp.WriteHeader(200)
resp.Write([]byte("ok"))
})
http.HandleFunc("/info", func(resp http.ResponseWriter, req *http.Request) {
var host string
if argIp := req.FormValue("ip"); argIp != "" {
host = argIp
if net.ParseIP(host).To4() == nil {
responseWithError(resp, http.StatusPreconditionFailed, errIPv4Only)
return
}
} else {
host = getRemoteIP(req)
2019-10-25 17:16:24 +08:00
}
db := ipgeo.Get()
2019-10-25 17:16:24 +08:00
info_cn, err := db.FindInfo(host, "CN")
if err != nil {
fmt.Fprintf(resp, "IP %s not found in the database.", host)
return
}
info_en, err := db.FindInfo(host, "EN")
if err != nil {
fmt.Fprintf(resp, "IP %s not found in the database.", host)
return
}
resp.Header().Set("Content-Type", "text/plain")
resp.WriteHeader(http.StatusOK)
server := mapping.Get(host)
2019-10-25 17:16:24 +08:00
fmt.Fprintln(resp, "您的IP:", host)
fmt.Fprintln(resp, "数据库中IP所属位置:", buildLocation(info_cn))
if info_cn.IspDomain != "" {
fmt.Fprintln(resp, "数据库中IP所属运营商:", info_cn.IspDomain)
}
fmt.Fprintln(resp, "您被分配的CDN节点为:", server)
fmt.Fprintln(resp)
fmt.Fprintln(resp, strings.Repeat("=", 72))
2019-10-25 17:16:24 +08:00
fmt.Fprintln(resp, "Your IP:", host)
fmt.Fprintln(resp, "Location for your IP according our database:", buildLocation(info_en))
if info_en.IspDomain != "" {
fmt.Fprintln(resp, "ISP for your IP according our database:", info_en.IspDomain)
}
fmt.Fprintln(resp, "Allocated CDN node for you:", server)
fmt.Fprintln(resp)
2019-10-25 17:16:24 +08:00
})
2019-10-27 16:56:09 +08:00
http.HandleFunc("/mapping", func(resp http.ResponseWriter, req *http.Request) {
host := getRemoteIP(req)
2019-10-27 16:56:09 +08:00
resp.Header().Set("Content-Type", "text/plain")
resp.WriteHeader(http.StatusOK)
server := mapping.Get(host)
fmt.Fprint(resp, server)
})
2019-10-28 20:21:05 +08:00
http.HandleFunc("/getMapping", func(resp http.ResponseWriter, req *http.Request) {
ip := req.FormValue("ip")
if net.ParseIP(ip).To4() == nil {
responseWithError(resp, http.StatusBadRequest, errIPv4Only)
return
}
resp.Header().Set("Content-Type", "text/plain")
resp.WriteHeader(http.StatusOK)
server := mapping.Get(ip)
2019-10-28 23:29:21 +08:00
log.Printf("%s => %s\n", ip, server)
2019-10-28 20:21:05 +08:00
fmt.Fprint(resp, server)
})
clientApi := http.NewServeMux()
http.Handle("/client/", http.StripPrefix("/client", clientApi))
clientV1Api := http.NewServeMux()
clientApi.Handle("/v1/", http.StripPrefix("/v1", clientV1Api))
clientV1Api.HandleFunc("/getNodes", func(resp http.ResponseWriter, req *http.Request) {
host := getRemoteIP(req)
nodes := mapping.GetNodes()
if nodes == nil {
responseWithJsonError(resp, http.StatusInternalServerError, "Unable to get nodes")
return
}
suffix := mapping.GetSuffix(host)
resp.Header().Set("Content-Type", "application/json")
resp.WriteHeader(http.StatusOK)
jsonEncoder := json.NewEncoder(resp)
jsonEncoder.Encode(map[string]interface{}{
"nodes": nodes,
"suffix": suffix,
})
})
2019-10-28 23:29:21 +08:00
log.Println("HTTP server is running on", *listen_spec)
http.ListenAndServe(*listen_spec, nil)
2019-10-25 17:16:24 +08:00
}