surrealpatch/util/fncs/http.go
2017-11-16 20:31:20 +00:00

210 lines
5 KiB
Go

// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fncs
import (
"bytes"
"context"
"errors"
"io"
"io/ioutil"
"net/http"
"time"
"encoding/json"
"golang.org/x/net/context/ctxhttp"
"github.com/abcum/surreal/util/build"
"github.com/abcum/surreal/util/hook"
)
type opts map[string]interface{}
var version = build.GetInfo().Ver
var httpResponseError = errors.New("HTTP response error")
func httpHead(ctx context.Context, args ...interface{}) (interface{}, error) {
return runSync(ctx, "HEAD", args...)
}
func httpGet(ctx context.Context, args ...interface{}) (interface{}, error) {
return runSync(ctx, "GET", args...)
}
func httpPut(ctx context.Context, args ...interface{}) (interface{}, error) {
return runSync(ctx, "PUT", args...)
}
func httpPost(ctx context.Context, args ...interface{}) (interface{}, error) {
return runSync(ctx, "POST", args...)
}
func httpPatch(ctx context.Context, args ...interface{}) (interface{}, error) {
return runSync(ctx, "PATCH", args...)
}
func httpDelete(ctx context.Context, args ...interface{}) (interface{}, error) {
return runSync(ctx, "DELETE", args...)
}
func httpAsyncHead(ctx context.Context, args ...interface{}) (interface{}, error) {
return runAsync(ctx, "HEAD", args...)
}
func httpAsyncGet(ctx context.Context, args ...interface{}) (interface{}, error) {
return runAsync(ctx, "GET", args...)
}
func httpAsyncPut(ctx context.Context, args ...interface{}) (interface{}, error) {
return runAsync(ctx, "PUT", args...)
}
func httpAsyncPost(ctx context.Context, args ...interface{}) (interface{}, error) {
return runAsync(ctx, "POST", args...)
}
func httpAsyncPatch(ctx context.Context, args ...interface{}) (interface{}, error) {
return runAsync(ctx, "PATCH", args...)
}
func httpAsyncDelete(ctx context.Context, args ...interface{}) (interface{}, error) {
return runAsync(ctx, "DELETE", args...)
}
func runSync(ctx context.Context, met string, args ...interface{}) (interface{}, error) {
url, bdy, opt := httpCnf(met, args...)
out, _ := httpRes(ctx, met, url, bdy, opt)
return out, nil
}
func runAsync(ctx context.Context, met string, args ...interface{}) (interface{}, error) {
url, bdy, opt := httpCnf(met, args...)
ctx = context.Background()
go hook.NewBackoff(5, 5, 10*time.Second).Run(ctx, func() error {
return httpErr(httpReq(ctx, met, url, bdy, opt))
})
return nil, nil
}
func httpCnf(met string, args ...interface{}) (url string, bdy io.Reader, opt opts) {
var bit []byte
switch met {
case "HEAD", "GET", "DELETE":
switch len(args) {
case 1:
url, _ = ensureString(args[0])
case 2:
url, _ = ensureString(args[0])
opt, _ = ensureObject(args[1])
}
case "PUT", "POST", "PATCH":
switch len(args) {
case 1:
url, _ = ensureString(args[0])
case 2:
url, _ = ensureString(args[0])
switch v := args[1].(type) {
case []interface{}, map[string]interface{}:
bit, _ = json.Marshal(v)
default:
bit, _ = ensureBytes(v)
}
case 3:
url, _ = ensureString(args[0])
switch v := args[1].(type) {
case []interface{}, map[string]interface{}:
bit, _ = json.Marshal(v)
default:
bit, _ = ensureBytes(v)
}
opt, _ = ensureObject(args[2])
}
}
return url, bytes.NewReader(bit), opt
}
func httpReq(ctx context.Context, met, url string, body io.Reader, conf opts) (*http.Response, error) {
cli := new(http.Client)
req, err := http.NewRequest(met, url, body)
if err != nil {
return nil, err
}
req.Header.Set("User-Agent", "SurrealDB HTTP/"+version)
if val, ok := conf["auth"]; ok {
if opt, ok := ensureObject(val); ok {
user, _ := ensureString(opt["user"])
pass, _ := ensureString(opt["pass"])
req.SetBasicAuth(user, pass)
}
}
if val, ok := conf["head"]; ok {
if opt, ok := ensureObject(val); ok {
for key, v := range opt {
head, _ := ensureString(v)
req.Header.Set(key, head)
}
}
}
res, err := ctxhttp.Do(ctx, cli, req)
if err != nil {
return nil, err
}
return res, nil
}
func httpRes(ctx context.Context, met, url string, body io.Reader, conf opts) (interface{}, error) {
var out interface{}
res, err := httpReq(ctx, met, url, body, conf)
if err != nil {
return nil, err
}
bdy, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
err = json.Unmarshal(bdy, &out)
if err != nil {
if len(bdy) != 0 {
return bdy, nil
}
}
return out, nil
}
func httpErr(res *http.Response, err error) error {
if err != nil {
return err
}
if res.StatusCode >= 500 {
return httpResponseError
}
return nil
}