Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
22855ebec2 | |||
c090dd60c6 | |||
be1053921b |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/bazel-*
|
24
BUILD.bazel
Normal file
24
BUILD.bazel
Normal file
@ -0,0 +1,24 @@
|
||||
load("@bazel_gazelle//:def.bzl", "gazelle")
|
||||
|
||||
gazelle(name = "gazelle")
|
||||
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["main.go"],
|
||||
importpath = "git.eve.moe/jackyyf/navigator",
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//ipgeo:go_default_library",
|
||||
"//mapping:go_default_library",
|
||||
"@com_github_ipipdotnet_ipdb_go//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_binary(
|
||||
name = "navigator",
|
||||
pure = "on",
|
||||
embed = [":go_default_library"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
43
WORKSPACE
Normal file
43
WORKSPACE
Normal file
@ -0,0 +1,43 @@
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_rules_go",
|
||||
urls = [
|
||||
"https://storage.googleapis.com/bazel-mirror/github.com/bazelbuild/rules_go/releases/download/v0.20.3/rules_go-v0.20.3.tar.gz",
|
||||
"https://github.com/bazelbuild/rules_go/releases/download/v0.20.3/rules_go-v0.20.3.tar.gz",
|
||||
],
|
||||
sha256 = "e88471aea3a3a4f19ec1310a55ba94772d087e9ce46e41ae38ecebe17935de7b",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "bazel_gazelle",
|
||||
urls = [
|
||||
"https://storage.googleapis.com/bazel-mirror/github.com/bazelbuild/bazel-gazelle/releases/download/v0.19.1/bazel-gazelle-v0.19.1.tar.gz",
|
||||
"https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.19.1/bazel-gazelle-v0.19.1.tar.gz",
|
||||
],
|
||||
sha256 = "86c6d481b3f7aedc1d60c1c211c6f76da282ae197c3b3160f54bd3a8f847896f",
|
||||
)
|
||||
|
||||
load("@io_bazel_rules_go//go:deps.bzl", "go_rules_dependencies", "go_register_toolchains")
|
||||
|
||||
go_rules_dependencies()
|
||||
|
||||
go_register_toolchains()
|
||||
|
||||
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository")
|
||||
|
||||
gazelle_dependencies()
|
||||
|
||||
go_repository(
|
||||
name = "com_github_ipipdotnet_ipdb_go",
|
||||
importpath = "github.com/ipipdotnet/ipdb-go",
|
||||
sum = "h1:Afa0qx/SgRevzIK8Qg1TevuD5M28kFLWbzPvU+GQJ08=",
|
||||
version = "v1.2.0",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "net_starlark_go",
|
||||
importpath = "go.starlark.net",
|
||||
sum = "h1:ZP11CRSzO9uOTTOVkH6yodtI3kSY69vUID8lx8B0M3s=",
|
||||
version = "v0.0.0-20191113183327-aaf7be003892",
|
||||
)
|
14
ipgeo/BUILD.bazel
Normal file
14
ipgeo/BUILD.bazel
Normal file
@ -0,0 +1,14 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["ipdb.go"],
|
||||
importpath = "git.eve.moe/jackyyf/navigator/ipgeo",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//mapping/elf:go_default_library",
|
||||
"@com_github_ipipdotnet_ipdb_go//:go_default_library",
|
||||
"@net_starlark_go//starlark:go_default_library",
|
||||
"@net_starlark_go//starlarkstruct:go_default_library",
|
||||
],
|
||||
)
|
90
main.go
90
main.go
@ -8,7 +8,6 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.eve.moe/jackyyf/navigator/ipgeo"
|
||||
@ -17,7 +16,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
errIPv4Only = "Navigator works for valid IPv4 only :)"
|
||||
errIPv4Only = "Navigator works for valid IPv4 only :)"
|
||||
remoteAddrHeader = "X-NAV-REMOTE-IP"
|
||||
)
|
||||
|
||||
type errorMessage struct {
|
||||
@ -36,16 +36,32 @@ func responseWithError(resp http.ResponseWriter, statusCode int, message string)
|
||||
|
||||
func responseWithJsonError(resp http.ResponseWriter, statusCode int, message string) {
|
||||
resp.Header().Set("Content-Type", "application/json")
|
||||
body, err := json.Marshal(&errorMessage{
|
||||
resp.WriteHeader(statusCode)
|
||||
encoder := json.NewEncoder(resp)
|
||||
err := encoder.Encode(&errorMessage{
|
||||
Error: message,
|
||||
})
|
||||
if err != nil {
|
||||
// This should never happen
|
||||
panic("json marshal failed, check code")
|
||||
}
|
||||
resp.Header().Set("Content-Length", strconv.Itoa(len(body)))
|
||||
resp.WriteHeader(statusCode)
|
||||
resp.Write(body)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func buildLocation(info *ipdb.CityInfo) string {
|
||||
@ -76,17 +92,12 @@ func main() {
|
||||
var host string
|
||||
if argIp := req.FormValue("ip"); argIp != "" {
|
||||
host = argIp
|
||||
} else {
|
||||
ip, _, err := net.SplitHostPort(req.RemoteAddr)
|
||||
if err != nil {
|
||||
if net.ParseIP(host).To4() == nil {
|
||||
responseWithError(resp, http.StatusPreconditionFailed, errIPv4Only)
|
||||
return
|
||||
}
|
||||
host = ip
|
||||
}
|
||||
if net.ParseIP(host).To4() == nil {
|
||||
responseWithError(resp, http.StatusPreconditionFailed, errIPv4Only)
|
||||
return
|
||||
} else {
|
||||
host = getRemoteIP(req)
|
||||
}
|
||||
db := ipgeo.Get()
|
||||
|
||||
@ -122,15 +133,7 @@ func main() {
|
||||
})
|
||||
|
||||
http.HandleFunc("/mapping", func(resp http.ResponseWriter, req *http.Request) {
|
||||
host, _, err := net.SplitHostPort(req.RemoteAddr)
|
||||
if err != nil {
|
||||
responseWithError(resp, http.StatusPreconditionFailed, errIPv4Only)
|
||||
return
|
||||
}
|
||||
if net.ParseIP(host).To4() == nil {
|
||||
responseWithError(resp, http.StatusPreconditionFailed, errIPv4Only)
|
||||
return
|
||||
}
|
||||
host := getRemoteIP(req)
|
||||
resp.Header().Set("Content-Type", "text/plain")
|
||||
resp.WriteHeader(http.StatusOK)
|
||||
server := mapping.Get(host)
|
||||
@ -148,6 +151,47 @@ func main() {
|
||||
log.Printf("%s => %s\n", ip, server)
|
||||
fmt.Fprint(resp, server)
|
||||
})
|
||||
http.HandleFunc("/getNodes", func(resp http.ResponseWriter, req *http.Request) {
|
||||
ip := req.FormValue("ip")
|
||||
if net.ParseIP(ip).To4() == nil {
|
||||
responseWithError(resp, http.StatusBadRequest, errIPv4Only)
|
||||
return
|
||||
}
|
||||
nodes := mapping.GetNodes()
|
||||
if nodes == nil {
|
||||
responseWithJsonError(resp, http.StatusInternalServerError, "Unable to get nodes")
|
||||
return
|
||||
}
|
||||
suffix := mapping.GetSuffix(ip)
|
||||
resp.Header().Set("Content-Type", "application/json")
|
||||
resp.WriteHeader(http.StatusOK)
|
||||
jsonEncoder := json.NewEncoder(resp)
|
||||
ret := make([]string, 0, len(nodes))
|
||||
for _, node := range nodes {
|
||||
ret = append(ret, node+suffix)
|
||||
}
|
||||
jsonEncoder.Encode(ret)
|
||||
})
|
||||
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,
|
||||
})
|
||||
})
|
||||
log.Println("HTTP server is running on", *listen_spec)
|
||||
http.ListenAndServe(*listen_spec, nil)
|
||||
}
|
||||
|
9
mapping/BUILD.bazel
Normal file
9
mapping/BUILD.bazel
Normal file
@ -0,0 +1,9 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["map.go"],
|
||||
importpath = "git.eve.moe/jackyyf/navigator/mapping",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//mapping/elf:go_default_library"],
|
||||
)
|
9
mapping/elf/BUILD.bazel
Normal file
9
mapping/elf/BUILD.bazel
Normal file
@ -0,0 +1,9 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["core.go"],
|
||||
importpath = "git.eve.moe/jackyyf/navigator/mapping/elf",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["@net_starlark_go//starlark:go_default_library"],
|
||||
)
|
@ -27,6 +27,7 @@ var (
|
||||
requiredFunc = map[string]struct{}{
|
||||
"getMapping": struct{}{},
|
||||
"getNodes": struct{}{},
|
||||
"getSuffix": struct{}{},
|
||||
}
|
||||
parsedFunc *starlark.StringDict
|
||||
parseThread = &starlark.Thread{
|
||||
@ -152,3 +153,42 @@ func GetMapping(ip string) string {
|
||||
log.Println("Script returned unexpected result:", ret.String())
|
||||
return ""
|
||||
}
|
||||
|
||||
func GetNodes() (res []string) {
|
||||
thread := pool.Get()
|
||||
ret, err := starlark.Call(thread, (*parsedFunc)["getNodes"], nil, nil)
|
||||
if err != nil {
|
||||
log.Println("Starlark execute error:", err.Error())
|
||||
return nil
|
||||
}
|
||||
if r, ok := ret.(*starlark.List); ok {
|
||||
res = make([]string, r.Len())
|
||||
for i := 0; i < r.Len(); i++ {
|
||||
v := r.Index(i)
|
||||
if s, ok := v.(starlark.String); ok {
|
||||
res[i] = string(s)
|
||||
} else {
|
||||
log.Println("Script returned unexpected result:",
|
||||
ret.String())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Println("Script returned unexpected result:", ret.String())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetSuffix(ip string) string {
|
||||
thread := pool.Get()
|
||||
ret, err := starlark.Call(thread, (*parsedFunc)["getSuffix"],
|
||||
starlark.Tuple{starlark.String(ip)}, nil)
|
||||
if err != nil {
|
||||
log.Println("Starlark execute error:", err.Error())
|
||||
return ""
|
||||
}
|
||||
if r, ok := ret.(starlark.String); ok {
|
||||
return string(r)
|
||||
}
|
||||
log.Println("Script returned unexpected result:", ret.String())
|
||||
return ""
|
||||
}
|
||||
|
@ -8,7 +8,10 @@ import (
|
||||
var (
|
||||
defaultServer = flag.String("fallback-node",
|
||||
"xe-mci1-us.edge.eve.network",
|
||||
"Default CDN node in case of any error when executing script")
|
||||
"Default CDN node in case of any script error")
|
||||
defaultSuffix = flag.String("fallback-suffix",
|
||||
".edge.eve.network",
|
||||
"Default CDN suffix in case of any script error")
|
||||
)
|
||||
|
||||
func Initialize() {
|
||||
@ -22,3 +25,15 @@ func Get(ip string) string {
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func GetSuffix(ip string) string {
|
||||
ret := elf.GetSuffix(ip)
|
||||
if ret == "" {
|
||||
return *defaultSuffix
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func GetNodes() []string {
|
||||
return elf.GetNodes()
|
||||
}
|
||||
|
@ -20,3 +20,11 @@ def getMapping(ip):
|
||||
|
||||
def getNodes():
|
||||
return ["xe-mci1-us", "ge-fsn1-de", "ge-lax1-us"]
|
||||
|
||||
def getSuffix(ip):
|
||||
info = geoLookup(ip)
|
||||
if not info:
|
||||
return default_suffix
|
||||
if info.CountryCode == "CN":
|
||||
return CHINA_MAINLAND_SUFFIX
|
||||
return GLOBAL_SUFFIX
|
||||
|
Reference in New Issue
Block a user