"Fossies" - the Fresh Open Source Software Archive

Member "geoipupdate-4.3.0/pkg/geoipupdate/database/http_reader.go" (16 Apr 2020, 3845 Bytes) of package /linux/misc/geoipupdate-4.3.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "http_reader.go": 4.1.5_vs_4.2.0.

    1 package database
    2 
    3 import (
    4     "compress/gzip"
    5     "fmt"
    6     "io"
    7     "io/ioutil"
    8     "log"
    9     "net/http"
   10     "net/url"
   11     "time"
   12 
   13     "github.com/maxmind/geoipupdate/v4/pkg/geoipupdate"
   14     "github.com/pkg/errors"
   15 )
   16 
   17 // HTTPDatabaseReader is a Reader that uses an HTTP client to retrieve
   18 // databases.
   19 type HTTPDatabaseReader struct {
   20     client            *http.Client
   21     url               string
   22     licenseKey        string
   23     accountID         int
   24     preserveFileTimes bool
   25     verbose           bool
   26 }
   27 
   28 // NewHTTPDatabaseReader creates a Reader that downloads database updates via
   29 // HTTP.
   30 func NewHTTPDatabaseReader(client *http.Client, config *geoipupdate.Config) Reader {
   31     return &HTTPDatabaseReader{
   32         client:            client,
   33         url:               config.URL,
   34         licenseKey:        config.LicenseKey,
   35         accountID:         config.AccountID,
   36         preserveFileTimes: config.PreserveFileTimes,
   37         verbose:           config.Verbose,
   38     }
   39 }
   40 
   41 // Get retrieves the given edition ID using an HTTP client, writes it to the
   42 // Writer, and validates the hash before committing.
   43 func (reader *HTTPDatabaseReader) Get(destination Writer, editionID string) error {
   44     defer func() {
   45         if err := destination.Close(); err != nil {
   46             log.Println(err)
   47         }
   48     }()
   49 
   50     maxMindURL := fmt.Sprintf(
   51         "%s/geoip/databases/%s/update?db_md5=%s",
   52         reader.url,
   53         url.PathEscape(editionID),
   54         url.QueryEscape(destination.GetHash()),
   55     )
   56 
   57     req, err := http.NewRequest(http.MethodGet, maxMindURL, nil)
   58     if err != nil {
   59         return errors.Wrap(err, "error creating request")
   60     }
   61     req.SetBasicAuth(fmt.Sprintf("%d", reader.accountID), reader.licenseKey)
   62 
   63     if reader.verbose {
   64         log.Printf("Performing update request to %s", maxMindURL)
   65     }
   66     response, err := reader.client.Do(req)
   67     if err != nil {
   68         return errors.Wrap(err, "error performing HTTP request")
   69     }
   70     defer func() {
   71         if err := response.Body.Close(); err != nil {
   72             log.Fatalf("Error closing response body: %+v", errors.Wrap(err, "closing body"))
   73         }
   74     }()
   75 
   76     if response.StatusCode == http.StatusNotModified {
   77         if reader.verbose {
   78             log.Printf("No new updates available for %s", editionID)
   79         }
   80         return nil
   81     }
   82 
   83     if response.StatusCode != http.StatusOK {
   84         buf, err := ioutil.ReadAll(io.LimitReader(response.Body, 256))
   85         if err == nil {
   86             return errors.Errorf("unexpected HTTP status code: %s: %s", response.Status, buf)
   87         }
   88         return errors.Errorf("unexpected HTTP status code: %s", response.Status)
   89     }
   90 
   91     gzReader, err := gzip.NewReader(response.Body)
   92     if err != nil {
   93         return errors.Wrap(err, "encountered an error creating GZIP reader")
   94     }
   95     defer func() {
   96         if err := gzReader.Close(); err != nil {
   97             log.Printf("error closing gzip reader: %s", err)
   98         }
   99     }()
  100 
  101     if _, err = io.Copy(destination, gzReader); err != nil {
  102         return errors.Wrap(err, "error writing response")
  103     }
  104 
  105     newMD5 := response.Header.Get("X-Database-MD5")
  106     if newMD5 == "" {
  107         return errors.New("no X-Database-MD5 header found")
  108     }
  109     if err := destination.ValidHash(newMD5); err != nil {
  110         return err
  111     }
  112 
  113     if err := destination.Commit(); err != nil {
  114         return errors.Wrap(err, "encountered an issue committing database update")
  115     }
  116 
  117     if reader.preserveFileTimes {
  118         modificationTime, err := lastModified(response.Header.Get("Last-Modified"))
  119         if err != nil {
  120             return errors.Wrap(err, "unable to get last modified time")
  121         }
  122         err = destination.SetFileModificationTime(modificationTime)
  123         if err != nil {
  124             return errors.Wrap(err, "unable to set modification time")
  125         }
  126     }
  127 
  128     return nil
  129 }
  130 
  131 // LastModified retrieves the date that the MaxMind database was last modified.
  132 func lastModified(lastModified string) (time.Time, error) {
  133     if lastModified == "" {
  134         return time.Time{}, errors.New("no Last-Modified header found")
  135     }
  136 
  137     t, err := time.ParseInLocation(time.RFC1123, lastModified, time.UTC)
  138     if err != nil {
  139         return time.Time{}, errors.Wrap(err, "error parsing time")
  140     }
  141 
  142     return t, nil
  143 }