diff --git a/backend/app/rpc/client_test.go b/backend/app/rpc/client_test.go
deleted file mode 100644
index 12b718ce..00000000
--- a/backend/app/rpc/client_test.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package rpc
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "net/http"
- "net/http/httptest"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestClient_Call(t *testing.T) {
- ts := testServer(t, `{"method":"test","params":[123,"abc"],"id":1}`, `{"result":"12345"}`)
- defer ts.Close()
- c := Client{API: ts.URL, Client: http.Client{}}
- resp, err := c.Call("test", 123, "abc")
- assert.NoError(t, err)
- res := ""
- err = json.Unmarshal(*resp.Result, &res)
- assert.NoError(t, err)
- assert.Equal(t, "12345", res)
- t.Logf("%v %T", res, res)
-}
-
-func TestClient_CallWithObject(t *testing.T) {
- ts := testServer(t, `{"method":"test","params":{"F1":123,"F2":"abc","F3":"2019-06-09T23:03:55Z"},"id":1}`, `{"result":"12345"}`)
- defer ts.Close()
- c := Client{API: ts.URL, Client: http.Client{}}
- obj := struct {
- F1 int
- F2 string
- F3 time.Time
- }{
- F1: 123,
- F2: "abc",
- F3: time.Date(2019, 6, 9, 23, 3, 55, 0, time.UTC),
- }
-
- resp, err := c.Call("test", obj)
- assert.NoError(t, err)
- res := ""
- err = json.Unmarshal(*resp.Result, &res)
- assert.NoError(t, err)
- assert.Equal(t, "12345", res)
- t.Logf("%v %T", res, res)
-}
-
-func TestClient_CallWithNoParams(t *testing.T) {
- ts := testServer(t, `{"method":"test","id":1}`, `{"result":"12345"}`)
- defer ts.Close()
- c := Client{API: ts.URL, Client: http.Client{}}
- resp, err := c.Call("test")
- assert.NoError(t, err)
- res := ""
- err = json.Unmarshal(*resp.Result, &res)
- assert.NoError(t, err)
- assert.Equal(t, "12345", res)
- t.Logf("%v %T", res, res)
-}
-
-func TestClient_CallError(t *testing.T) {
- ts := testServer(t, `{"method":"test","params":[123,"abc"],"id":1}`, `{"error":"some error"}`)
- defer ts.Close()
- c := Client{API: ts.URL, Client: http.Client{}}
- _, err := c.Call("test", 123, "abc")
- assert.EqualError(t, err, "some error")
-}
-
-func TestClient_CallBadResponse(t *testing.T) {
- ts := testServer(t, `{"method":"test","params":[123,"abc"],"id":1}`, `{"result":"12345 invalid}`)
- defer ts.Close()
- c := Client{API: ts.URL, Client: http.Client{}}
- _, err := c.Call("test", 123, "abc")
- assert.NotNil(t, err)
-}
-
-func TestClient_CallBadRemote(t *testing.T) {
- ts := testServer(t, `{"method":"test","params":[123,"abc"],"id":1}`, `{"result":"12345"}`)
- defer ts.Close()
- c := Client{API: "http://127.0.0.2", Client: http.Client{Timeout: 10 * time.Millisecond}}
- _, err := c.Call("test", 123)
- assert.NotNil(t, err)
-}
-
-func testServer(t *testing.T, req, resp string) *httptest.Server {
- return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- body, err := ioutil.ReadAll(r.Body)
- require.NoError(t, err)
- assert.Equal(t, req, string(body))
- t.Logf("req: %s", string(body))
- fmt.Fprintf(w, resp)
- }))
-}
diff --git a/backend/app/rpc/remote.go b/backend/app/rpc/remote.go
deleted file mode 100644
index 48d8ac50..00000000
--- a/backend/app/rpc/remote.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// Package rpc implements client ans server for RPC-like communication with remote storage.
-// The protocol is somewhat simplified version of json-rpc with a single POST call sending
-// Request json (method name and the list of parameters) and receiving back json Response with "result" json
-// and error string
-package rpc
-
-import (
- "encoding/json"
-)
-
-// Request encloses method name and all params
-type Request struct {
- Method string `json:"method"`
- Params interface{} `json:"params,omitempty"`
- ID uint64 `json:"id"`
-}
-
-// Response encloses result and error received from remote server
-type Response struct {
- Result *json.RawMessage `json:"result,omitempty"`
- Error string `json:"error,omitempty"`
- ID uint64 `json:"id"`
-}
diff --git a/backend/app/rpc/server_test.go b/backend/app/rpc/server_test.go
deleted file mode 100644
index c39fe51a..00000000
--- a/backend/app/rpc/server_test.go
+++ /dev/null
@@ -1,245 +0,0 @@
-package rpc
-
-import (
- "bytes"
- "encoding/json"
- "io/ioutil"
- "net/http"
- "net/http/httptest"
- "testing"
- "time"
-
- "github.com/pkg/errors"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestServerPrimitiveTypes(t *testing.T) {
- s := Server{API: "/v1/cmd"}
-
- type respData struct {
- Res1 string
- Res2 bool
- }
-
- s.Add("test", func(id uint64, params json.RawMessage) Response {
- args := []interface{}{}
- if err := json.Unmarshal(params, &args); err != nil {
- return Response{Error: err.Error()}
- }
- t.Logf("%+v", args)
-
- assert.Equal(t, 3, len(args))
- assert.Equal(t, "blah", args[0].(string))
- assert.Equal(t, 42., args[1].(float64))
- assert.Equal(t, true, args[2].(bool))
-
- r, err := s.EncodeResponse(id, respData{"res blah", true}, nil)
- assert.NoError(t, err)
- return r
- })
-
- go func() { _ = s.Run(9091) }()
- defer func() { assert.NoError(t, s.Shutdown()) }()
- time.Sleep(10 * time.Millisecond)
-
- // check with direct http call
- clientReq := Request{Method: "test", Params: []interface{}{"blah", 42, true}, ID: 123}
- b := bytes.Buffer{}
- require.NoError(t, json.NewEncoder(&b).Encode(clientReq))
- resp, err := http.Post("http://127.0.0.1:9091/v1/cmd", "application/json", &b)
- require.NoError(t, err)
- defer resp.Body.Close()
- assert.Equal(t, 200, resp.StatusCode)
- data, err := ioutil.ReadAll(resp.Body)
- assert.NoError(t, err)
- assert.Equal(t, `{"result":{"Res1":"res blah","Res2":true},"id":123}`+"\n", string(data))
-
- // check with client call
- c := Client{API: "http://127.0.0.1:9091/v1/cmd", Client: http.Client{}}
- r, err := c.Call("test", "blah", 42, true)
- assert.NoError(t, err)
- assert.Equal(t, "", r.Error)
-
- res := respData{}
- err = json.Unmarshal(*r.Result, &res)
- assert.NoError(t, err)
- assert.Equal(t, respData{Res1: "res blah", Res2: true}, res)
- assert.Equal(t, uint64(1), r.ID)
-}
-
-func TestServerWithObject(t *testing.T) {
- s := Server{API: "/v1/cmd"}
-
- type respData struct {
- Res1 string
- Res2 bool
- }
-
- type reqData struct {
- Time time.Time
- F1 string
- F2 time.Duration
- }
-
- s.Add("test", func(id uint64, params json.RawMessage) Response {
- arg := reqData{}
- if err := json.Unmarshal(params, &arg); err != nil {
- return Response{Error: err.Error()}
- }
- t.Logf("%+v", arg)
-
- r, err := s.EncodeResponse(id, respData{"res blah", true}, nil)
- assert.NoError(t, err)
- return r
- })
-
- go func() { _ = s.Run(9091) }()
- defer func() { assert.NoError(t, s.Shutdown()) }()
- time.Sleep(10 * time.Millisecond)
-
- c := Client{API: "http://127.0.0.1:9091/v1/cmd", Client: http.Client{}}
- r, err := c.Call("test", reqData{Time: time.Now(), F1: "sawert", F2: time.Minute})
- assert.NoError(t, err)
- assert.Equal(t, "", r.Error)
-
- res := respData{}
- err = json.Unmarshal(*r.Result, &res)
- assert.NoError(t, err)
- assert.Equal(t, respData{Res1: "res blah", Res2: true}, res)
-}
-
-func TestServerMethodNotImplemented(t *testing.T) {
- s := Server{}
- ts := httptest.NewServer(http.HandlerFunc(s.handler))
- defer ts.Close()
- s.Add("test", func(id uint64, params json.RawMessage) Response {
- return Response{}
- })
-
- r := Request{Method: "blah"}
- buf := bytes.Buffer{}
- assert.NoError(t, json.NewEncoder(&buf).Encode(r))
- resp, err := http.Post(ts.URL, "application/json", &buf)
- require.NoError(t, err)
- assert.Equal(t, http.StatusNotImplemented, resp.StatusCode)
-
- assert.EqualError(t, s.Shutdown(), "http server is not running")
-}
-
-func TestServerWithAuth(t *testing.T) {
- s := Server{API: "/v1/cmd", AuthUser: "user", AuthPasswd: "passwd"}
-
- s.Add("test", func(id uint64, params json.RawMessage) Response {
- args := []interface{}{}
- if err := json.Unmarshal(params, &args); err != nil {
- return Response{Error: err.Error()}
- }
- t.Logf("%+v", args)
-
- assert.Equal(t, 3, len(args))
- assert.Equal(t, "blah", args[0].(string))
- assert.Equal(t, 42., args[1].(float64))
- assert.Equal(t, true, args[2].(bool))
-
- r, err := s.EncodeResponse(id, "res blah", nil)
- assert.NoError(t, err)
- return r
- })
-
- go func() { _ = s.Run(9091) }()
- time.Sleep(10 * time.Millisecond)
- defer func() { assert.NoError(t, s.Shutdown()) }()
-
- c := Client{API: "http://127.0.0.1:9091/v1/cmd", Client: http.Client{}, AuthUser: "user", AuthPasswd: "passwd"}
- r, err := c.Call("test", "blah", 42, true)
- assert.NoError(t, err)
- assert.Equal(t, "", r.Error)
- val := ""
- err = json.Unmarshal(*r.Result, &val)
- assert.NoError(t, err)
- assert.Equal(t, "res blah", val)
-
- c = Client{API: "http://127.0.0.1:9091/v1/cmd", Client: http.Client{}}
- _, err = c.Call("test", "blah", 42, true)
- assert.EqualError(t, err, "bad status 401 Unauthorized for test")
-}
-
-func TestServerErrReturn(t *testing.T) {
- s := Server{API: "/v1/cmd", AuthUser: "user", AuthPasswd: "passwd"}
-
- s.Add("test", func(id uint64, params json.RawMessage) Response {
- args := []interface{}{}
- if err := json.Unmarshal(params, &args); err != nil {
- return Response{Error: err.Error()}
- }
- t.Logf("%+v", args)
-
- assert.Equal(t, 3, len(args))
- assert.Equal(t, "blah", args[0].(string))
- assert.Equal(t, 42., args[1].(float64))
- assert.Equal(t, true, args[2].(bool))
-
- r, err := s.EncodeResponse(id, "res blah", errors.New("some error"))
- assert.NoError(t, err)
- return r
- })
-
- go func() { _ = s.Run(9091) }()
- defer func() { assert.NoError(t, s.Shutdown()) }()
- time.Sleep(10 * time.Millisecond)
-
- c := Client{API: "http://127.0.0.1:9091/v1/cmd", Client: http.Client{}, AuthUser: "user", AuthPasswd: "passwd"}
- _, err := c.Call("test", "blah", 42, true)
- assert.EqualError(t, err, "some error")
-}
-
-func TestServerGroup(t *testing.T) {
- s := Server{API: "/v1/cmd"}
- s.Group("pre", HandlersGroup{
- "fn1": func(id uint64, params json.RawMessage) Response {
- return Response{}
- },
- "fn2": func(id uint64, params json.RawMessage) Response {
- return Response{}
- },
- })
- go func() { _ = s.Run(9091) }()
- defer func() { assert.NoError(t, s.Shutdown()) }()
- time.Sleep(10 * time.Millisecond)
-
- c := Client{API: "http://127.0.0.1:9091/v1/cmd", Client: http.Client{}}
- _, err := c.Call("fn1")
- assert.EqualError(t, err, "bad status 501 Not Implemented for fn1")
-
- _, err = c.Call("pre.fn1")
- assert.NoError(t, err)
- _, err = c.Call("pre.fn2")
- assert.NoError(t, err)
-}
-
-func TestServerAddLate(t *testing.T) {
- s := Server{API: "/v1/cmd"}
- s.Add("fn1", func(id uint64, params json.RawMessage) Response {
- return Response{}
- })
- go func() { _ = s.Run(9091) }()
- defer func() { assert.NoError(t, s.Shutdown()) }()
- time.Sleep(10 * time.Millisecond)
-
- // too late, ignored after run
- s.Add("fn2", func(id uint64, params json.RawMessage) Response {
- return Response{}
- })
-
- c := Client{API: "http://127.0.0.1:9091/v1/cmd", Client: http.Client{}}
- _, err := c.Call("fn1")
- assert.NoError(t, err)
- _, err = c.Call("fn2")
- assert.EqualError(t, err, "bad status 501 Not Implemented for fn2")
-}
-
-func TestServerNoHandlers(t *testing.T) {
- s := Server{API: "/v1/cmd", AuthUser: "user", AuthPasswd: "passwd"}
- assert.EqualError(t, s.Run(9091), "nothing mapped for dispatch, Add has to be called prior to Run")
-}
diff --git a/backend/app/store/engine/remote.go b/backend/app/store/engine/remote.go
index bb084295..186ff9f0 100644
--- a/backend/app/store/engine/remote.go
+++ b/backend/app/store/engine/remote.go
@@ -3,13 +3,14 @@ package engine
import (
"encoding/json"
- "github.com/umputun/remark/backend/app/rpc"
+ "github.com/go-pkgz/jrpc"
+
"github.com/umputun/remark/backend/app/store"
)
// RPC implements remote engine and delegates all Calls to remote http server
type RPC struct {
- rpc.Client
+ jrpc.Client
}
// Create comment and return ID
diff --git a/backend/app/store/engine/remote_test.go b/backend/app/store/engine/remote_test.go
index 160f6d29..b97b637c 100644
--- a/backend/app/store/engine/remote_test.go
+++ b/backend/app/store/engine/remote_test.go
@@ -9,10 +9,10 @@ import (
"testing"
"time"
+ "github.com/go-pkgz/jrpc"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- "github.com/umputun/remark/backend/app/rpc"
"github.com/umputun/remark/backend/app/store"
)
@@ -20,7 +20,7 @@ func TestRemote_Create(t *testing.T) {
ts := testServer(t, `{"method":"store.create","params":{"id":"123","pid":"","text":"msg","user":{"name":"","id":"","picture":"","admin":false},"locator":{"site":"site","url":"http://example.com/url"},"score":0,"vote":0,"time":"0001-01-01T00:00:00Z"},"id":1}`,
`{"result":"12345","id":1}`)
defer ts.Close()
- c := RPC{Client: rpc.Client{API: ts.URL, Client: http.Client{}}}
+ c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
var eng Interface = &c
_ = eng
@@ -35,7 +35,7 @@ func TestRemote_Create(t *testing.T) {
func TestRemote_Get(t *testing.T) {
ts := testServer(t, `{"method":"store.get","params":{"locator":{"url":"http://example.com/url"},"comment_id":"site"},"id":1}`, `{"result":{"id":"123","pid":"","text":"msg","delete":true}}`)
defer ts.Close()
- c := RPC{Client: rpc.Client{API: ts.URL, Client: http.Client{}}}
+ c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
req := GetRequest{Locator: store.Locator{URL: "http://example.com/url"}, CommentID: "site"}
res, err := c.Get(req)
@@ -47,7 +47,7 @@ func TestRemote_Get(t *testing.T) {
func TestRemote_GetWithErrorResult(t *testing.T) {
ts := testServer(t, `{"method":"store.get","params":{"locator":{"url":"http://example.com/url"},"comment_id":"site"},"id":1}`, `{"error":"failed"}`)
defer ts.Close()
- c := RPC{Client: rpc.Client{API: ts.URL, Client: http.Client{}}}
+ c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
req := GetRequest{Locator: store.Locator{URL: "http://example.com/url"}, CommentID: "site"}
_, err := c.Get(req)
@@ -57,7 +57,7 @@ func TestRemote_GetWithErrorResult(t *testing.T) {
func TestRemote_GetWithErrorDecode(t *testing.T) {
ts := testServer(t, `{"method":"store.get","params":{"locator":{"url":"http://example.com/url"},"comment_id":"site"},"id":1}`, ``)
defer ts.Close()
- c := RPC{Client: rpc.Client{API: ts.URL, Client: http.Client{}}}
+ c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
req := GetRequest{Locator: store.Locator{URL: "http://example.com/url"}, CommentID: "site"}
_, err := c.Get(req)
@@ -65,7 +65,7 @@ func TestRemote_GetWithErrorDecode(t *testing.T) {
}
func TestRemote_GetWithErrorRemote(t *testing.T) {
- c := RPC{Client: rpc.Client{API: "http://127.0.0.2", Client: http.Client{Timeout: 10 * time.Millisecond}}}
+ c := RPC{Client: jrpc.Client{API: "http://127.0.0.2", Client: http.Client{Timeout: 10 * time.Millisecond}}}
req := GetRequest{Locator: store.Locator{URL: "http://example.com/url"}, CommentID: "site"}
_, err := c.Get(req)
@@ -81,7 +81,7 @@ func TestRemote_FailedStatus(t *testing.T) {
w.WriteHeader(400)
}))
defer ts.Close()
- c := RPC{Client: rpc.Client{API: ts.URL, Client: http.Client{}}}
+ c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
req := GetRequest{Locator: store.Locator{URL: "http://example.com/url"}, CommentID: "site"}
_, err := c.Get(req)
@@ -91,7 +91,7 @@ func TestRemote_FailedStatus(t *testing.T) {
func TestRemote_Update(t *testing.T) {
ts := testServer(t, `{"method":"store.update","params":{"id":"123","pid":"","text":"msg","user":{"name":"","id":"","picture":"","admin":false},"locator":{"site":"site123","url":"http://example.com/url"},"score":0,"vote":0,"time":"0001-01-01T00:00:00Z"},"id":1}`, `{}`)
defer ts.Close()
- c := RPC{Client: rpc.Client{API: ts.URL, Client: http.Client{}}}
+ c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
err := c.Update(store.Comment{ID: "123", Locator: store.Locator{URL: "http://example.com/url", SiteID: "site123"},
Text: "msg"})
@@ -102,7 +102,7 @@ func TestRemote_Update(t *testing.T) {
func TestRemote_Find(t *testing.T) {
ts := testServer(t, `{"method":"store.find","params":{"locator":{"url":"http://example.com/url"},"sort":"-time","since":"0001-01-01T00:00:00Z","limit":10},"id":1}`, `{"result":[{"text":"1"},{"text":"2"}]}`)
defer ts.Close()
- c := RPC{Client: rpc.Client{API: ts.URL, Client: http.Client{}}}
+ c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
res, err := c.Find(FindRequest{Locator: store.Locator{URL: "http://example.com/url"}, Sort: "-time", Limit: 10})
assert.NoError(t, err)
@@ -112,7 +112,7 @@ func TestRemote_Find(t *testing.T) {
func TestRemote_Info(t *testing.T) {
ts := testServer(t, `{"method":"store.info","params":{"locator":{"url":"http://example.com/url"},"limit":10,"skip":5,"ro_age":10},"id":1}`, `{"result":[{"url":"u1","count":22},{"url":"u2","count":33}]}`)
defer ts.Close()
- c := RPC{Client: rpc.Client{API: ts.URL, Client: http.Client{}}}
+ c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
res, err := c.Info(InfoRequest{Locator: store.Locator{URL: "http://example.com/url"},
Limit: 10, Skip: 5, ReadOnlyAge: 10})
@@ -123,7 +123,7 @@ func TestRemote_Info(t *testing.T) {
func TestRemote_Flag(t *testing.T) {
ts := testServer(t, `{"method":"store.flag","params":{"flag":"verified","locator":{"url":"http://example.com/url"}},"id":1}`, `{"result":false}`)
defer ts.Close()
- c := RPC{Client: rpc.Client{API: ts.URL, Client: http.Client{}}}
+ c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
res, err := c.Flag(FlagRequest{Locator: store.Locator{URL: "http://example.com/url"}, Flag: Verified})
assert.NoError(t, err)
@@ -133,7 +133,7 @@ func TestRemote_Flag(t *testing.T) {
func TestRemote_ListFlag(t *testing.T) {
ts := testServer(t, `{"method":"store.list_flags","params":{"flag":"blocked","locator":{"site":"site_id","url":""}},"id":1}`, `{"result":[{"ID":"id1"},{"ID":"id2"}]}`)
defer ts.Close()
- c := RPC{Client: rpc.Client{API: ts.URL, Client: http.Client{}}}
+ c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
res, err := c.ListFlags(FlagRequest{Locator: store.Locator{SiteID: "site_id"}, Flag: Blocked})
assert.NoError(t, err)
assert.Equal(t, []interface{}{map[string]interface{}{"ID": "id1"}, map[string]interface{}{"ID": "id2"}}, res)
@@ -142,7 +142,7 @@ func TestRemote_ListFlag(t *testing.T) {
func TestRemote_Count(t *testing.T) {
ts := testServer(t, `{"method":"store.count","params":{"locator":{"url":"http://example.com/url"},"since":"0001-01-01T00:00:00Z"},"id":1}`, `{"result":11}`)
defer ts.Close()
- c := RPC{Client: rpc.Client{API: ts.URL, Client: http.Client{}}}
+ c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
res, err := c.Count(FindRequest{Locator: store.Locator{URL: "http://example.com/url"}})
assert.NoError(t, err)
@@ -153,7 +153,7 @@ func TestRemote_Delete(t *testing.T) {
ts := testServer(t, `{"method":"store.delete","params":{"locator":{"url":"http://example.com/url"},"del_mode":0},"id":1}`,
`{}`)
defer ts.Close()
- c := RPC{Client: rpc.Client{API: ts.URL, Client: http.Client{}}}
+ c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
err := c.Delete(DeleteRequest{Locator: store.Locator{URL: "http://example.com/url"}})
assert.NoError(t, err)
@@ -162,7 +162,7 @@ func TestRemote_Delete(t *testing.T) {
func TestRemote_Close(t *testing.T) {
ts := testServer(t, `{"method":"store.close","id":1}`, `{}`)
defer ts.Close()
- c := RPC{Client: rpc.Client{API: ts.URL, Client: http.Client{}}}
+ c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
err := c.Close()
assert.NoError(t, err)
}
diff --git a/backend/go.mod b/backend/go.mod
index f8fdac22..4b78ea1f 100644
--- a/backend/go.mod
+++ b/backend/go.mod
@@ -9,12 +9,13 @@ require (
github.com/PuerkitoBio/goquery v1.5.0
github.com/coreos/bbolt v1.3.3
github.com/dgrijalva/jwt-go v3.2.0+incompatible
- github.com/didip/tollbooth v4.0.0+incompatible
+ github.com/didip/tollbooth v4.0.2+incompatible
github.com/didip/tollbooth_chi v0.0.0-20170928041846-6ab5f3083f3d
github.com/go-chi/chi v4.0.2+incompatible
github.com/go-chi/cors v1.0.0
github.com/go-chi/render v1.0.1
github.com/go-pkgz/auth v0.7.2
+ github.com/go-pkgz/jrpc v0.1.0
github.com/go-pkgz/lcw v0.3.1
github.com/go-pkgz/lgr v0.6.3
github.com/go-pkgz/repeater v1.1.2
@@ -35,7 +36,7 @@ require (
github.com/stretchr/testify v1.3.0
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9
- golang.org/x/net v0.0.0-20190628185345-da137c7871d7
+ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect
gopkg.in/russross/blackfriday.v2 v2.0.1
)
diff --git a/backend/go.sum b/backend/go.sum
index 56a7f941..2936f18e 100644
--- a/backend/go.sum
+++ b/backend/go.sum
@@ -21,6 +21,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/didip/tollbooth v4.0.0+incompatible h1:ayQZYuF5QOxx3NdYRNuRVFLv9/2b64JtSUlewb+0TMo=
github.com/didip/tollbooth v4.0.0+incompatible/go.mod h1:A9b0665CE6l1KmzpDws2++elm/CsuWBMa5Jv4WY0PEY=
+github.com/didip/tollbooth v4.0.2+incompatible h1:fVSa33JzSz0hoh2NxpwZtksAzAgd7zjmGO20HCZtF4M=
+github.com/didip/tollbooth v4.0.2+incompatible/go.mod h1:A9b0665CE6l1KmzpDws2++elm/CsuWBMa5Jv4WY0PEY=
github.com/didip/tollbooth_chi v0.0.0-20170928041846-6ab5f3083f3d h1:vs5Nf6IE0N/PwGJ8//zRed4gpCdcr99K2HzX7RuLOQ8=
github.com/didip/tollbooth_chi v0.0.0-20170928041846-6ab5f3083f3d/go.mod h1:YWyIfq3y4ArRfWZ9XksmuusP+7Mad+T0iFZ0kv0XG/M=
github.com/globalsign/mgo v0.0.0-20180615134936-113d3961e731/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
@@ -34,6 +36,8 @@ github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
github.com/go-pkgz/auth v0.7.2 h1:+LvAgqwQtYuWphpZE8qLtspVd65+VgreJkFLsKrtmmk=
github.com/go-pkgz/auth v0.7.2/go.mod h1:ibOpZYISiaOvAHe2bsKj2s3v4AkMam2WxxIFn+zhulo=
+github.com/go-pkgz/jrpc v0.1.0 h1:hNg/IyfEqJcSWOKkuHw0ZwcuGc9TDp7QZREsD2ycmiM=
+github.com/go-pkgz/jrpc v0.1.0/go.mod h1:JxZsvoBklA50DNhELVJnJ567Rt+KrMH9rR3u515wvE8=
github.com/go-pkgz/lcw v0.3.1 h1:PhfB0xNUawLMlx5rXvOTIc7d5LMrr1GM9vIzmG96aUI=
github.com/go-pkgz/lcw v0.3.1/go.mod h1:k+PY1CkCMTLXILtFoJOyK65Qqi9rkoTYunFH1vE/C0I=
github.com/go-pkgz/lgr v0.2.2/go.mod h1:hBM1NM/SoYdlrykgdgJWGrZ/TM/XaZIjRbJfx7NkMm8=
@@ -115,6 +119,7 @@ github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/umputun/remark v1.4.0 h1:rJf4ndpvRDS7tOrtIpVkacItTxF5pVpXvDqtb1dX40M=
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -153,6 +158,8 @@ golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
diff --git a/backend/vendor/github.com/didip/tollbooth/README.md b/backend/vendor/github.com/didip/tollbooth/README.md
index d1971b84..f6145879 100644
--- a/backend/vendor/github.com/didip/tollbooth/README.md
+++ b/backend/vendor/github.com/didip/tollbooth/README.md
@@ -7,9 +7,7 @@ This is a generic middleware to rate-limit HTTP requests.
**NOTE 1:** This library is considered finished.
-**NOTE 2:** In the coming weeks, I will be removing thirdparty modules and moving them to their own dedicated repos.
-
-**NOTE 3:** Major version changes are backward-incompatible. `v2.0.0` streamlines the ugliness of the old API.
+**NOTE 2:** Major version changes are backward-incompatible. `v2.0.0` streamlines the ugliness of the old API.
## Versions
@@ -21,7 +19,7 @@ This is a generic middleware to rate-limit HTTP requests.
**v3.x.x:** Apparently we have been using golang.org/x/time/rate incorrectly. See issue #48. It always limit X number per 1 second. The time duration is not changeable, so it does not make sense to pass TTL to tollbooth.
-## Five Minutes Tutorial
+## Five Minute Tutorial
```go
package main
@@ -161,4 +159,6 @@ Sometimes, other frameworks require a little bit of shim to use Tollbooth. These
* [Stopwatch](https://github.com/didip/stopwatch): A small library to measure latency of things. Useful if you want to report latency data to Graphite.
+* [LaborUnion](https://github.com/didip/laborunion): A dynamic worker pool library.
+
* [Gomet](https://github.com/didip/gomet): Simple HTTP client & server long poll library for Go. Useful for receiving live updates without needing Websocket.
diff --git a/backend/vendor/github.com/didip/tollbooth/tollbooth.go b/backend/vendor/github.com/didip/tollbooth/tollbooth.go
index c023db26..b4f7b3e8 100644
--- a/backend/vendor/github.com/didip/tollbooth/tollbooth.go
+++ b/backend/vendor/github.com/didip/tollbooth/tollbooth.go
@@ -6,10 +6,11 @@ import (
"strings"
"fmt"
+ "math"
+
"github.com/didip/tollbooth/errors"
"github.com/didip/tollbooth/libstring"
"github.com/didip/tollbooth/limiter"
- "math"
)
// setResponseHeaders configures X-Rate-Limit-Limit and X-Rate-Limit-Duration
@@ -58,18 +59,21 @@ func BuildKeys(lmt *limiter.Limiter, r *http.Request) [][]string {
if libstring.StringInSlice(lmtMethods, r.Method) {
for headerKey, headerValues := range lmtHeaders {
if (headerValues == nil || len(headerValues) <= 0) && r.Header.Get(headerKey) != "" {
- // If header values are empty, rate-limit all request with headerKey.
+ // If header values are empty, rate-limit all request containing headerKey.
username, _, ok := r.BasicAuth()
if ok && libstring.StringInSlice(lmtBasicAuthUsers, username) {
- sliceKeys = append(sliceKeys, []string{remoteIP, path, r.Method, headerKey, username})
+ sliceKeys = append(sliceKeys, []string{remoteIP, path, r.Method, headerKey, r.Header.Get(headerKey), username})
}
} else if len(headerValues) > 0 && r.Header.Get(headerKey) != "" {
// If header values are not empty, rate-limit all request with headerKey and headerValues.
for _, headerValue := range headerValues {
- username, _, ok := r.BasicAuth()
- if ok && libstring.StringInSlice(lmtBasicAuthUsers, username) {
- sliceKeys = append(sliceKeys, []string{remoteIP, path, r.Method, headerKey, headerValue, username})
+ if r.Header.Get(headerKey) == headerValue {
+ username, _, ok := r.BasicAuth()
+ if ok && libstring.StringInSlice(lmtBasicAuthUsers, username) {
+ sliceKeys = append(sliceKeys, []string{remoteIP, path, r.Method, headerKey, headerValue, username})
+ }
+ break
}
}
}
@@ -82,12 +86,15 @@ func BuildKeys(lmt *limiter.Limiter, r *http.Request) [][]string {
for headerKey, headerValues := range lmtHeaders {
if (headerValues == nil || len(headerValues) <= 0) && r.Header.Get(headerKey) != "" {
// If header values are empty, rate-limit all request with headerKey.
- sliceKeys = append(sliceKeys, []string{remoteIP, path, r.Method, headerKey})
+ sliceKeys = append(sliceKeys, []string{remoteIP, path, r.Method, headerKey, r.Header.Get(headerKey)})
} else if len(headerValues) > 0 && r.Header.Get(headerKey) != "" {
- // If header values are not empty, rate-limit all request with headerKey and headerValues.
+ // We are only limiting if request's header value is defined inside `headerValues`.
for _, headerValue := range headerValues {
- sliceKeys = append(sliceKeys, []string{remoteIP, path, r.Method, headerKey, headerValue})
+ if r.Header.Get(headerKey) == headerValue {
+ sliceKeys = append(sliceKeys, []string{remoteIP, path, r.Method, headerKey, headerValue})
+ break
+ }
}
}
}
@@ -113,12 +120,15 @@ func BuildKeys(lmt *limiter.Limiter, r *http.Request) [][]string {
for headerKey, headerValues := range lmtHeaders {
if (headerValues == nil || len(headerValues) <= 0) && r.Header.Get(headerKey) != "" {
// If header values are empty, rate-limit all request with headerKey.
- sliceKeys = append(sliceKeys, []string{remoteIP, path, headerKey})
+ sliceKeys = append(sliceKeys, []string{remoteIP, path, headerKey, r.Header.Get(headerKey)})
} else if len(headerValues) > 0 && r.Header.Get(headerKey) != "" {
// If header values are not empty, rate-limit all request with headerKey and headerValues.
for _, headerValue := range headerValues {
- sliceKeys = append(sliceKeys, []string{remoteIP, path, headerKey, headerValue})
+ if r.Header.Get(headerKey) == headerValue {
+ sliceKeys = append(sliceKeys, []string{remoteIP, path, headerKey, headerValue})
+ break
+ }
}
}
}
diff --git a/backend/vendor/github.com/go-pkgz/jrpc/.gitignore b/backend/vendor/github.com/go-pkgz/jrpc/.gitignore
new file mode 100644
index 00000000..f1c181ec
--- /dev/null
+++ b/backend/vendor/github.com/go-pkgz/jrpc/.gitignore
@@ -0,0 +1,12 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
diff --git a/backend/vendor/github.com/go-pkgz/jrpc/.golangci.yml b/backend/vendor/github.com/go-pkgz/jrpc/.golangci.yml
new file mode 100644
index 00000000..bb83e9ae
--- /dev/null
+++ b/backend/vendor/github.com/go-pkgz/jrpc/.golangci.yml
@@ -0,0 +1,70 @@
+linters-settings:
+ govet:
+ check-shadowing: true
+ golint:
+ min-confidence: 0
+ gocyclo:
+ min-complexity: 15
+ maligned:
+ suggest-new: true
+ dupl:
+ threshold: 100
+ goconst:
+ min-len: 2
+ min-occurrences: 2
+ misspell:
+ locale: US
+ lll:
+ line-length: 140
+ gocritic:
+ enabled-tags:
+ - performance
+ - style
+ - experimental
+ disabled-checks:
+ - wrapperFunc
+
+linters:
+ disable-all: true
+ enable:
+ - megacheck
+ - golint
+ - govet
+ - unconvert
+ - megacheck
+ - structcheck
+ - gas
+ - gocyclo
+ - dupl
+ - misspell
+ - unparam
+ - varcheck
+ - deadcode
+ - typecheck
+ - ineffassign
+ - varcheck
+ - stylecheck
+ - gochecknoinits
+ - scopelint
+ - gocritic
+ - golint
+ - nakedret
+ - gosimple
+ - prealloc
+ fast: false
+
+
+run:
+# modules-download-mode: vendor
+ skip-dirs:
+ - vendor
+ tests: true
+
+issues:
+ exclude-rules:
+ - text: "weak cryptographic primitive"
+ linters:
+ - gosec
+
+service:
+ golangci-lint-version: 1.17.x
\ No newline at end of file
diff --git a/backend/vendor/github.com/go-pkgz/jrpc/.travis.yml b/backend/vendor/github.com/go-pkgz/jrpc/.travis.yml
new file mode 100644
index 00000000..0f0c5bdc
--- /dev/null
+++ b/backend/vendor/github.com/go-pkgz/jrpc/.travis.yml
@@ -0,0 +1,21 @@
+language: go
+
+go:
+ - "1.12.x"
+
+install: true
+
+before_install:
+ - export TZ=America/Chicago
+ - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.17.1
+ - go get github.com/mattn/goveralls
+ - export PATH=$(pwd)/bin:$PATH
+
+script:
+ - GO111MODULE=on go get ./...
+ - GO111MODULE=on go mod vendor
+ - GO111MODULE=on go test -v -mod=vendor -covermode=count -coverprofile=profile.cov ./... || travis_terminate 1;
+ - GO111MODULE=on go test -v -covermode=count -coverprofile=profile.cov ./... || travis_terminate 1;
+ - golangci-lint run -v || travis_terminate 1;
+ - $GOPATH/bin/goveralls -coverprofile=profile.cov -service=travis-ci
+
diff --git a/backend/vendor/github.com/go-pkgz/jrpc/LICENSE b/backend/vendor/github.com/go-pkgz/jrpc/LICENSE
new file mode 100644
index 00000000..ac540250
--- /dev/null
+++ b/backend/vendor/github.com/go-pkgz/jrpc/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Umputun
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/backend/vendor/github.com/go-pkgz/jrpc/README.md b/backend/vendor/github.com/go-pkgz/jrpc/README.md
new file mode 100644
index 00000000..a3e36c9b
--- /dev/null
+++ b/backend/vendor/github.com/go-pkgz/jrpc/README.md
@@ -0,0 +1,99 @@
+# jrpc - rpc with json [](https://travis-ci.org/go-pkgz/jrpc) [](https://goreportcard.com/report/github.com/go-pkgz/jrpc) [](https://coveralls.io/github/go-pkgz/jrpc?branch=master) [](https://godoc.org/github.com/go-pkgz/jrpc)
+
+jrpc library provides client and server for RPC-like communication over HTTP with json encoded messages.
+The protocol is a somewhat simplified version of json-rpc with a single POST call sending Request json
+(method name and the list of parameters) moreover, receiving json Response with result data and an error string.
+
+## Usage
+
+### Plugin (server)
+
+```go
+// Server wraps jrpc.Server and adds synced map to store data
+type Puglin struct {
+ *jrpc.Server
+}
+
+// create plugin (jrpc server)
+plugin := jrpcServer{
+ Server: &jrpc.Server{
+ API: "/command", // base url for rpc calls
+ AuthUser: "user", // basic auth user name
+ AuthPasswd: "password", // basic auth password
+ AppName: "jrpc-example", // plugin name for headers
+ Logger: logger,
+ },
+}
+
+plugin.Add("mycommand", func(id uint64, params json.RawMessage) Response {
+ return jrpc.EncodeResponse(id, "hello, it works", nil)
+})
+```
+
+### Application (client)
+
+```go
+// Client makes jrpc.Client and invoke remote call
+rpcClient := jrpc.Client{
+ API: "http://127.0.0.1:8080/command",
+ Client: http.Client{},
+ AuthUser: "user",
+ AuthPasswd: "password",
+}
+
+resp, err := rpcClient.Call("mycommand")
+var message string
+if err = json.Unmarshal(*resp.Result, &message); err != nil {
+ panic(err)
+}
+```
+
+*for functional examples for both plugin and application see [_example](https://github.com/go-pkgz/jrpc/tree/master/_example)*
+
+## Technical details
+
+ * `jrpc.Server` runs on user-defined port as a regular http server
+ * Server accepts a single POST request on user-defined url with [Request](https://github.com/go-pkgz/jrpc/blob/master/jrpc.go#L12) sent as json payload
+ request details and an example:
+
+ ```go
+ type Request struct {
+ Method string `json:"method"`
+ Params interface{} `json:"params,omitempty"`
+ ID uint64 `json:"id"`
+ }
+ ```
+ example:
+
+ ```json
+ {
+ "method":"test",
+ "params":[123,"abc"],
+ "id":1
+ }
+ ```
+
+* Params can be a struct, primitive type or slice of values, even with different types.
+* Server defines `ServerFn` handler function to react on a POST request. The handler provided by the user.
+* Communication between the server and the caller can be protected with basic auth.
+* [Client](https://github.com/go-pkgz/jrpc/blob/master/client.go) provides a single method `Call` and return `Response`
+
+ response details:
+
+ ```go
+ // Response encloses result and error received from remote server
+ type Response struct {
+ Result *json.RawMessage `json:"result,omitempty"`
+ Error string `json:"error,omitempty"`
+ ID uint64 `json:"id"`
+ }
+ ```
+
+* User should encode and decode json payloads on the application level, see provided [examples](https://github.com/go-pkgz/jrpc/tree/master/_example)
+* `jrpc.Server` doesn't support https internally (yet). If used on exposed or non-private networks, should be proxied with something providing https termination (nginx and others).
+
+## Status
+
+The code was extracted from [remark42](https://github.com/umputun/remark) and still under development. Until v1.x released the
+ API & protocol may change.
+
\ No newline at end of file
diff --git a/backend/app/rpc/client.go b/backend/vendor/github.com/go-pkgz/jrpc/client.go
similarity index 65%
rename from backend/app/rpc/client.go
rename to backend/vendor/github.com/go-pkgz/jrpc/client.go
index 5cef2a16..b9a40573 100644
--- a/backend/app/rpc/client.go
+++ b/backend/vendor/github.com/go-pkgz/jrpc/client.go
@@ -1,26 +1,29 @@
-package rpc
+package jrpc
import (
"bytes"
"encoding/json"
"net/http"
- "reflect"
"sync/atomic"
"github.com/pkg/errors"
)
// Client implements remote engine and delegates all calls to remote http server
+// if AuthUser and AuthPasswd defined will be used for basic auth in each call to server
type Client struct {
- API string
- Client http.Client
- AuthUser string
- AuthPasswd string
+ API string // URL to jrpc server with entrypoint, i.e. http://127.0.0.1:8080/command
+ Client http.Client // http client injected by user
+ AuthUser string // basic auth user name, should match Server.AuthUser, optional
+ AuthPasswd string // basic auth password, should match Server.AuthPasswd, optional
- id uint64
+ id uint64 // used with atomic to populate unique id to Request.ID
}
-// Call remote server with given method and arguments
+// Call remote server with given method and arguments.
+// Empty args will be ignored, single arg will be marshaled as-us and multiple args marshaled as []interface{}.
+// Returns Response and error. Note: Response has it's own Error field, but that onw controlled by server.
+// Returned error represent client-level errors, like failed http call, failed marshaling and so on.
func (r *Client) Call(method string, args ...interface{}) (*Response, error) {
var b []byte
@@ -32,7 +35,7 @@ func (r *Client) Call(method string, args ...interface{}) (*Response, error) {
if err != nil {
return nil, errors.Wrapf(err, "marshaling failed for %s", method)
}
- case len(args) == 1 && reflect.TypeOf(args[0]).Kind() == reflect.Struct:
+ case len(args) == 1:
b, err = json.Marshal(Request{Method: method, Params: args[0], ID: atomic.AddUint64(&r.id, 1)})
if err != nil {
return nil, errors.Wrapf(err, "marshaling failed for %s", method)
diff --git a/backend/vendor/github.com/go-pkgz/jrpc/go.mod b/backend/vendor/github.com/go-pkgz/jrpc/go.mod
new file mode 100644
index 00000000..1e3fdb2a
--- /dev/null
+++ b/backend/vendor/github.com/go-pkgz/jrpc/go.mod
@@ -0,0 +1,16 @@
+module github.com/go-pkgz/jrpc
+
+go 1.12
+
+require (
+ github.com/didip/tollbooth v4.0.2+incompatible
+ github.com/didip/tollbooth_chi v0.0.0-20170928041846-6ab5f3083f3d
+ github.com/go-chi/chi v4.0.2+incompatible
+ github.com/go-chi/render v1.0.1
+ github.com/go-pkgz/rest v1.4.1
+ github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
+ github.com/pkg/errors v0.8.1
+ github.com/stretchr/testify v1.3.0
+ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 // indirect
+ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
+)
diff --git a/backend/vendor/github.com/go-pkgz/jrpc/go.sum b/backend/vendor/github.com/go-pkgz/jrpc/go.sum
new file mode 100644
index 00000000..9a20171c
--- /dev/null
+++ b/backend/vendor/github.com/go-pkgz/jrpc/go.sum
@@ -0,0 +1,32 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/didip/tollbooth v4.0.2+incompatible h1:fVSa33JzSz0hoh2NxpwZtksAzAgd7zjmGO20HCZtF4M=
+github.com/didip/tollbooth v4.0.2+incompatible/go.mod h1:A9b0665CE6l1KmzpDws2++elm/CsuWBMa5Jv4WY0PEY=
+github.com/didip/tollbooth_chi v0.0.0-20170928041846-6ab5f3083f3d h1:vs5Nf6IE0N/PwGJ8//zRed4gpCdcr99K2HzX7RuLOQ8=
+github.com/didip/tollbooth_chi v0.0.0-20170928041846-6ab5f3083f3d/go.mod h1:YWyIfq3y4ArRfWZ9XksmuusP+7Mad+T0iFZ0kv0XG/M=
+github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs=
+github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
+github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
+github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
+github.com/go-pkgz/rest v1.4.1 h1:DmaVLPH2O7yLehrWOW0uz01d2mVHz9fBR/iuTiPRzaw=
+github.com/go-pkgz/rest v1.4.1/go.mod h1:COazNj35u3RXAgQNBr6neR599tYP3URiOpsu9p0rOtk=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
+github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
diff --git a/backend/vendor/github.com/go-pkgz/jrpc/jrpc.go b/backend/vendor/github.com/go-pkgz/jrpc/jrpc.go
new file mode 100644
index 00000000..86063690
--- /dev/null
+++ b/backend/vendor/github.com/go-pkgz/jrpc/jrpc.go
@@ -0,0 +1,40 @@
+// Package jrpc implements client and server for RPC-like communication over HTTP with json encoded messages.
+// The protocol is somewhat simplified version of json-rpc with a single POST call sending Request json
+// (method name and the list of parameters) and receiving back json Response with "result" json
+// and error string
+package jrpc
+
+import (
+ "encoding/json"
+)
+
+// Request encloses method name and all params
+type Request struct {
+ Method string `json:"method"` // method (function) name
+ Params interface{} `json:"params,omitempty"` // function arguments
+ ID uint64 `json:"id"` // unique call id
+}
+
+// Response encloses result and error received from remote server
+type Response struct {
+ Result *json.RawMessage `json:"result,omitempty"` // response json
+ Error string `json:"error,omitempty"` // optional remote (server side / plugin side) error
+ ID uint64 `json:"id"` // unique call id, echoed Request.ID to allow calls tracing
+}
+
+// EncodeResponse convert anything (type interface{}) and incoming error (if any) to Response
+func EncodeResponse(id uint64, resp interface{}, e error) Response {
+ v, err := json.Marshal(&resp)
+ if err != nil {
+ return Response{Error: err.Error()}
+ }
+ if e != nil {
+ return Response{ID: id, Result: nil, Error: e.Error()} // pass input error
+ }
+ raw := json.RawMessage{}
+ if err := raw.UnmarshalJSON(v); err != nil {
+ return Response{Error: err.Error()}
+ }
+
+ return Response{ID: id, Result: &raw}
+}
diff --git a/backend/app/rpc/server.go b/backend/vendor/github.com/go-pkgz/jrpc/server.go
similarity index 58%
rename from backend/app/rpc/server.go
rename to backend/vendor/github.com/go-pkgz/jrpc/server.go
index 55d95866..a52e1434 100644
--- a/backend/app/rpc/server.go
+++ b/backend/vendor/github.com/go-pkgz/jrpc/server.go
@@ -1,4 +1,4 @@
-package rpc
+package jrpc
import (
"context"
@@ -13,21 +13,19 @@ import (
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/render"
- log "github.com/go-pkgz/lgr"
- R "github.com/go-pkgz/rest"
+ "github.com/go-pkgz/rest"
"github.com/go-pkgz/rest/logger"
"github.com/pkg/errors"
-
- "github.com/umputun/remark/backend/app/rest"
)
// Server is json-rpc server with an optional basic auth
type Server struct {
- API string
- AuthUser string
- AuthPasswd string
- Version string
- AppName string
+ API string // url path, i.e. "/command" or "/rpc" etc.
+ AuthUser string // basic auth user name, should match Client.AuthUser, optional
+ AuthPasswd string // basic auth password, should match Client.AuthPasswd, optional
+ Version string // server version, injected from main and used for informational headers only
+ AppName string // plugin name, injected from main and used for informational headers only
+ Logger L // logger, if nil will default to NoOpLogger
funcs struct {
m map[string]ServerFn
@@ -40,26 +38,26 @@ type Server struct {
}
}
-// Encoder is a function to encode call's result to Response
-type Encoder func(id uint64, resp interface{}, e error) (Response, error)
-
-// ServerFn handler registered for each method with Add
-// Implementations provided by consumer and define response logic.
+// ServerFn handler registered for each method with Add or Group.
+// Implementations provided by consumer and defines response logic.
type ServerFn func(id uint64, params json.RawMessage) Response
// Run http server on given port
func (s *Server) Run(port int) error {
+ if s.Logger == nil {
+ s.Logger = NoOpLogger
+ }
if s.AuthUser == "" || s.AuthPasswd == "" {
- log.Print("[WARN] extension server runs without auth")
+ s.Logger.Logf("[WARN] extension server runs without auth")
}
if s.funcs.m == nil && len(s.funcs.m) == 0 {
return errors.Errorf("nothing mapped for dispatch, Add has to be called prior to Run")
}
router := chi.NewRouter()
- router.Use(middleware.Throttle(1000), middleware.RealIP, R.Recoverer(log.Default()))
- router.Use(R.AppInfo(s.AppName, "umputun", s.Version), R.Ping)
- logInfoWithBody := logger.New(logger.Log(log.Default()), logger.WithBody, logger.Prefix("[INFO]")).Handler
+ router.Use(middleware.Throttle(1000), middleware.RealIP, rest.Recoverer(s.Logger))
+ router.Use(rest.AppInfo(s.AppName, "umputun", s.Version), rest.Ping)
+ logInfoWithBody := logger.New(logger.Log(s.Logger), logger.WithBody, logger.Prefix("[INFO]")).Handler
router.Use(middleware.Timeout(5 * time.Second))
router.Use(logInfoWithBody, tollbooth_chi.LimitHandler(tollbooth.NewLimiter(1000, nil)), middleware.NoCache)
router.Use(s.basicAuth)
@@ -76,26 +74,10 @@ func (s *Server) Run(port int) error {
}
s.httpServer.Unlock()
- log.Printf("[INFO] listen on %d", port)
+ s.Logger.Logf("[INFO] listen on %d", port)
return s.httpServer.ListenAndServe()
}
-// EncodeResponse convert anything to Response
-func (s *Server) EncodeResponse(id uint64, resp interface{}, e error) (Response, error) {
- v, err := json.Marshal(&resp)
- if err != nil {
- return Response{}, err
- }
- if e != nil {
- return Response{ID: id, Result: nil, Error: e.Error()}, nil
- }
- raw := json.RawMessage{}
- if err = raw.UnmarshalJSON(v); err != nil {
- return Response{}, err
- }
- return Response{ID: id, Result: &raw}, nil
-}
-
// Shutdown http server
func (s *Server) Shutdown() error {
s.httpServer.Lock()
@@ -108,12 +90,12 @@ func (s *Server) Shutdown() error {
return s.httpServer.Shutdown(ctx)
}
-// Add method handler
+// Add method handler. Handler will be called on matching method (Request.Method)
func (s *Server) Add(method string, fn ServerFn) {
s.httpServer.Lock()
defer s.httpServer.Unlock()
if s.httpServer.Server != nil {
- log.Printf("[WARN] ignored method %s, can't be added to activated server", method)
+ s.Logger.Logf("[WARN] ignored method %s, can't be added to activated server", method)
return
}
@@ -122,19 +104,20 @@ func (s *Server) Add(method string, fn ServerFn) {
})
s.funcs.m[method] = fn
- log.Printf("[INFO] add handler for %s", method)
+ s.Logger.Logf("[INFO] add handler for %s", method)
}
// HandlersGroup alias for map of handlers
type HandlersGroup map[string]ServerFn
-// Group of handlers with common prefix
+// Group of handlers with common prefix, match on group.method
func (s *Server) Group(prefix string, m HandlersGroup) {
for k, v := range m {
s.Add(prefix+"."+k, v)
}
}
+// handler is http handler multiplexing calls by req.Method
func (s *Server) handler(w http.ResponseWriter, r *http.Request) {
req := struct {
ID uint64 `json:"id"`
@@ -143,12 +126,12 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) {
}{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
- rest.SendErrorJSON(w, r, http.StatusBadRequest, err, req.Method, 0)
+ rest.SendErrorJSON(w, r, s.Logger, http.StatusBadRequest, err, req.Method)
return
}
fn, ok := s.funcs.m[req.Method]
if !ok {
- rest.SendErrorJSON(w, r, http.StatusNotImplemented, errors.New("unsupported method"), req.Method, 0)
+ rest.SendErrorJSON(w, r, s.Logger, http.StatusNotImplemented, errors.New("unsupported method"), req.Method)
return
}
@@ -160,6 +143,7 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) {
render.JSON(w, r, fn(req.ID, params))
}
+// basicAuth middleware. enabled only if both AuthUser and AuthPasswd defined.
func (s *Server) basicAuth(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -177,3 +161,17 @@ func (s *Server) basicAuth(h http.Handler) http.Handler {
h.ServeHTTP(w, r)
})
}
+
+// L defined logger interface used for an optional rest logging
+type L interface {
+ Logf(format string, args ...interface{})
+}
+
+// LoggerFunc type is an adapter to allow the use of ordinary functions as Logger.
+type LoggerFunc func(format string, args ...interface{})
+
+// Logf calls f(id)
+func (f LoggerFunc) Logf(format string, args ...interface{}) { f(format, args...) }
+
+// NoOpLogger logger does nothing
+var NoOpLogger = LoggerFunc(func(format string, args ...interface{}) {})
diff --git a/backend/vendor/modules.txt b/backend/vendor/modules.txt
index 582f708d..f501d4fb 100644
--- a/backend/vendor/modules.txt
+++ b/backend/vendor/modules.txt
@@ -10,7 +10,7 @@ github.com/coreos/bbolt
github.com/davecgh/go-spew/spew
# github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/dgrijalva/jwt-go
-# github.com/didip/tollbooth v4.0.0+incompatible
+# github.com/didip/tollbooth v4.0.2+incompatible
github.com/didip/tollbooth
github.com/didip/tollbooth/errors
github.com/didip/tollbooth/libstring
@@ -38,6 +38,8 @@ github.com/go-pkgz/auth/provider/sender
github.com/go-pkgz/auth/token
github.com/go-pkgz/auth/logger
github.com/go-pkgz/auth/middleware
+# github.com/go-pkgz/jrpc v0.1.0
+github.com/go-pkgz/jrpc
# github.com/go-pkgz/lcw v0.3.1
github.com/go-pkgz/lcw
# github.com/go-pkgz/lgr v0.6.3
@@ -98,7 +100,7 @@ golang.org/x/crypto/acme
# golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9
golang.org/x/image/draw
golang.org/x/image/math/f64
-# golang.org/x/net v0.0.0-20190628185345-da137c7871d7
+# golang.org/x/net v0.0.0-20190724013045-ca1201d0de80
golang.org/x/net/html
golang.org/x/net/idna
golang.org/x/net/html/atom