diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..458ed92
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,58 @@
+VERSION = $(shell cat version)
+REVISION := $(shell git rev-parse --short HEAD)
+INFO_COLOR=\033[1;34m
+RESET=\033[0m
+BOLD=\033[1m
+TEST ?= $(shell go list ./... | grep -v -e vendor -e keys -e tmp)
+
+GOVERSION=$(shell go version)
+GO ?= GO111MODULE=on go
+
+TESTCONFIG="misc/test.conf"
+
+DIST ?= unknown
+PREFIX=/usr
+BINDIR=$(PREFIX)/sbin
+SOURCES=Makefile go.mod go.sum version cmd cache_stnsd main.go package/
+BUILD=tmp/bin
+UNAME_S := $(shell uname -s)
+.DEFAULT_GOAL := build
+
+.PHONY: build
+## build: build the nke
+build:
+ $(GO) build -o $(BUILD)/cache-stnsd -ldflags "-X github.com/STNS/cache-stnsd/cmd.version=$(VERSION)"
+
+.PHONY: release
+## release: release nke (tagging and exec goreleaser)
+release:
+ goreleaser --rm-dist
+
+.PHONY: bump
+bump:
+ git semv minor --bump
+ git tag | tail -n1 | sed 's/v//g' > version
+
+.PHONY: releasedeps
+releasedeps: git-semv goreleaser
+
+.PHONY: git-semv
+git-semv:
+ brew tap linyows/git-semv
+ brew install git-semv
+
+
+.PHONY: goreleaser
+goreleaser:
+ brew install goreleaser/tap/goreleaser
+ brew install goreleaser
+
+.PHONY: test
+test:
+ @echo "$(INFO_COLOR)==> $(RESET)$(BOLD)Testing$(RESET)"
+ $(GO) test -v $(TEST) -timeout=30s -parallel=4
+ $(GO) test -race $(TEST)
+
+.PHONY: github_release
+github_release: ## Create some distribution packages
+ ghr -u STNS --replace v$(VERSION) builds/
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..a404e16
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,9 @@
+module github.com/STNS/libstns-go
+
+go 1.14
+
+require (
+ github.com/hashicorp/go-retryablehttp v0.6.7
+ github.com/sirupsen/logrus v1.7.0
+ github.com/thoas/go-funk v0.7.0
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..21a330a
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,19 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
+github.com/hashicorp/go-retryablehttp v0.6.7 h1:8/CAEZt/+F7kR7GevNHulKkUjLht3CPmn7egmhieNKo=
+github.com/hashicorp/go-retryablehttp v0.6.7/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
+github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/thoas/go-funk v0.7.0 h1:GmirKrs6j6zJbhJIficOsz2aAI7700KsU/5YrdHRM1Y=
+github.com/thoas/go-funk v0.7.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/libstns/http.go b/libstns/http.go
new file mode 100644
index 0000000..d51ed49
--- /dev/null
+++ b/libstns/http.go
@@ -0,0 +1,191 @@
+package libstns
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "net/url"
+ "path"
+ "strings"
+ "time"
+
+ "github.com/hashicorp/go-retryablehttp"
+ "github.com/sirupsen/logrus"
+ "github.com/thoas/go-funk"
+)
+
+var version string
+
+type TLS struct {
+ CA string
+ Cert string
+ Key string
+}
+
+type HttpOptions struct {
+ ApiEndpoint string
+ AuthToken string
+ User string
+ Password string
+ UserAgent string
+ SSLVerify bool
+ HttpProxy string
+ RequestTimeout int
+ RequestRetry int
+ HttpHeaders map[string]string
+ TLS TLS
+}
+type Http struct {
+ opt *HttpOptions
+}
+
+type Response struct {
+ StatusCode int
+ Headers map[string]string
+ Body []byte
+}
+
+func NewHttp(opt *HttpOptions) *Http {
+ if opt.UserAgent == "" {
+ opt.UserAgent = "libstns-go"
+ }
+ return &Http{
+ opt: opt,
+ }
+}
+func (h *Http) Request(path string) (*Response, error) {
+ supportHeaders := []string{
+ "user-highest-id",
+ "user-lowest-id",
+ "group-highest-id",
+ "group-lowest-id",
+ }
+
+ req, err := http.NewRequest("GET", path, nil)
+ if err != nil {
+ logrus.Errorf("make http request error:%s", err.Error())
+ return nil, err
+ }
+
+ h.setHeaders(req)
+ h.setBasicAuth(req)
+
+ tc, err := h.tlsConfig()
+ if err != nil {
+ logrus.Errorf("make tls config error:%s", err.Error())
+ return nil, err
+ }
+
+ tr := &http.Transport{
+ TLSClientConfig: tc,
+ Dial: (&net.Dialer{
+ Timeout: time.Duration(h.opt.RequestTimeout) * time.Second,
+ }).Dial,
+ }
+
+ tr.Proxy = http.ProxyFromEnvironment
+ if h.opt.HttpProxy != "" {
+ proxyUrl, err := url.Parse(h.opt.HttpProxy)
+ if err == nil {
+ tr.Proxy = http.ProxyURL(proxyUrl)
+ }
+ }
+ retryClient := retryablehttp.NewClient()
+ retryClient.RetryMax = h.opt.RequestRetry
+
+ client := retryClient.StandardClient()
+ client.Transport = tr
+ resp, err := client.Do(req)
+ if err != nil {
+ logrus.Errorf("http request error:%s", err.Error())
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ headers := map[string]string{}
+ for k, v := range resp.Header {
+ if funk.ContainsString(supportHeaders, strings.ToLower(k)) {
+ headers[k] = v[0]
+ }
+ }
+
+ switch resp.StatusCode {
+ case http.StatusOK:
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ r := Response{
+ StatusCode: resp.StatusCode,
+ Body: body,
+ Headers: headers,
+ }
+
+ return &r, nil
+ default:
+ r := Response{
+ StatusCode: resp.StatusCode,
+ Headers: headers,
+ }
+ return &r, nil
+ }
+}
+
+func (h *Http) RequestURL(requestPath, query string) (*url.URL, error) {
+ u, err := url.Parse(h.opt.ApiEndpoint)
+ if err != nil {
+ return nil, err
+ }
+
+ u.Path = path.Join(u.Path, requestPath)
+ u.RawQuery = query
+ return u, nil
+
+}
+
+func (h *Http) setHeaders(req *http.Request) {
+ for k, v := range h.opt.HttpHeaders {
+ req.Header.Add(k, v)
+ }
+ req.Header.Set("User-Agent", fmt.Sprintf("%s/%s", h.opt.UserAgent, version))
+}
+
+func (h *Http) setBasicAuth(req *http.Request) {
+ if h.opt.User != "" && h.opt.Password != "" {
+ req.SetBasicAuth(h.opt.User, h.opt.Password)
+ }
+}
+
+func (h *Http) tlsConfig() (*tls.Config, error) {
+ tlsConfig := &tls.Config{InsecureSkipVerify: !h.opt.SSLVerify}
+ if h.opt.TLS.CA != "" {
+ CA_Pool := x509.NewCertPool()
+
+ severCert, err := ioutil.ReadFile(h.opt.TLS.CA)
+ if err != nil {
+ return nil, err
+ }
+ CA_Pool.AppendCertsFromPEM(severCert)
+
+ tlsConfig.RootCAs = CA_Pool
+ }
+
+ if h.opt.TLS.Cert != "" && h.opt.TLS.Key != "" {
+ x509Cert, err := tls.LoadX509KeyPair(h.opt.TLS.Cert, h.opt.TLS.Key)
+ if err != nil {
+ return nil, err
+ }
+ tlsConfig.Certificates = make([]tls.Certificate, 1)
+ tlsConfig.Certificates[0] = x509Cert
+ }
+
+ if len(tlsConfig.Certificates) == 0 && tlsConfig.RootCAs == nil {
+ tlsConfig = nil
+ }
+
+ return tlsConfig, nil
+}