mirror of
https://github.com/dcarrillo/whatismyip.git
synced 2025-07-01 18:39:28 +00:00
first commit
This commit is contained in:
101
router/generic.go
Normal file
101
router/generic.go
Normal file
@ -0,0 +1,101 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/internal/httputils"
|
||||
"github.com/dcarrillo/whatismyip/internal/setting"
|
||||
"github.com/dcarrillo/whatismyip/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const userAgentPattern = `curl|wget|libwww-perl|python|ansible-httpget|HTTPie|WindowsPowerShell|http_request|Go-http-client|^$`
|
||||
|
||||
type JSONResponse struct {
|
||||
IP string `json:"ip"`
|
||||
IPVersion byte `json:"ip_version"`
|
||||
ClientPort string `json:"client_port"`
|
||||
Country string `json:"country"`
|
||||
CountryCode string `json:"country_code"`
|
||||
City string `json:"city"`
|
||||
Latitude float64 `json:"latitude"`
|
||||
Longitude float64 `json:"longitude"`
|
||||
PostalCode string `json:"postal_code"`
|
||||
TimeZone string `json:"time_zone"`
|
||||
ASN uint `json:"asn"`
|
||||
ASNOrganization string `json:"asn_organization"`
|
||||
Host string `json:"host"`
|
||||
Headers http.Header `json:"headers"`
|
||||
}
|
||||
|
||||
func getRoot(ctx *gin.Context) {
|
||||
reg := regexp.MustCompile(userAgentPattern)
|
||||
if reg.Match([]byte(ctx.Request.UserAgent())) {
|
||||
ctx.String(http.StatusOK, ctx.ClientIP())
|
||||
} else {
|
||||
name := "home"
|
||||
if setting.App.TemplatePath != "" {
|
||||
name = filepath.Base(setting.App.TemplatePath)
|
||||
}
|
||||
ctx.HTML(http.StatusOK, name, jsonOutput(ctx))
|
||||
}
|
||||
}
|
||||
|
||||
func getClientPortAsString(ctx *gin.Context) {
|
||||
ctx.String(http.StatusOK, strings.Split(ctx.Request.RemoteAddr, ":")[1]+"\n")
|
||||
}
|
||||
|
||||
func getAllAsString(ctx *gin.Context) {
|
||||
output := "IP: " + ctx.ClientIP() + "\n"
|
||||
output += "Client Port: " + strings.Split(ctx.Request.RemoteAddr, ":")[1] + "\n"
|
||||
|
||||
r := service.Geo{IP: net.ParseIP(ctx.ClientIP())}
|
||||
if record := r.LookUpCity(); record != nil {
|
||||
output += geoCityRecordToString(record) + "\n"
|
||||
}
|
||||
|
||||
if record := r.LookUpASN(); record != nil {
|
||||
output += geoASNRecordToString(record) + "\n"
|
||||
}
|
||||
|
||||
h := ctx.Request.Header
|
||||
h["Host"] = []string{ctx.Request.Host}
|
||||
output += httputils.HeadersToSortedString(h)
|
||||
|
||||
ctx.String(http.StatusOK, output)
|
||||
}
|
||||
|
||||
func getJSON(ctx *gin.Context) {
|
||||
ctx.JSON(http.StatusOK, jsonOutput(ctx))
|
||||
}
|
||||
|
||||
func jsonOutput(ctx *gin.Context) JSONResponse {
|
||||
ip := service.Geo{IP: net.ParseIP(ctx.ClientIP())}
|
||||
asnRecord := ip.LookUpASN()
|
||||
cityRecord := ip.LookUpCity()
|
||||
var version byte = 4
|
||||
if p := net.ParseIP(ctx.ClientIP()).To4(); p == nil {
|
||||
version = 6
|
||||
}
|
||||
|
||||
return JSONResponse{
|
||||
IP: ctx.ClientIP(),
|
||||
IPVersion: version,
|
||||
ClientPort: strings.Split(ctx.Request.RemoteAddr, ":")[1],
|
||||
Country: cityRecord.Country.Names["en"],
|
||||
CountryCode: cityRecord.Country.ISOCode,
|
||||
City: cityRecord.City.Names["en"],
|
||||
Latitude: cityRecord.Location.Latitude,
|
||||
Longitude: cityRecord.Location.Longitude,
|
||||
PostalCode: cityRecord.Postal.Code,
|
||||
TimeZone: cityRecord.Location.TimeZone,
|
||||
ASN: asnRecord.AutonomousSystemNumber,
|
||||
ASNOrganization: asnRecord.AutonomousSystemOrganization,
|
||||
Host: ctx.Request.Host,
|
||||
Headers: ctx.Request.Header,
|
||||
}
|
||||
}
|
120
router/generic_test.go
Normal file
120
router/generic_test.go
Normal file
@ -0,0 +1,120 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIP4RootFromCli(t *testing.T) {
|
||||
uas := []string{"", "curl", "wget", "libwww-perl", "python", "ansible-httpget", "HTTPie", "WindowsPowerShell", "http_request", "Go-http-client"}
|
||||
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
req.Header.Set("X-Real-IP", testIP.ipv4)
|
||||
|
||||
for _, ua := range uas {
|
||||
req.Header.Set("User-Agent", ua)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, testIP.ipv4, w.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestHost(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/host", nil)
|
||||
req.Host = "test"
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, "test", w.Body.String())
|
||||
}
|
||||
|
||||
func TestClientPort(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/client-port", nil)
|
||||
req.RemoteAddr = testIP.ipv4 + ":" + "1000"
|
||||
req.Header.Set("X-Real-IP", testIP.ipv4)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, contentType.text, w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, "1000\n", w.Body.String())
|
||||
}
|
||||
|
||||
func TestNotFound(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/not-found", nil)
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 404, w.Code)
|
||||
assert.Equal(t, contentType.text, w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, "Not Found", w.Body.String())
|
||||
}
|
||||
|
||||
func TestJSON(t *testing.T) {
|
||||
expectedIPv4 := `{"client_port":"1000","ip":"81.2.69.192","ip_version":4,"country":"United Kingdom","country_code":"GB","city":"London","latitude":51.5142,"longitude":-0.0931,"postal_code":"","time_zone":"Europe/London","asn":0,"asn_organization":"","host":"test","headers":{"X-Real-Ip":["81.2.69.192"]}}`
|
||||
expectedIPv6 := `{"asn":3352, "asn_organization":"TELEFONICA DE ESPANA", "city":"", "client_port":"9000", "country":"", "country_code":"", "headers":{"X-Real-Ip":["2a02:9000::1"]}, "host":"test", "ip":"2a02:9000::1", "ip_version":6, "latitude":0, "longitude":0, "postal_code":"", "time_zone":""}`
|
||||
|
||||
req, _ := http.NewRequest("GET", "/json", nil)
|
||||
req.RemoteAddr = testIP.ipv4 + ":" + "1000"
|
||||
req.Host = "test"
|
||||
req.Header.Set("X-Real-IP", testIP.ipv4)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, contentType.json, w.Header().Get("Content-Type"))
|
||||
assert.JSONEq(t, expectedIPv4, w.Body.String())
|
||||
|
||||
req.RemoteAddr = testIP.ipv6 + ":" + "1000"
|
||||
req.Host = "test"
|
||||
req.Header.Set("X-Real-IP", testIP.ipv6)
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, contentType.json, w.Header().Get("Content-Type"))
|
||||
assert.JSONEq(t, expectedIPv6, w.Body.String())
|
||||
}
|
||||
|
||||
func TestAll(t *testing.T) {
|
||||
expected := `IP: 81.2.69.192
|
||||
Client Port: 1000
|
||||
City: London
|
||||
Country: United Kingdom
|
||||
Country Code: GB
|
||||
Latitude: 51.514200
|
||||
Longitude: -0.093100
|
||||
Postal Code:
|
||||
Time Zone: Europe/London
|
||||
|
||||
ASN Number: 0
|
||||
ASN Organization:
|
||||
|
||||
Header1: one
|
||||
Host: test
|
||||
X-Real-Ip: 81.2.69.192
|
||||
`
|
||||
|
||||
req, _ := http.NewRequest("GET", "/all", nil)
|
||||
req.RemoteAddr = testIP.ipv4 + ":" + "1000"
|
||||
req.Host = "test"
|
||||
req.Header.Set("X-Real-IP", testIP.ipv4)
|
||||
req.Header.Set("Header1", "one")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, contentType.text, w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, expected, w.Body.String())
|
||||
}
|
143
router/geo.go
Normal file
143
router/geo.go
Normal file
@ -0,0 +1,143 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/models"
|
||||
"github.com/dcarrillo/whatismyip/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type geoDataFormatter struct {
|
||||
title string
|
||||
format func(*models.GeoRecord) string
|
||||
}
|
||||
|
||||
type asnDataFormatter struct {
|
||||
title string
|
||||
format func(*models.ASNRecord) string
|
||||
}
|
||||
|
||||
var geoOutput = map[string]geoDataFormatter{
|
||||
"country": {
|
||||
title: "Country",
|
||||
format: func(record *models.GeoRecord) string {
|
||||
return record.Country.Names["en"]
|
||||
},
|
||||
},
|
||||
"country_code": {
|
||||
title: "Country Code",
|
||||
format: func(record *models.GeoRecord) string {
|
||||
return record.Country.ISOCode
|
||||
},
|
||||
},
|
||||
"city": {
|
||||
title: "City",
|
||||
format: func(record *models.GeoRecord) string {
|
||||
return record.City.Names["en"]
|
||||
},
|
||||
},
|
||||
"latitude": {
|
||||
title: "Latitude",
|
||||
format: func(record *models.GeoRecord) string {
|
||||
return fmt.Sprintf("%f", record.Location.Latitude)
|
||||
},
|
||||
},
|
||||
"longitude": {
|
||||
title: "Longitude",
|
||||
format: func(record *models.GeoRecord) string {
|
||||
return fmt.Sprintf("%f", record.Location.Longitude)
|
||||
},
|
||||
},
|
||||
"postal_code": {
|
||||
title: "Postal Code",
|
||||
format: func(record *models.GeoRecord) string {
|
||||
return record.Postal.Code
|
||||
},
|
||||
},
|
||||
"time_zone": {
|
||||
title: "Time Zone",
|
||||
format: func(record *models.GeoRecord) string {
|
||||
return record.Location.TimeZone
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var asnOutput = map[string]asnDataFormatter{
|
||||
"number": {
|
||||
title: "ASN Number",
|
||||
format: func(record *models.ASNRecord) string {
|
||||
return fmt.Sprintf("%d", record.AutonomousSystemNumber)
|
||||
},
|
||||
},
|
||||
"organization": {
|
||||
title: "ASN Organization",
|
||||
format: func(record *models.ASNRecord) string {
|
||||
return record.AutonomousSystemOrganization
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func getGeoAsString(ctx *gin.Context) {
|
||||
field := strings.ToLower(ctx.Params.ByName("field"))
|
||||
ip := service.Geo{IP: net.ParseIP(ctx.ClientIP())}
|
||||
record := ip.LookUpCity()
|
||||
|
||||
if field == "" {
|
||||
ctx.String(http.StatusOK, geoCityRecordToString(record))
|
||||
} else if g, ok := geoOutput[field]; ok {
|
||||
ctx.String(http.StatusOK, g.format(record))
|
||||
} else {
|
||||
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
||||
}
|
||||
}
|
||||
|
||||
func getASNAsString(ctx *gin.Context) {
|
||||
field := strings.ToLower(ctx.Params.ByName("field"))
|
||||
ip := service.Geo{IP: net.ParseIP(ctx.ClientIP())}
|
||||
record := ip.LookUpASN()
|
||||
|
||||
if field == "" {
|
||||
ctx.String(http.StatusOK, geoASNRecordToString(record))
|
||||
} else if g, ok := asnOutput[field]; ok {
|
||||
ctx.String(http.StatusOK, g.format(record))
|
||||
} else {
|
||||
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
||||
}
|
||||
}
|
||||
|
||||
func geoCityRecordToString(record *models.GeoRecord) string {
|
||||
var output string
|
||||
|
||||
keys := make([]string, 0, len(geoOutput))
|
||||
for k := range geoOutput {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
output += fmt.Sprintf("%s: %v\n", geoOutput[k].title, geoOutput[k].format(record))
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
func geoASNRecordToString(record *models.ASNRecord) string {
|
||||
var output string
|
||||
|
||||
keys := make([]string, 0, len(asnOutput))
|
||||
for k := range asnOutput {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
output += fmt.Sprintf("%s: %v\n", asnOutput[k].title, asnOutput[k].format(record))
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
106
router/geo_test.go
Normal file
106
router/geo_test.go
Normal file
@ -0,0 +1,106 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGeo(t *testing.T) {
|
||||
expected := `City: London
|
||||
Country: United Kingdom
|
||||
Country Code: GB
|
||||
Latitude: 51.514200
|
||||
Longitude: -0.093100
|
||||
Postal Code:
|
||||
Time Zone: Europe/London
|
||||
`
|
||||
|
||||
req, _ := http.NewRequest("GET", "/geo", nil)
|
||||
req.Header.Set("X-Real-IP", testIP.ipv4)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, contentType.text, w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, expected, w.Body.String())
|
||||
}
|
||||
|
||||
func TestGeoField(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/geo/latitude", nil)
|
||||
req.Header.Set("X-Real-IP", testIP.ipv4)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, contentType.text, w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, "51.514200", w.Body.String())
|
||||
}
|
||||
|
||||
func TestGeoField404(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/geo/not-found", nil)
|
||||
req.Header.Set("X-Real-IP", testIP.ipv4)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 404, w.Code)
|
||||
}
|
||||
|
||||
func TestASN(t *testing.T) {
|
||||
expected := `ASN Number: 12552
|
||||
ASN Organization: IP-Only
|
||||
`
|
||||
|
||||
req, _ := http.NewRequest("GET", "/asn", nil)
|
||||
req.Header.Set("X-Real-IP", testIP.ipv4ASN)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, contentType.text, w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, expected, w.Body.String())
|
||||
}
|
||||
|
||||
func TestASNField(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/asn/organization", nil)
|
||||
req.Header.Set("X-Real-IP", testIP.ipv4ASN)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, contentType.text, w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, "IP-Only", w.Body.String())
|
||||
}
|
||||
|
||||
func TestASNField404(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/asn/not-found", nil)
|
||||
req.Header.Set("X-Real-IP", testIP.ipv4ASN)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 404, w.Code)
|
||||
}
|
||||
|
||||
func TestASN_IPv6(t *testing.T) {
|
||||
expected := `ASN Number: 6739
|
||||
ASN Organization: Cableuropa - ONO
|
||||
`
|
||||
|
||||
req, _ := http.NewRequest("GET", "/asn", nil)
|
||||
req.Header.Set("X-Real-IP", testIP.ipv6ASN)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, contentType.text, w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, expected, w.Body.String())
|
||||
}
|
28
router/headers.go
Normal file
28
router/headers.go
Normal file
@ -0,0 +1,28 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/internal/httputils"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func getHeadersAsSortedString(ctx *gin.Context) {
|
||||
h := ctx.Request.Header
|
||||
h["Host"] = []string{ctx.Request.Host}
|
||||
|
||||
ctx.String(http.StatusOK, httputils.HeadersToSortedString(h))
|
||||
}
|
||||
|
||||
func getHeaderAsString(ctx *gin.Context) {
|
||||
h := ctx.Params.ByName("header")
|
||||
if v := ctx.GetHeader(h); v != "" {
|
||||
ctx.String(http.StatusOK, template.HTMLEscapeString(v))
|
||||
} else if strings.ToLower(h) == "host" {
|
||||
ctx.String(http.StatusOK, template.HTMLEscapeString(ctx.Request.Host))
|
||||
} else {
|
||||
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
||||
}
|
||||
}
|
41
router/headers_test.go
Normal file
41
router/headers_test.go
Normal file
@ -0,0 +1,41 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestHeader(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/user-agent", nil)
|
||||
req.Header.Set("User-Agent", "test")
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, contentType.text, w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, "test", w.Body.String())
|
||||
}
|
||||
|
||||
func TestHeaders(t *testing.T) {
|
||||
expected := `Header1: value1
|
||||
Header2: value21
|
||||
Header2: value22
|
||||
Header3: value3
|
||||
Host:
|
||||
`
|
||||
|
||||
req, _ := http.NewRequest("GET", "/headers", nil)
|
||||
req.Header["Header1"] = []string{"value1"}
|
||||
req.Header["Header2"] = []string{"value21", "value22"}
|
||||
req.Header["Header3"] = []string{"value3"}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
app.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, contentType.text, w.Header().Get("Content-Type"))
|
||||
assert.Equal(t, expected, w.Body.String())
|
||||
}
|
32
router/setup.go
Normal file
32
router/setup.go
Normal file
@ -0,0 +1,32 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"log"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/internal/setting"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func SetupTemplate(r *gin.Engine) {
|
||||
if setting.App.TemplatePath == "" {
|
||||
t, _ := template.New("home").Parse(home)
|
||||
r.SetHTMLTemplate(t)
|
||||
} else {
|
||||
log.Printf("Template %s has been loaded", setting.App.TemplatePath)
|
||||
r.LoadHTMLFiles(setting.App.TemplatePath)
|
||||
}
|
||||
}
|
||||
|
||||
func Setup(r *gin.Engine) {
|
||||
r.GET("/", getRoot)
|
||||
r.GET("/client-port", getClientPortAsString)
|
||||
r.GET("/geo", getGeoAsString)
|
||||
r.GET("/geo/:field", getGeoAsString)
|
||||
r.GET("/asn", getASNAsString)
|
||||
r.GET("/asn/:field", getASNAsString)
|
||||
r.GET("/headers", getHeadersAsSortedString)
|
||||
r.GET("/all", getAllAsString)
|
||||
r.GET("/json", getJSON)
|
||||
r.GET("/:header", getHeaderAsString)
|
||||
}
|
47
router/setup_test.go
Normal file
47
router/setup_test.go
Normal file
@ -0,0 +1,47 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/dcarrillo/whatismyip/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type testIPs struct {
|
||||
ipv4 string
|
||||
ipv4ASN string
|
||||
ipv6 string
|
||||
ipv6ASN string
|
||||
}
|
||||
|
||||
type contentTypes struct {
|
||||
text string
|
||||
json string
|
||||
}
|
||||
|
||||
var (
|
||||
app *gin.Engine
|
||||
testIP = testIPs{
|
||||
ipv4: "81.2.69.192",
|
||||
ipv4ASN: "82.99.17.64",
|
||||
ipv6: "2a02:9000::1",
|
||||
ipv6ASN: "2a02:a800::1",
|
||||
}
|
||||
contentType = contentTypes{
|
||||
text: "text/plain; charset=utf-8",
|
||||
json: "application/json; charset=utf-8",
|
||||
}
|
||||
)
|
||||
|
||||
const trustedHeader = "X-Real-IP"
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
app = gin.Default()
|
||||
app.TrustedPlatform = trustedHeader
|
||||
models.Setup("../test/GeoIP2-City-Test.mmdb", "../test/GeoLite2-ASN-Test.mmdb")
|
||||
Setup(app)
|
||||
defer models.CloseDBs()
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
45
router/templates.go
Normal file
45
router/templates.go
Normal file
@ -0,0 +1,45 @@
|
||||
package router
|
||||
|
||||
const home = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>What is my IP Address ?</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>What is my IP ?</h1>
|
||||
<hr />
|
||||
<h2> Your IPv{{ .IPVersion }} address is: {{ .IP }}</h2>
|
||||
<table>
|
||||
<tr> <td> Client Port </td> <td> {{ .ClientPort }} </td> </tr>
|
||||
<tr> <td> Host </td> <td> {{ .Host }} </td> </tr>
|
||||
</table>
|
||||
<h3> Geolocation </h3>
|
||||
<table>
|
||||
<tr> <td> Country </td> <td> {{ .Country }} </td> </tr>
|
||||
<tr> <td> Country Code </td> <td> {{ .CountryCode }} </td> </tr>
|
||||
<tr> <td> City </td> <td> {{ .City }} </td> </tr>
|
||||
<tr> <td> Latitude </td> <td> {{ .Latitude }} </td> </tr>
|
||||
<tr> <td> Longitude </td> <td> {{ .Longitude }} </td> </tr>
|
||||
<tr> <td> Postal Code </td> <td> {{ .PostalCode }} </td> </tr>
|
||||
<tr> <td> Time Zone </td> <td> {{ .TimeZone }} </td> </tr>
|
||||
</table>
|
||||
<h3> Autonomous System </h3>
|
||||
<table>
|
||||
<tr> <td> ASN </td> <td> {{ .ASN }} </td> </tr>
|
||||
<tr> <td> ASN Organization </td> <td> {{ .ASNOrganization }} </td> </tr>
|
||||
</table>
|
||||
<h3> Headers </h3>
|
||||
<table>
|
||||
{{- range $key, $value := .Headers }}
|
||||
{{- range $content := $value }}
|
||||
<tr> <td> {{ $key }} </td> <td> {{ $content }} </td> </tr>
|
||||
{{- end}}
|
||||
{{- end }}
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
`
|
85
router/templates_test.go
Normal file
85
router/templates_test.go
Normal file
@ -0,0 +1,85 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const expectedHome = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>What is my IP Address ?</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>What is my IP ?</h1>
|
||||
<hr />
|
||||
<h2> Your IPv4 address is: 127.0.0.1</h2>
|
||||
<table>
|
||||
<tr> <td> Client Port </td> <td> 1000 </td> </tr>
|
||||
<tr> <td> Host </td> <td> localhost </td> </tr>
|
||||
</table>
|
||||
<h3> Geolocation </h3>
|
||||
<table>
|
||||
<tr> <td> Country </td> <td> A Country </td> </tr>
|
||||
<tr> <td> Country Code </td> <td> XX </td> </tr>
|
||||
<tr> <td> City </td> <td> A City </td> </tr>
|
||||
<tr> <td> Latitude </td> <td> 100 </td> </tr>
|
||||
<tr> <td> Longitude </td> <td> -100 </td> </tr>
|
||||
<tr> <td> Postal Code </td> <td> 00000 </td> </tr>
|
||||
<tr> <td> Time Zone </td> <td> My/Timezone </td> </tr>
|
||||
</table>
|
||||
<h3> Autonomous System </h3>
|
||||
<table>
|
||||
<tr> <td> ASN </td> <td> 0 </td> </tr>
|
||||
<tr> <td> ASN Organization </td> <td> My ISP </td> </tr>
|
||||
</table>
|
||||
<h3> Headers </h3>
|
||||
<table>
|
||||
<tr> <td> Header1 </td> <td> value1 </td> </tr>
|
||||
<tr> <td> Header2 </td> <td> value21 </td> </tr>
|
||||
<tr> <td> Header2 </td> <td> value22 </td> </tr>
|
||||
<tr> <td> Header3 </td> <td> value3 </td> </tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
func TestDefaultTemplate(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
|
||||
req.Header["Header1"] = []string{"value1"}
|
||||
req.Header["Header2"] = []string{"value21", "value22"}
|
||||
req.Header["Header3"] = []string{"value3"}
|
||||
|
||||
tmpl, _ := template.New("home").Parse(home)
|
||||
response := JSONResponse{
|
||||
IP: "127.0.0.1",
|
||||
IPVersion: 4,
|
||||
ClientPort: "1000",
|
||||
Country: "A Country",
|
||||
CountryCode: "XX",
|
||||
City: "A City",
|
||||
Latitude: 100,
|
||||
Longitude: -100,
|
||||
PostalCode: "00000",
|
||||
TimeZone: "My/Timezone",
|
||||
ASN: 0,
|
||||
ASNOrganization: "My ISP",
|
||||
Host: "localhost",
|
||||
Headers: req.Header,
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
err := tmpl.Execute(buf, response)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expectedHome, buf.String())
|
||||
}
|
Reference in New Issue
Block a user