浏览代码

First commit

Andrey Blinov 7 年之前
当前提交
f730c265e8
共有 4 个文件被更改,包括 224 次插入0 次删除
  1. 1 0
      .gitignore
  2. 106 0
      config.go
  3. 77 0
      handler.go
  4. 40 0
      init.go

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+db/

+ 106 - 0
config.go

@@ -0,0 +1,106 @@
+package geoip
+
+import (
+	"github.com/mholt/caddy"
+	maxminddb "github.com/oschwald/maxminddb-golang"
+)
+
+// Config specifies configuration parsed for Caddyfile
+type Config struct {
+	DBHandler *maxminddb.Reader // Database's handler if it gets opened.
+
+	// Yout can set returned header names in config
+	// Country
+	HeaderNameCountryCode string
+	HeaderNameCountryIsEU string
+	HeaderNameCountryName string
+
+	// City
+	HeaderNameCityName string
+
+	// Location
+	HeaderNameLocationLat      string
+	HeaderNameLocationLon      string
+	HeaderNameLocationTimeZone string
+}
+
+// NewConfig initialize new Config with default values
+func NewConfig() Config {
+	c := Config{}
+
+	c.HeaderNameCountryCode = "X-Geoip-Country-Code"
+	c.HeaderNameCountryIsEU = "X-Geoip-Country-Eu"
+	c.HeaderNameCountryName = "X-Geoip-Country-Name"
+
+	c.HeaderNameCityName = "X-Geoip-City-Name"
+
+	c.HeaderNameLocationLat = "X-Geoip-Location-Lat"
+	c.HeaderNameLocationLon = "X-Geoip-Location-Lon"
+	c.HeaderNameLocationTimeZone = "X-Geoip-Location-Tz"
+	return c
+}
+
+func parseConfig(c *caddy.Controller) (Config, error) {
+	var config = NewConfig()
+	for c.Next() {
+		for c.NextBlock() {
+			value := c.Val()
+
+			switch value {
+			case "database":
+				if !c.NextArg() {
+					continue
+				}
+				// Check if a database has already been opened
+				if config.DBHandler != nil {
+					continue
+				}
+
+				database := c.Val()
+
+				// Open the database.
+				var err error
+				config.DBHandler, err = maxminddb.Open(database)
+				if err != nil {
+					return config, c.Err("geoip: Can't open database: " + database)
+				}
+			case "set_header_country_code":
+				if !c.NextArg() {
+					continue
+				}
+				config.HeaderNameCountryCode = c.Val()
+			case "set_header_country_name":
+				if !c.NextArg() {
+					continue
+				}
+				config.HeaderNameCountryName = c.Val()
+			case "set_header_country_eu":
+				if !c.NextArg() {
+					continue
+				}
+				config.HeaderNameCountryIsEU = c.Val()
+			case "set_header_city_name":
+				if !c.NextArg() {
+					continue
+				}
+				config.HeaderNameCityName = c.Val()
+			case "set_header_location_lat":
+				if !c.NextArg() {
+					continue
+				}
+				config.HeaderNameLocationLat = c.Val()
+			case "set_header_location_lon":
+				if !c.NextArg() {
+					continue
+				}
+				config.HeaderNameLocationLon = c.Val()
+			case "set_header_location_tz":
+				if !c.NextArg() {
+					continue
+				}
+				config.HeaderNameLocationTimeZone = c.Val()
+			}
+		}
+	}
+	return config, nil
+}

+ 77 - 0
handler.go

@@ -0,0 +1,77 @@
+package geoip
+
+import (
+	"errors"
+	"log"
+	"net"
+	"net/http"
+	"strconv"
+	"strings"
+)
+
+func (gip GeoIP) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
+	gip.addGeoIPHeaders(w, r)
+	return gip.Next.ServeHTTP(w, r)
+}
+
+var record struct {
+	Country struct {
+		ISOCode           string            `maxminddb:"iso_code"`
+		IsInEuropeanUnion bool              `maxminddb:"is_in_european_union"`
+		Names             map[string]string `maxminddb:"names"`
+	} `maxminddb:"country"`
+
+	City struct {
+		Names map[string]string `maxminddb:"names"`
+	} `maxminddb:"city"`
+
+	Location struct {
+		Latitude  float64 `maxminddb:"latitude"`
+		Longitude float64 `maxminddb:"longitude"`
+		TimeZone  string  `maxminddb:"time_zone"`
+	} `maxminddb:"location"`
+}
+
+func (gip GeoIP) addGeoIPHeaders(w http.ResponseWriter, r *http.Request) {
+	clientIP, _ := getClientIP(r, true)
+
+	err := gip.Config.DBHandler.Lookup(clientIP, &record)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	w.Header().Add(gip.Config.HeaderNameCountryCode, record.Country.ISOCode)
+	w.Header().Add(gip.Config.HeaderNameCountryIsEU, strconv.FormatBool(record.Country.IsInEuropeanUnion))
+	w.Header().Add(gip.Config.HeaderNameCountryName, record.Country.Names["en"])
+
+	w.Header().Add(gip.Config.HeaderNameCityName, record.City.Names["en"])
+
+	w.Header().Add(gip.Config.HeaderNameLocationLat, strconv.FormatFloat(record.Location.Latitude, 'f', 6, 64))
+	w.Header().Add(gip.Config.HeaderNameLocationLon, strconv.FormatFloat(record.Location.Longitude, 'f', 6, 64))
+	w.Header().Add(gip.Config.HeaderNameLocationTimeZone, record.Location.TimeZone)
+}
+
+func getClientIP(r *http.Request, strict bool) (net.IP, error) {
+	var ip string
+
+	// Use the client ip from the 'X-Forwarded-For' header, if available.
+	if fwdFor := r.Header.Get("X-Forwarded-For"); fwdFor != "" && !strict {
+		ips := strings.Split(fwdFor, ", ")
+		ip = ips[0]
+	} else {
+		// Otherwise, get the client ip from the request remote address.
+		var err error
+		ip, _, err = net.SplitHostPort(r.RemoteAddr)
+		if err != nil {
+			return nil, err
+		}
+	}
+	//	ip = "212.50.99.193"
+	// Parse the ip address string into a net.IP.
+	parsedIP := net.ParseIP(ip)
+	if parsedIP == nil {
+		return nil, errors.New("unable to parse address")
+	}
+
+	return parsedIP, nil
+}

+ 40 - 0
init.go

@@ -0,0 +1,40 @@
+package geoip
+
+import (
+	"github.com/mholt/caddy"
+	"github.com/mholt/caddy/caddyhttp/httpserver"
+)
+
+// GeoIP Comments me
+type GeoIP struct {
+	Next   httpserver.Handler
+	Config Config
+}
+
+// Init initializes the plugin
+func init() {
+	caddy.RegisterPlugin("geoip", caddy.Plugin{
+		ServerType: "http",
+		Action:     setup,
+	})
+}
+
+func setup(c *caddy.Controller) error {
+	ifconfig, err := parseConfig(c)
+	if err != nil {
+		return err
+	}
+
+	// Create new middleware
+	newMiddleWare := func(next httpserver.Handler) httpserver.Handler {
+		return &GeoIP{
+			Next:   next,
+			Config: ifconfig,
+		}
+	}
+	// Add middleware
+	cfg := httpserver.GetConfig(c)
+	cfg.AddMiddleware(newMiddleWare)
+
+	return nil
+}