2015-02-22 03:09:46 +01:00
|
|
|
package remote
|
|
|
|
|
|
|
|
import (
|
2015-02-23 17:32:55 +01:00
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"net/url"
|
2018-01-19 03:30:21 +01:00
|
|
|
"reflect"
|
2015-02-22 03:09:46 +01:00
|
|
|
"testing"
|
2015-10-22 20:03:25 +02:00
|
|
|
|
|
|
|
"github.com/hashicorp/go-cleanhttp"
|
2015-02-22 03:09:46 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestHTTPClient_impl(t *testing.T) {
|
|
|
|
var _ Client = new(HTTPClient)
|
2017-08-13 18:16:42 +02:00
|
|
|
var _ ClientLocker = new(HTTPClient)
|
2015-02-22 03:09:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestHTTPClient(t *testing.T) {
|
2015-02-23 17:32:55 +01:00
|
|
|
handler := new(testHTTPHandler)
|
|
|
|
ts := httptest.NewServer(http.HandlerFunc(handler.Handle))
|
|
|
|
defer ts.Close()
|
|
|
|
|
|
|
|
url, err := url.Parse(ts.URL)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
2017-08-20 14:59:04 +02:00
|
|
|
// Test basic get/update
|
2015-10-22 20:03:25 +02:00
|
|
|
client := &HTTPClient{URL: url, Client: cleanhttp.DefaultClient()}
|
2015-02-23 17:32:55 +01:00
|
|
|
testClient(t, client)
|
2017-08-13 18:16:42 +02:00
|
|
|
|
2018-01-11 23:09:59 +01:00
|
|
|
// test just a single PUT
|
|
|
|
p := &HTTPClient{
|
|
|
|
URL: url,
|
|
|
|
UpdateMethod: "PUT",
|
|
|
|
Client: cleanhttp.DefaultClient(),
|
|
|
|
}
|
|
|
|
testClient(t, p)
|
|
|
|
|
2017-08-20 14:59:04 +02:00
|
|
|
// Test locking and alternative UpdateMethod
|
2017-08-19 19:31:47 +02:00
|
|
|
a := &HTTPClient{
|
|
|
|
URL: url,
|
2017-08-20 14:59:04 +02:00
|
|
|
UpdateMethod: "PUT",
|
2017-08-19 19:31:47 +02:00
|
|
|
LockURL: url,
|
|
|
|
LockMethod: "LOCK",
|
|
|
|
UnlockURL: url,
|
|
|
|
UnlockMethod: "UNLOCK",
|
|
|
|
Client: cleanhttp.DefaultClient(),
|
|
|
|
}
|
|
|
|
b := &HTTPClient{
|
|
|
|
URL: url,
|
2017-08-20 14:59:04 +02:00
|
|
|
UpdateMethod: "PUT",
|
2017-08-19 19:31:47 +02:00
|
|
|
LockURL: url,
|
|
|
|
LockMethod: "LOCK",
|
|
|
|
UnlockURL: url,
|
|
|
|
UnlockMethod: "UNLOCK",
|
|
|
|
Client: cleanhttp.DefaultClient(),
|
|
|
|
}
|
2017-08-13 18:16:42 +02:00
|
|
|
TestRemoteLocks(t, a, b)
|
2017-08-19 20:17:25 +02:00
|
|
|
|
2018-01-19 03:30:21 +01:00
|
|
|
// test a WebDAV-ish backend
|
|
|
|
davhandler := new(testHTTPHandler)
|
|
|
|
ts = httptest.NewServer(http.HandlerFunc(davhandler.HandleWebDAV))
|
|
|
|
defer ts.Close()
|
|
|
|
|
|
|
|
url, err = url.Parse(ts.URL)
|
|
|
|
c := &HTTPClient{
|
|
|
|
URL: url,
|
|
|
|
UpdateMethod: "PUT",
|
|
|
|
Client: cleanhttp.DefaultClient(),
|
|
|
|
}
|
|
|
|
testClient(t, c) // first time through: 201
|
|
|
|
testClient(t, c) // second time, with identical data: 204
|
2017-08-19 20:17:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func assertError(t *testing.T, err error, expected string) {
|
|
|
|
if err == nil {
|
|
|
|
t.Fatalf("Expected empty config to err")
|
|
|
|
} else if err.Error() != expected {
|
|
|
|
t.Fatalf("Expected err.Error() to be \"%s\", got \"%s\"", expected, err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestHTTPClientFactory(t *testing.T) {
|
|
|
|
// missing address
|
|
|
|
_, err := httpFactory(map[string]string{})
|
|
|
|
assertError(t, err, "missing 'address' configuration")
|
|
|
|
|
|
|
|
// defaults
|
|
|
|
conf := map[string]string{
|
|
|
|
"address": "http://127.0.0.1:8888/foo",
|
|
|
|
}
|
|
|
|
c, err := httpFactory(conf)
|
|
|
|
client, _ := c.(*HTTPClient)
|
|
|
|
if client == nil || err != nil {
|
|
|
|
t.Fatal("Unexpected failure, address")
|
|
|
|
}
|
|
|
|
if client.URL.String() != conf["address"] {
|
|
|
|
t.Fatalf("Expected address \"%s\", got \"%s\"", conf["address"], client.URL.String())
|
|
|
|
}
|
2017-08-20 14:59:04 +02:00
|
|
|
if client.UpdateMethod != "POST" {
|
|
|
|
t.Fatalf("Expected update_method \"%s\", got \"%s\"", "POST", client.UpdateMethod)
|
2017-08-19 20:17:25 +02:00
|
|
|
}
|
|
|
|
if client.LockURL != nil || client.LockMethod != "LOCK" {
|
|
|
|
t.Fatal("Unexpected lock_address or lock_method")
|
|
|
|
}
|
|
|
|
if client.UnlockURL != nil || client.UnlockMethod != "UNLOCK" {
|
|
|
|
t.Fatal("Unexpected unlock_address or unlock_method")
|
|
|
|
}
|
|
|
|
if client.Username != "" || client.Password != "" {
|
|
|
|
t.Fatal("Unexpected username or password")
|
|
|
|
}
|
|
|
|
|
|
|
|
// custom
|
|
|
|
conf = map[string]string{
|
|
|
|
"address": "http://127.0.0.1:8888/foo",
|
2017-08-20 14:59:04 +02:00
|
|
|
"update_method": "BLAH",
|
2017-08-19 20:17:25 +02:00
|
|
|
"lock_address": "http://127.0.0.1:8888/bar",
|
|
|
|
"lock_method": "BLIP",
|
|
|
|
"unlock_address": "http://127.0.0.1:8888/baz",
|
|
|
|
"unlock_method": "BLOOP",
|
|
|
|
"username": "user",
|
|
|
|
"password": "pass",
|
|
|
|
}
|
|
|
|
c, err = httpFactory(conf)
|
|
|
|
client, _ = c.(*HTTPClient)
|
|
|
|
if client == nil || err != nil {
|
2017-08-20 14:59:04 +02:00
|
|
|
t.Fatal("Unexpected failure, update_method")
|
2017-08-19 20:17:25 +02:00
|
|
|
}
|
2017-08-20 14:59:04 +02:00
|
|
|
if client.UpdateMethod != "BLAH" {
|
|
|
|
t.Fatalf("Expected update_method \"%s\", got \"%s\"", "BLAH", client.UpdateMethod)
|
2017-08-19 20:17:25 +02:00
|
|
|
}
|
|
|
|
if client.LockURL.String() != conf["lock_address"] || client.LockMethod != "BLIP" {
|
|
|
|
t.Fatalf("Unexpected lock_address \"%s\" vs \"%s\" or lock_method \"%s\" vs \"%s\"", client.LockURL.String(),
|
|
|
|
conf["lock_address"], client.LockMethod, conf["lock_method"])
|
|
|
|
}
|
|
|
|
if client.UnlockURL.String() != conf["unlock_address"] || client.UnlockMethod != "BLOOP" {
|
|
|
|
t.Fatalf("Unexpected unlock_address \"%s\" vs \"%s\" or unlock_method \"%s\" vs \"%s\"", client.UnlockURL.String(),
|
|
|
|
conf["unlock_address"], client.UnlockMethod, conf["unlock_method"])
|
|
|
|
}
|
|
|
|
if client.Username != "user" || client.Password != "pass" {
|
|
|
|
t.Fatalf("Unexpected username \"%s\" vs \"%s\" or password \"%s\" vs \"%s\"", client.Username, conf["username"],
|
|
|
|
client.Password, conf["password"])
|
|
|
|
}
|
2015-02-23 17:32:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type testHTTPHandler struct {
|
2017-08-13 18:16:42 +02:00
|
|
|
Data []byte
|
|
|
|
Locked bool
|
2015-02-23 17:32:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (h *testHTTPHandler) Handle(w http.ResponseWriter, r *http.Request) {
|
|
|
|
switch r.Method {
|
|
|
|
case "GET":
|
|
|
|
w.Write(h.Data)
|
2018-01-11 23:09:59 +01:00
|
|
|
case "PUT":
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
if _, err := io.Copy(buf, r.Body); err != nil {
|
|
|
|
w.WriteHeader(500)
|
|
|
|
}
|
|
|
|
w.WriteHeader(201)
|
|
|
|
h.Data = buf.Bytes()
|
|
|
|
case "POST":
|
2017-08-19 19:31:47 +02:00
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
if _, err := io.Copy(buf, r.Body); err != nil {
|
|
|
|
w.WriteHeader(500)
|
2015-02-23 17:32:55 +01:00
|
|
|
}
|
2017-08-19 19:31:47 +02:00
|
|
|
h.Data = buf.Bytes()
|
|
|
|
case "LOCK":
|
|
|
|
if h.Locked {
|
2017-08-19 20:17:25 +02:00
|
|
|
w.WriteHeader(423)
|
2017-08-19 19:31:47 +02:00
|
|
|
} else {
|
|
|
|
h.Locked = true
|
|
|
|
}
|
|
|
|
case "UNLOCK":
|
|
|
|
h.Locked = false
|
2015-02-23 17:32:55 +01:00
|
|
|
case "DELETE":
|
|
|
|
h.Data = nil
|
|
|
|
w.WriteHeader(200)
|
|
|
|
default:
|
|
|
|
w.WriteHeader(500)
|
|
|
|
w.Write([]byte(fmt.Sprintf("Unknown method: %s", r.Method)))
|
|
|
|
}
|
2015-02-22 03:09:46 +01:00
|
|
|
}
|
2018-01-19 03:30:21 +01:00
|
|
|
|
|
|
|
// mod_dav-ish behavior
|
|
|
|
func (h *testHTTPHandler) HandleWebDAV(w http.ResponseWriter, r *http.Request) {
|
|
|
|
switch r.Method {
|
|
|
|
case "GET":
|
|
|
|
w.Write(h.Data)
|
|
|
|
case "PUT":
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
if _, err := io.Copy(buf, r.Body); err != nil {
|
|
|
|
w.WriteHeader(500)
|
|
|
|
}
|
|
|
|
if reflect.DeepEqual(h.Data, buf.Bytes()) {
|
|
|
|
h.Data = buf.Bytes()
|
|
|
|
w.WriteHeader(204)
|
|
|
|
} else {
|
|
|
|
h.Data = buf.Bytes()
|
|
|
|
w.WriteHeader(201)
|
|
|
|
}
|
|
|
|
case "DELETE":
|
|
|
|
h.Data = nil
|
|
|
|
w.WriteHeader(200)
|
|
|
|
default:
|
|
|
|
w.WriteHeader(500)
|
|
|
|
w.Write([]byte(fmt.Sprintf("Unknown method: %s", r.Method)))
|
|
|
|
}
|
|
|
|
}
|