2019-10-14 21:39:25 +01:00
|
|
|
// Package http adds a http lock implementation
|
|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"hash/crc32"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
2020-01-30 14:39:00 +03:00
|
|
|
"github.com/micro/go-micro/v2/sync/lock"
|
2019-10-14 21:39:25 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
DefaultPath = "/sync/lock"
|
|
|
|
DefaultAddress = "localhost:8080"
|
|
|
|
)
|
|
|
|
|
|
|
|
type httpLock struct {
|
|
|
|
opts lock.Options
|
|
|
|
}
|
|
|
|
|
2019-10-14 21:52:18 +01:00
|
|
|
func (h *httpLock) url(do, id string) (string, error) {
|
2019-10-14 21:39:25 +01:00
|
|
|
sum := crc32.ChecksumIEEE([]byte(id))
|
|
|
|
node := h.opts.Nodes[sum%uint32(len(h.opts.Nodes))]
|
|
|
|
|
|
|
|
// parse the host:port or whatever
|
|
|
|
uri, err := url.Parse(node)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(uri.Scheme) == 0 {
|
|
|
|
uri.Scheme = "http"
|
|
|
|
}
|
|
|
|
|
|
|
|
// set path
|
|
|
|
// build path
|
2019-10-14 21:52:18 +01:00
|
|
|
path := filepath.Join(DefaultPath, do, h.opts.Prefix, id)
|
2019-10-14 21:39:25 +01:00
|
|
|
uri.Path = path
|
|
|
|
|
|
|
|
// return url
|
|
|
|
return uri.String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpLock) Acquire(id string, opts ...lock.AcquireOption) error {
|
|
|
|
var options lock.AcquireOptions
|
|
|
|
for _, o := range opts {
|
|
|
|
o(&options)
|
|
|
|
}
|
|
|
|
|
2019-10-14 21:52:18 +01:00
|
|
|
uri, err := h.url("acquire", id)
|
2019-10-14 21:39:25 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
ttl := fmt.Sprintf("%d", int64(options.TTL.Seconds()))
|
|
|
|
wait := fmt.Sprintf("%d", int64(options.Wait.Seconds()))
|
|
|
|
|
|
|
|
rsp, err := http.PostForm(uri, url.Values{
|
|
|
|
"id": {id},
|
|
|
|
"ttl": {ttl},
|
|
|
|
"wait": {wait},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer rsp.Body.Close()
|
|
|
|
|
|
|
|
b, err := ioutil.ReadAll(rsp.Body)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// success
|
|
|
|
if rsp.StatusCode == 200 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// return error
|
|
|
|
return errors.New(string(b))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpLock) Release(id string) error {
|
2019-10-14 21:52:18 +01:00
|
|
|
uri, err := h.url("release", id)
|
2019-10-14 21:39:25 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
vals := url.Values{
|
|
|
|
"id": {id},
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("DELETE", uri, strings.NewReader(vals.Encode()))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
rsp, err := http.DefaultClient.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer rsp.Body.Close()
|
|
|
|
|
|
|
|
b, err := ioutil.ReadAll(rsp.Body)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// success
|
|
|
|
if rsp.StatusCode == 200 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// return error
|
|
|
|
return errors.New(string(b))
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewLock(opts ...lock.Option) lock.Lock {
|
|
|
|
var options lock.Options
|
|
|
|
for _, o := range opts {
|
|
|
|
o(&options)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(options.Nodes) == 0 {
|
|
|
|
options.Nodes = []string{DefaultAddress}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &httpLock{
|
|
|
|
opts: options,
|
|
|
|
}
|
|
|
|
}
|