Guest User

Untitled

a guest
Apr 26th, 2018
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.94 KB | None | 0 0
  1. package main
  2.  
  3. import (
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "net/url"
  9. "os"
  10. "runtime"
  11. "sync"
  12. "time"
  13.  
  14. "github.com/patrickmn/go-cache"
  15. "github.com/sirupsen/logrus"
  16. )
  17.  
  18. // client - the core type of the package
  19. type client struct {
  20. *config
  21. *logrus.Logger
  22. *http.Client
  23. *cache.Cache
  24. }
  25.  
  26. // config - configuration of the client
  27. type config = struct {
  28. CacheExpiration time.Duration
  29. LogLevel logrus.Level
  30. ipstack service // parameters of api.ipstack.com
  31. geoip service // parameters of geoip.nekudo.com
  32. }
  33.  
  34. // service - describes parameters of a service which provides information about the country by the provided IP
  35. type service struct {
  36. Key string
  37. Host string
  38. Path string
  39. Scheme string
  40. QueryLimit int // maximum amount of queries per minute
  41. QueryCounter int // number of used queries
  42. QueryOffset time.Time // time of sending the first query
  43. }
  44.  
  45. // New - creates new instance of client type
  46. func New(cfg *config) *client {
  47. log := logrus.New()
  48. logFile, _ := os.Create("cquery.log")
  49. log.SetLevel(cfg.LogLevel)
  50. mw := io.MultiWriter(os.Stdout, logFile)
  51. log.Out = mw
  52. return &client{
  53. config: cfg,
  54. Logger: log,
  55. Client: &http.Client{},
  56. Cache: cache.New(cfg.CacheExpiration*time.Second, cfg.CacheExpiration*time.Second),
  57. }
  58. }
  59.  
  60. // Code - gets country codes for an array of IPs
  61. func (c *client) Code(input []string, output chan string) {
  62. wg := &sync.WaitGroup{}
  63. for _, IP := range input {
  64. if code, found := c.Cache.Get(IP); found {
  65. c.Debugf("found in cache: %v - %v", IP, code)
  66. output <- fmt.Sprintf("%v: %v", IP, code)
  67. continue
  68. }
  69. wg.Add(1)
  70. serv := c.selectService()
  71. serv.update()
  72. go c.query(IP, serv, output, wg)
  73. }
  74. wg.Wait()
  75. close(output)
  76. }
  77.  
  78. // selectService - selects a service
  79. func (c *client) selectService() (serv *service) {
  80.  
  81. elapsedIPSTACK := time.Since(c.config.ipstack.QueryOffset)
  82. elapsedGEOIP := time.Since(c.config.geoip.QueryOffset)
  83.  
  84. // choose a server which first query was the earliest
  85. switch {
  86. case elapsedIPSTACK >= elapsedGEOIP:
  87. serv = &c.config.ipstack
  88. default:
  89. serv = &c.config.geoip
  90. }
  91. c.Logger.Debugf("%v has been chosen: ipstack %v %v/%v, geoip %v %v/%v", serv.Host,
  92. elapsedIPSTACK, c.config.ipstack.QueryCounter, c.config.ipstack.QueryLimit,
  93. elapsedGEOIP, c.config.geoip.QueryCounter, c.config.geoip.QueryLimit)
  94. return serv
  95. }
  96.  
  97. // update - update the service statistics about queries
  98. func (s *service) update() {
  99. if s.QueryCounter == s.QueryLimit-1 {
  100. // nullify the counter and update the time of query
  101. s.QueryOffset = time.Now()
  102. s.QueryCounter = 0
  103. }
  104. s.QueryCounter += 1
  105. }
  106.  
  107. // query - gets a country code for the provided IP
  108. func (c *client) query(IP string, s *service, result chan string, wg *sync.WaitGroup) {
  109. runtime.Gosched() // TODO: remove
  110. defer wg.Done()
  111. ur := url.URL{
  112. Host: s.Host,
  113. Scheme: s.Scheme,
  114. Path: s.Path + IP,
  115. }
  116. query := ur.Query()
  117. query.Set("access_key", s.Key)
  118. ur.RawQuery = query.Encode()
  119. c.Logger.Debug("url: ", ur.String())
  120. res, err := c.Client.Get(ur.String())
  121. defer res.Body.Close()
  122. if err != nil {
  123. c.Logger.Errorf("failed to receive a response for the url %v: %v", ur.String(), err)
  124. }
  125.  
  126. var a answer
  127. decoder := json.NewDecoder(res.Body)
  128. err = decoder.Decode(&a)
  129. if err != nil {
  130. c.Logger.Errorf("failed to unmarshal json for the url %v: %v", ur.String(), err)
  131. }
  132.  
  133. switch s.Host {
  134. case "geoip.nekudo.com":
  135. result <- IP + " - " + a.Country.Code
  136. default:
  137. result <- IP + " - " + a.Code
  138. }
  139. }
  140.  
  141. // answer - structure for JSON unmarshalling of responses of services
  142. type answer struct {
  143. Code string `json:"country_code"`
  144. Country struct {
  145. Code string `json:"code"`
  146. } `json:"country"`
  147. }
  148.  
  149. func main() {
  150.  
  151. var defaultCfg = config{
  152. CacheExpiration: 300,
  153. ipstack: service{
  154. Key: "76a08091e75e6395b71193f387b9fb27",
  155. Host: "api.ipstack.com",
  156. Path: "",
  157. Scheme: "http",
  158. QueryLimit: 5,
  159. },
  160. geoip: service{
  161. Key: "",
  162. Host: "geoip.nekudo.com",
  163. Path: "api/",
  164. Scheme: "http",
  165. QueryLimit: 5,
  166. },
  167. }
  168.  
  169. cl := New(&defaultCfg)
  170. cl.Logger.SetLevel(5)
  171.  
  172. output := make(chan string, 15)
  173. addr := []string{"108.10.10.10", "152.168.0.0", "128.10.10.10", "234.23.2.11", "33.33.33.2", "34.3.5.2", "200.200.200.200", "168.10.10.10", "192.168.10.0", "128.10.110.10", "134.23.21.11", "133.33.33.12", "134.3.5.2", "200.200.200.200", "133.33.33.2"}
  174. cl.Code(addr, output)
  175.  
  176. for code := range output {
  177. fmt.Println(code)
  178. }
  179.  
  180. }
Add Comment
Please, Sign In to add comment