Code refactor, adding GoLand workspace with Bazel plugin.

This commit is contained in:
Yifu Yu 2020-01-12 22:09:30 +08:00
parent 6ac03c18cc
commit 2dbb47f519
25 changed files with 519 additions and 194 deletions

18
.ijwb/.bazelproject Normal file
View File

@ -0,0 +1,18 @@
directories:
.
# Automatically includes all relevant targets under the 'directories' above
derive_targets_from_directories: true
targets:
# If source code isn't resolving, add additional targets that compile it here
additional_languages:
# Uncomment any additional languages you want supported
# dart
# javascript
# python
# typescript
build_flags:
--features=pure

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.system.id="Blaze" type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$/../..">
<excludeFolder url="file://$MODULE_DIR$/.." />
<excludeFolder url="file://$MODULE_DIR$/../../.idea" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.system.id="Blaze" type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$/../../..">
<sourceFolder url="file://$MODULE_DIR$/../../.." isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/../.." />
<excludeFolder url="file://$MODULE_DIR$/../../../bazel-bin" />
<excludeFolder url="file://$MODULE_DIR$/../../../bazel-genfiles" />
<excludeFolder url="file://$MODULE_DIR$/../../../bazel-navigator" />
<excludeFolder url="file://$MODULE_DIR$/../../../bazel-out" />
<excludeFolder url="file://$MODULE_DIR$/../../../bazel-testlogs" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.ijwb/.idea/$CACHE_FILE$ Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="NodePackageJsonFileManager">
<packageJsonPaths />
</component>
</project>

6
.ijwb/.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
# Default ignored files
/workspace.xml
# Project exclude paths
/.

1
.ijwb/.idea/.name Normal file
View File

@ -0,0 +1 @@
navigator

View File

@ -0,0 +1,3 @@
<component name="ProjectDictionaryState">
<dictionary name="eve" />
</component>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalDependencies">
<plugin id="com.google.idea.bazel.ijwb" />
</component>
</project>

72
.ijwb/.idea/misc.xml Normal file
View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownProjectSettings">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true" openRemoteLinks="true">
<PanelProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false">
<PegdownExtensions>
<option name="ABBREVIATIONS" value="false" />
<option name="ANCHORLINKS" value="true" />
<option name="ASIDE" value="false" />
<option name="ATXHEADERSPACE" value="true" />
<option name="AUTOLINKS" value="true" />
<option name="DEFINITIONS" value="false" />
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="FOOTNOTES" value="false" />
<option name="HARDWRAPS" value="false" />
<option name="HTML_DEEP_PARSER" value="false" />
<option name="INSERTED" value="false" />
<option name="QUOTES" value="false" />
<option name="RELAXEDHRULES" value="true" />
<option name="SMARTS" value="false" />
<option name="STRIKETHROUGH" value="true" />
<option name="SUBSCRIPT" value="false" />
<option name="SUPERSCRIPT" value="false" />
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
<option name="SUPPRESS_INLINE_HTML" value="false" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
<option name="TOC" value="false" />
<option name="WIKILINKS" value="true" />
</PegdownExtensions>
<ParserOptions>
<option name="COMMONMARK_LISTS" value="true" />
<option name="DUMMY" value="false" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="FLEXMARK_FRONT_MATTER" value="false" />
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="GITBOOK_URL_ENCODING" value="false" />
<option name="GITHUB_EMOJI_URL" value="false" />
<option name="GITHUB_LISTS" value="false" />
<option name="GITHUB_WIKI_LINKS" value="true" />
<option name="JEKYLL_FRONT_MATTER" value="false" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true" embedImages="false" embedHttpImages="false">
<GeneratorProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
</CssSettings>
<HtmlExportSettings updateOnSave="false" parentDir="$ProjectFileDir$" targetDir="$ProjectFileDir$" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
</project>

9
.ijwb/.idea/modules.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.blaze/modules/.project-data-dir.iml" filepath="$PROJECT_DIR$/.blaze/modules/.project-data-dir.iml" />
<module fileurl="file://$PROJECT_DIR$/.blaze/modules/.workspace.iml" filepath="$PROJECT_DIR$/.blaze/modules/.workspace.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.goide.execution.application.GoApplicationRunConfigurationProducer" />
<option value="com.goide.execution.testing.frameworks.gobench.GobenchRunConfigurationProducer" />
<option value="com.goide.execution.testing.frameworks.gocheck.GocheckRunConfigurationProducer" />
<option value="com.goide.execution.testing.frameworks.gotest.GotestRunConfigurationProducer" />
<option value="com.intellij.javascript.jest.JestRunConfigurationProducer" />
<option value="com.intellij.javascript.protractor.ProtractorRunConfigurationProducer" />
<option value="com.intellij.lang.javascript.buildTools.grunt.rc.GruntRunConfigurationProducer" />
<option value="com.intellij.lang.javascript.buildTools.gulp.rc.GulpRunConfigurationProducer" />
<option value="com.intellij.lang.javascript.buildTools.npm.rc.NpmRunConfigurationProducer" />
</set>
</option>
</component>
</project>

6
.ijwb/.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectTasksOptions">
<TaskOptions isEnabled="true">
<option name="arguments" value="fmt $FilePath$" />
<option name="checkSyntaxErrors" value="true" />
<option name="description" />
<option name="exitCodeBehavior" value="ERROR" />
<option name="fileExtension" value="go" />
<option name="immediateSync" value="true" />
<option name="name" value="go fmt" />
<option name="output" value="$FilePath$" />
<option name="outputFilters">
<array />
</option>
<option name="outputFromStdout" value="false" />
<option name="program" value="$GoExecPath$" />
<option name="runOnExternalChanges" value="false" />
<option name="scopeName" value="Project Files" />
<option name="trackOnlyRoot" value="true" />
<option name="workingDir" value="$ProjectFileDir$" />
<envs>
<env name="GOROOT" value="$GOROOT$" />
<env name="GOPATH" value="$GOPATH$" />
<env name="PATH" value="$GoBinDirs$" />
</envs>
</TaskOptions>
<TaskOptions isEnabled="true">
<option name="arguments" value="run //:gazelle -- -go_prefix git.eve.moe/jackyyf/navigator" />
<option name="checkSyntaxErrors" value="true" />
<option name="description" />
<option name="exitCodeBehavior" value="ERROR" />
<option name="fileExtension" value="go" />
<option name="immediateSync" value="true" />
<option name="name" value="gazelle" />
<option name="output" value="" />
<option name="outputFilters">
<array />
</option>
<option name="outputFromStdout" value="false" />
<option name="program" value="bazel" />
<option name="runOnExternalChanges" value="true" />
<option name="scopeName" value="All Places" />
<option name="trackOnlyRoot" value="false" />
<option name="workingDir" value="$Projectpath$" />
<envs />
</TaskOptions>
</component>
</project>

View File

@ -10,15 +10,16 @@ go_library(
importpath = "git.eve.moe/jackyyf/navigator", importpath = "git.eve.moe/jackyyf/navigator",
visibility = ["//visibility:private"], visibility = ["//visibility:private"],
deps = [ deps = [
"//api/beacon/v1:go_default_library",
"//api/navigator:go_default_library",
"//ipgeo:go_default_library", "//ipgeo:go_default_library",
"//mapping:go_default_library", "//mapping:go_default_library",
"@com_github_ipipdotnet_ipdb_go//:go_default_library",
], ],
) )
go_binary( go_binary(
name = "navigator", name = "navigator",
pure = "on", data = glob(["rules/**"]),
embed = [":go_default_library"], embed = [":go_default_library"],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
) )

8
api/beacon/BUILD.bazel Normal file
View File

@ -0,0 +1,8 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["api.go"],
importpath = "git.eve.moe/jackyyf/navigator/api/beacon",
visibility = ["//visibility:public"],
)

18
api/beacon/api.go Normal file
View File

@ -0,0 +1,18 @@
package beacon
import (
"fmt"
"net/http"
)
var (
apiServeMux = http.NewServeMux()
)
func init() {
http.Handle("/beacon/", http.StripPrefix("/beacon", apiServeMux))
}
func RegisterApi(version string, handler http.Handler) {
apiServeMux.Handle(fmt.Sprintf("/%s/", version), http.StripPrefix(fmt.Sprint("/", version), handler))
}

13
api/beacon/v1/BUILD.bazel Normal file
View File

@ -0,0 +1,13 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["api.go"],
importpath = "git.eve.moe/jackyyf/navigator/api/beacon/v1",
visibility = ["//visibility:public"],
deps = [
"//api/beacon:go_default_library",
"//mapping:go_default_library",
"//utils:go_default_library",
],
)

33
api/beacon/v1/api.go Normal file
View File

@ -0,0 +1,33 @@
package v1
import (
"encoding/json"
"git.eve.moe/jackyyf/navigator/api/beacon"
"git.eve.moe/jackyyf/navigator/mapping"
"git.eve.moe/jackyyf/navigator/utils"
"net/http"
)
var (
serveMux = http.NewServeMux()
)
func init() {
serveMux.HandleFunc("/getNodes", func(resp http.ResponseWriter, req *http.Request) {
host := utils.GetRemoteIP(req)
nodes := mapping.GetNodes()
if nodes == nil {
utils.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,
})
})
beacon.RegisterApi("v1", serveMux)
}

13
api/navigator/BUILD.bazel Normal file
View File

@ -0,0 +1,13 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["api.go"],
importpath = "git.eve.moe/jackyyf/navigator/api/navigator",
visibility = ["//visibility:public"],
deps = [
"//ipgeo:go_default_library",
"//mapping:go_default_library",
"//utils:go_default_library",
],
)

109
api/navigator/api.go Normal file
View File

@ -0,0 +1,109 @@
package navigator
import (
"encoding/json"
"fmt"
"git.eve.moe/jackyyf/navigator/ipgeo"
"git.eve.moe/jackyyf/navigator/mapping"
"git.eve.moe/jackyyf/navigator/utils"
"log"
"net"
"net/http"
"strings"
)
const (
errIPv4Only = "Navigator works for valid IPv4 only :)"
)
func init() {
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 {
utils.ResponseWithError(resp, http.StatusPreconditionFailed, errIPv4Only)
return
}
} else {
host = utils.GetRemoteIP(req)
}
db := ipgeo.Get()
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)
fmt.Fprintln(resp, "您的IP:", host)
fmt.Fprintln(resp, "数据库中IP所属位置:", utils.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))
fmt.Fprintln(resp, "Your IP:", host)
fmt.Fprintln(resp, "Location for your IP according our database:", utils.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)
})
http.HandleFunc("/mapping", func(resp http.ResponseWriter, req *http.Request) {
host := utils.GetRemoteIP(req)
resp.Header().Set("Content-Type", "text/plain")
resp.WriteHeader(http.StatusOK)
server := mapping.Get(host)
fmt.Fprint(resp, server)
})
http.HandleFunc("/getMapping", func(resp http.ResponseWriter, req *http.Request) {
ip := req.FormValue("ip")
if net.ParseIP(ip).To4() == nil {
utils.ResponseWithError(resp, http.StatusBadRequest, errIPv4Only)
return
}
resp.Header().Set("Content-Type", "text/plain")
resp.WriteHeader(http.StatusOK)
server := mapping.Get(ip)
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 {
utils.ResponseWithError(resp, http.StatusBadRequest, errIPv4Only)
return
}
nodes := mapping.GetNodes()
if nodes == nil {
utils.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)
})
}

183
main.go
View File

@ -1,197 +1,24 @@
package main package main
import ( import (
"encoding/json"
"flag" "flag"
"fmt" _ "git.eve.moe/jackyyf/navigator/api/beacon/v1"
"log" _ "git.eve.moe/jackyyf/navigator/api/navigator"
"net"
"net/http"
_ "net/http/pprof"
"strings"
"git.eve.moe/jackyyf/navigator/ipgeo" "git.eve.moe/jackyyf/navigator/ipgeo"
"git.eve.moe/jackyyf/navigator/mapping" "git.eve.moe/jackyyf/navigator/mapping"
"github.com/ipipdotnet/ipdb-go" "log"
"net/http"
_ "net/http/pprof"
) )
const (
errIPv4Only = "Navigator works for valid IPv4 only :)"
remoteAddrHeader = "X-NAV-REMOTE-IP"
)
type errorMessage struct {
Error string `json:error`
}
var ( var (
listen_spec = flag.String("bind", "127.0.0.1:8086", "http server bind spec") listen_spec = flag.String("bind", "127.0.0.1:8086", "http server bind spec")
) )
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{
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
}
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() { func main() {
flag.Parse() flag.Parse()
ipgeo.Initialize() ipgeo.Initialize()
mapping.Initialize() mapping.Initialize()
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)
}
db := ipgeo.Get()
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)
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))
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)
})
http.HandleFunc("/mapping", func(resp http.ResponseWriter, req *http.Request) {
host := getRemoteIP(req)
resp.Header().Set("Content-Type", "text/plain")
resp.WriteHeader(http.StatusOK)
server := mapping.Get(host)
fmt.Fprint(resp, server)
})
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)
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) log.Println("HTTP server is running on", *listen_spec)
http.ListenAndServe(*listen_spec, nil) http.ListenAndServe(*listen_spec, nil)
} }

View File

@ -143,14 +143,7 @@ func reload() {
parsedFunc = &globals parsedFunc = &globals
} }
func GetMapping(ip string) string { func getTarget(ret starlark.Value) string {
thread := pool.Get()
ret, err := starlark.Call(thread, (*parsedFunc)["getMapping"],
starlark.Tuple{starlark.String(ip)}, nil)
if err != nil {
log.Println("Starlark execute error:", err.Error())
return ""
}
switch r := ret.(type) { switch r := ret.(type) {
case starlark.String: case starlark.String:
return string(r) return string(r)
@ -205,6 +198,17 @@ scriptError:
return "" return ""
} }
func GetMapping(ip string) string {
thread := pool.Get()
ret, err := starlark.Call(thread, (*parsedFunc)["getMapping"],
starlark.Tuple{starlark.String(ip)}, nil)
if err != nil {
log.Println("Starlark execute error:", err.Error())
return ""
}
return getTarget(ret)
}
func GetNodes() (res []string) { func GetNodes() (res []string) {
thread := pool.Get() thread := pool.Get()
ret, err := starlark.Call(thread, (*parsedFunc)["getNodes"], nil, nil) ret, err := starlark.Call(thread, (*parsedFunc)["getNodes"], nil, nil)
@ -215,12 +219,11 @@ func GetNodes() (res []string) {
if r, ok := ret.(*starlark.List); ok { if r, ok := ret.(*starlark.List); ok {
res = make([]string, r.Len()) res = make([]string, r.Len())
for i := 0; i < r.Len(); i++ { for i := 0; i < r.Len(); i++ {
v := r.Index(i) res[i] = getTarget(r.Index(i))
if s, ok := v.(starlark.String); ok { if res[i] == "" {
res[i] = string(s)
} else {
log.Println("Script returned unexpected result:", log.Println("Script returned unexpected result:",
ret.String()) ret.String())
return nil
} }
} }
} else { } else {

View File

@ -3,7 +3,9 @@ HETZNER_FSN_1GE = "ge-fsn1-de"
HETZNER_HEL_1GE = "ge-hel1-fi" HETZNER_HEL_1GE = "ge-hel1-fi"
CLOUDCONE_LAX1_1GE = "ge-lax1-us" CLOUDCONE_LAX1_1GE = "ge-lax1-us"
CLOUDCONE_LAX2_1GE = "ge-lax2-us" CLOUDCONE_LAX2_1GE = "ge-lax2-us"
CLOUDCONE_LAX_LB = (CLOUDCONE_LAX1_1GE, CLOUDCONE_LAX2_1GE) CLOUDCONE_LAX3_1GE = "ge-lax3-us"
HOSTSOLUTIONS_OMR1_1GE = "ge-omr1-ro"
CLOUDCONE_LAX_LB = (CLOUDCONE_LAX1_1GE, CLOUDCONE_LAX2_1GE, CLOUDCONE_LAX3_1GE)
default_server = WHOLESALE_INTERNET_10GE default_server = WHOLESALE_INTERNET_10GE
CHINA_MAINLAND_SUFFIX = ".eveedge.link" CHINA_MAINLAND_SUFFIX = ".eveedge.link"
@ -30,7 +32,7 @@ def getMapping(ip):
return default_server return default_server
def getNodes(): def getNodes():
return ["xe-mci1-us", "ge-fsn1-de", "ge-lax1-us"] return [WHOLESALE_INTERNET_10GE, HETZNER_FSN_1GE, CLOUDCONE_LAX_LB, HOSTSOLUTIONS_OMR1_1GE]
def getSuffix(ip): def getSuffix(ip):
info = geoLookup(ip) info = geoLookup(ip)

9
utils/BUILD.bazel Normal file
View File

@ -0,0 +1,9 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["utils.go"],
importpath = "git.eve.moe/jackyyf/navigator/utils",
visibility = ["//visibility:public"],
deps = ["@com_github_ipipdotnet_ipdb_go//:go_default_library"],
)

67
utils/utils.go Normal file
View File

@ -0,0 +1,67 @@
package utils
import (
"encoding/json"
"github.com/ipipdotnet/ipdb-go"
"net"
"net/http"
"strings"
)
const (
remoteAddrHeader = "X-NAV-REMOTE-IP"
)
type errorMessage struct {
Error string `json:"error"`
}
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{
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
}
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)
}