Skip to content

Instantly share code, notes, and snippets.

@elubow
Created March 22, 2017 14:30
Show Gist options
  • Save elubow/14e1340496e44cc8065bb5db93c2432e to your computer and use it in GitHub Desktop.
Save elubow/14e1340496e44cc8065bb5db93c2432e to your computer and use it in GitHub Desktop.
import (
"fmt"
"errors"
"os"
"time"
"fiftyonedegrees"
)
type userAgentData struct {
lastLoad time.Time
userAgentDb FiftyOneDegreesPatternV3.Provider
}
type DeviceData struct {
DeviceType string `json:"device_type"`
BrowserName string `json:"browser_name"`
BrowserVendor string `json:"browser_vendor"`
BrowserVersion string `json:"browser_version"`
PlatformName string `json:"platform_name"`
PlatformVendor string `json:"platform_vendor"`
PlatformVersion string `json:"platform_version"`
HardwareFamily string `json:"hardware_family"`
HardwareModel string `json:"hardware_model"`
HardwareName string `json:"hardware_name"`
HardwareVendor string `json:"hardware_vendor"`
}
func main() {
userAgentIntervalLoop( time.Duration(60*time.Minute) )
db, err := loadFiftyOneDegrees()
if err != nil {
fmt.Printf("loading error: %s", err)
}
// do work here indefinitely
}
func loadFiftyOneDegrees() (FiftyOneDegreesPatternV3.Provider, error) {
var (
path string
)
dBFileFound := false
fileName := "51Degrees-LiteV3_2.dat"
for _, loadPath := range loadPaths["prod"] {
path = fmt.Sprintf("%s/%s", loadPath, fileName)
_, err := os.Stat(path)
if err != nil {
continue
}
dBFileFound = true
}
if !dBFileFound {
return nil, errors.New(fileName + " file not found")
}
db := FiftyOneDegreesPatternV3.NewProvider(path)
return db, nil
}
func ParseUserAgent(ua string) *DeviceData {
start := time.Now().UTC()
curCache := (*userAgentData)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&s.userAgentData))))
deviceMatch := curCache.userAgentDb.GetMatch(ua)
deviceData := NewUnknownDeviceData()
deviceData.DeviceType = deviceMatch.GetValue("DeviceType")
deviceData.HardwareFamily = deviceMatch.GetValue("HardwareFamily")
deviceData.HardwareName = deviceMatch.GetValue("HardwareName")
deviceData.HardwareVendor = deviceMatch.GetValue("HardwareVendor")
deviceData.HardwareModel = deviceMatch.GetValue("HardwareModel")
deviceData.BrowserName = deviceMatch.GetValue("BrowserName")
deviceData.BrowserVersion = deviceMatch.GetValue("BrowserVersion")
deviceData.BrowserVendor = deviceMatch.GetValue("BrowserVendor")
deviceData.PlatformName = deviceMatch.GetValue("PlatformName")
deviceData.PlatformVendor = deviceMatch.GetValue("PlatformVendor")
deviceData.PlatformVersion = deviceMatch.GetValue("PlatformVersion")
deviceMatch.Close()
return deviceData
}
func NewUnknownDeviceData() *DeviceData {
return &DeviceData{
DeviceType: "Desktop",
BrowserName: "Unknown",
BrowserVendor: "Unknown",
BrowserVersion: "Unknown",
PlatformName: "Unknown",
PlatformVendor: "Unknown",
PlatformVersion: "Unknown",
HardwareFamily: "Emulator",
HardwareModel: "Unknown",
HardwareName: "Desktop|Emulator",
HardwareVendor: "Unknown",
}
}
func reloadUserAgentDatabase() error {
newUserAgentDb, err := loadFiftyOneDegrees()
if err != nil {
return err
}
// atomically swap the pointers
newUserAgentData := &userAgentData{
userAgentDb: newUserAgentDb,
lastLoad: time.Now().UTC(),
}
oldPointer := atomic.SwapPointer((*unsafe.Pointer)(unsafe.Pointer(&s.userAgentData)), unsafe.Pointer(newUserAgentData))
oldCache := (*userAgentData)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&oldPointer))))
if oldCache != nil {
fmt.Printf("Close()ing old userAgentDb and freeing memory")
}
fmt.Printf("reloaded userAgentDb")
return nil
}
func userAgentIntervalLoop(userAgentReloadInterval time.Duration) {
fmt.Printf("Setting user agent database reload to %d minutes", (s.userAgentReloadInterval / time.Minute))
ticker := time.NewTicker(userAgentReloadInterval)
for {
select {
case <-ticker.C:
if err := reloadUserAgentDatabase(); err != nil {
fmt.Printf("not swapping userAgent Database: %s", err)
}
}
}
exit:
ticker.Stop()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment