Skip to content

Commit

Permalink
Optional user agent information added
Browse files Browse the repository at this point in the history
  • Loading branch information
Webklex committed Jan 17, 2020
1 parent 8e2a235 commit f00426f
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 19 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) princip
### Added
- NaN

## [1.0.2] - 2020-01-18
- Optional user agent information added

## [1.0.1] - 2020-01-17
- Language parsing improved

Expand Down
44 changes: 43 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,17 @@ You can pass a different IP or hostname. For example, to lookup the geolocation
resolves the name first, then uses the first IP address available, which might be IPv4 or IPv6:
```bash
curl :8080/json/{ip or hostname}?lang={language}
curl :8080/json/{ip or hostname}?lang={language}[&user]
```
Same semantics are available for the `/xml/{ip}` and `/csv/{ip}` endpoints.
JSON responses can be encoded as JSONP, by adding the `callback` parameter:
The used default language depends on the present `Accept-Language` header. You can define the used language by
providing a `lang` parameter containing the two digit country code (en, ru, es, fr, de, jp).
Add the `user` parameter to the end to receive user device specific information. Please see the [JSON example](#json)
for output details.
### CSV
```bash
Expand Down Expand Up @@ -221,6 +224,45 @@ curl :8080/json/
"metro_code": 839
}
```
```bash
curl :8080/json/?user
```
```json
{
"ip": "000.000.000.000",
"is_in_european_union": false,
"continent_code": "Nordamerika",
"country_code": "US",
"country_name": "USA",
"region_code": "NV",
"region_name": "",
"city": "Las Vegas",
"zip_code": "89129",
"time_zone": "America/Los_Angeles",
"latitude": 36.2473,
"longitude": -115.2821,
"accuracy_radius": 20,
"metro_code": 839,
"user": {
"language": {
"language": "en",
"region": "US",
"tag": "en-US"
},
"system": {
"os": "Linux",
"browser": "Ubuntu Chromium",
"version": "79.0.3945.79",
"os_version": "x86_64",
"device": "",
"mobile": false,
"tablet": false,
"desktop": true,
"bot": false
}
}
}
```
### JSONP
```bash
Expand Down
127 changes: 109 additions & 18 deletions server/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"encoding/json"
"encoding/xml"
"github.com/go-web/httpmux"
"github.com/mileusna/useragent"
"golang.org/x/text/language"
"io"
"math"
Expand Down Expand Up @@ -39,7 +40,7 @@ func (s *Server) IpLookUp(writer writerFunc) http.HandlerFunc {
w.Header().Set("X-Database-Date", s.Api.db.Date().Format(http.TimeFormat))
lang := getRequestParam(r, "lang")

resp := q.Record(ip, lang)
resp := q.Record(ip, lang, r)
writer(w, r, resp)
}
}
Expand All @@ -51,8 +52,30 @@ func (q *GeoIpQuery) Translate(names map[string]string, lang string) string {
return names["en"]
}

func (q *GeoIpQuery) Record(ip net.IP, lang string) *responseRecord {
func convertFloat32ToFloat64(q []float32) []float64{
result := make([]float64, len(q))
for i, val := range q {
result[i] = float64(val)
}
return result
}

func getMostPreferredLanguage(tag []language.Tag, q []float32) language.Tag {
lang := language.Tag{}
score := 0.0
for i, val := range q {
f := float64(val)
if score < f {
score = f
lang = tag[i]
}
}
return lang
}

func (q *GeoIpQuery) Record(ip net.IP, lang string, request *http.Request) *responseRecord {
lang = parseAcceptLanguage(lang, q.Country.Names)
// parse header

r := &responseRecord{
IP: ip.String(),
Expand All @@ -72,6 +95,37 @@ func (q *GeoIpQuery) Record(ip net.IP, lang string) *responseRecord {
r.RegionCode = q.Region[0].ISOCode
r.RegionName = q.Region[0].Names[lang]
}

if len(request.URL.Query()["user"]) > 0 {
t, qq, _ := language.ParseAcceptLanguage(request.Header.Get("Accept-Language"))

plang := getMostPreferredLanguage(t, qq)
base, _ := plang.Base()
region, _ := plang.Region()

userAgent := ua.Parse(request.Header.Get("User-Agent"))

r.User = &UserRecord{
Language: &LanguageRecord{
Language: base.String(),
Region: region.String(),
Tag: plang.String(),
},
// Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/79.0.3945.79 Chrome/79.0.3945.79 Safari/537.36
System: &SystemRecord{
OS: userAgent.OS,
Browser: userAgent.Name,
Version: userAgent.Version,
OSVersion: userAgent.OSVersion,
Device: userAgent.Device,
Mobile: userAgent.Mobile,
Tablet: userAgent.Tablet,
Desktop: userAgent.Desktop,
Bot: userAgent.Bot,
},
}
}

return r
}

Expand All @@ -80,25 +134,62 @@ func (rr *responseRecord) String() string {
w := csv.NewWriter(b)
w.UseCRLF = true
var inEU int
var err error
if rr.IsInEuropeanUnion {
inEU = 1
}
err := w.Write([]string{
rr.IP,
rr.ContinentCode,
rr.CountryCode,
rr.CountryName,
rr.RegionCode,
rr.RegionName,
rr.City,
rr.ZipCode,
rr.TimeZone,
strconv.FormatFloat(rr.Latitude, 'f', 4, 64),
strconv.FormatFloat(rr.Longitude, 'f', 4, 64),
strconv.Itoa(int(rr.MetroCode)),
strconv.Itoa(inEU),
strconv.Itoa(int(rr.AccuracyRadius)),
})
if rr.User != nil {
var SystemMobile int;if rr.User.System.Mobile {SystemMobile = 1}
var SystemTablet int;if rr.User.System.Tablet {SystemTablet = 1}
var SystemDesktop int;if rr.User.System.Desktop {SystemDesktop = 1}
var SystemBot int;if rr.User.System.Bot {SystemBot = 1}

err = w.Write([]string{
rr.IP,
rr.ContinentCode,
rr.CountryCode,
rr.CountryName,
rr.RegionCode,
rr.RegionName,
rr.City,
rr.ZipCode,
rr.TimeZone,
strconv.FormatFloat(rr.Latitude, 'f', 4, 64),
strconv.FormatFloat(rr.Longitude, 'f', 4, 64),
strconv.Itoa(int(rr.MetroCode)),
strconv.Itoa(inEU),
strconv.Itoa(int(rr.AccuracyRadius)),
rr.User.Language.Language,
rr.User.Language.Region,
rr.User.Language.Tag,
rr.User.System.OS,
rr.User.System.Browser,
rr.User.System.Version,
rr.User.System.OSVersion,
rr.User.System.Device,
strconv.Itoa(SystemMobile),
strconv.Itoa(SystemTablet),
strconv.Itoa(SystemDesktop),
strconv.Itoa(SystemBot),
})
}else{
err = w.Write([]string{
rr.IP,
rr.ContinentCode,
rr.CountryCode,
rr.CountryName,
rr.RegionCode,
rr.RegionName,
rr.City,
rr.ZipCode,
rr.TimeZone,
strconv.FormatFloat(rr.Latitude, 'f', 4, 64),
strconv.FormatFloat(rr.Longitude, 'f', 4, 64),
strconv.Itoa(int(rr.MetroCode)),
strconv.Itoa(inEU),
strconv.Itoa(int(rr.AccuracyRadius)),
})
}
if err != nil {
return ""
}
Expand Down
24 changes: 24 additions & 0 deletions server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,30 @@ type responseRecord struct {
Longitude float64 `json:"longitude"`
AccuracyRadius uint `json:"accuracy_radius"`
MetroCode uint `json:"metro_code"`
User *UserRecord `json:"user,omitempty"`
}

type UserRecord struct {
Language *LanguageRecord `json:"language"`
System *SystemRecord `json:"system"`
}

type LanguageRecord struct {
Language string `json:"language"`
Region string `json:"region"`
Tag string `json:"tag"`
}

type SystemRecord struct {
OS string `json:"os"`
Browser string `json:"browser"`
Version string `json:"version"`
OSVersion string `json:"os_version"`
Device string `json:"device"`
Mobile bool `json:"mobile"`
Tablet bool `json:"tablet"`
Desktop bool `json:"desktop"`
Bot bool `json:"bot"`
}

type ApiHandler struct {
Expand Down

0 comments on commit f00426f

Please sign in to comment.