mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-24 00:28:44 +05:00
HttpPoster implementation in CDR replication and actions with call_url, new configuration parameters added: http_failed_dir, replication attempts, fixes #201, Rated bug fix in CDRC, Cost imported via CDRC in templates now
This commit is contained in:
@@ -124,7 +124,8 @@ const (
|
||||
RATED_SUBJECT = "RatedSubject"
|
||||
COST = "Cost"
|
||||
COST_DETAILS = "CostDetails"
|
||||
RATED = "Rated"
|
||||
RATED = "rated"
|
||||
RATED_FLD = "Rated"
|
||||
DEFAULT_RUNID = "*default"
|
||||
META_DEFAULT = "*default"
|
||||
STATIC_VALUE_PREFIX = "^"
|
||||
|
||||
@@ -21,14 +21,33 @@ package utils
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
CONTENT_JSON = "json"
|
||||
CONTENT_FORM = "form"
|
||||
CONTENT_TEXT = "text"
|
||||
)
|
||||
|
||||
// Converts interface to []byte
|
||||
func GetBytes(content interface{}) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
err := enc.Encode(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// Post without automatic failover
|
||||
func HttpJsonPost(url string, skipTlsVerify bool, content interface{}) ([]byte, error) {
|
||||
body, err := json.Marshal(content)
|
||||
@@ -55,8 +74,20 @@ func HttpJsonPost(url string, skipTlsVerify bool, content interface{}) ([]byte,
|
||||
}
|
||||
|
||||
// Post with built-in failover
|
||||
func HttpJsonPoster(url string, skipTlsVerify bool, content interface{}, retries int, fallbackFilePath string) ([]byte, error) {
|
||||
body, err := json.Marshal(content)
|
||||
func HttpPoster(addr string, skipTlsVerify bool, content interface{}, contentType string, attempts int, fallbackFilePath string) ([]byte, error) {
|
||||
var body []byte
|
||||
var urlData url.Values
|
||||
var err error
|
||||
switch contentType {
|
||||
case CONTENT_JSON:
|
||||
body, err = json.Marshal(content)
|
||||
case CONTENT_FORM:
|
||||
urlData = content.(url.Values)
|
||||
case CONTENT_TEXT:
|
||||
body = content.([]byte)
|
||||
default:
|
||||
err = fmt.Errorf("Unsupported ContentType: %s", contentType)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -65,22 +96,31 @@ func HttpJsonPoster(url string, skipTlsVerify bool, content interface{}, retries
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
delay := Fib()
|
||||
for i := 0; i < retries; i++ {
|
||||
resp, err := client.Post(url, "application/json", bytes.NewBuffer(body))
|
||||
bodyType := "application/x-www-form-urlencoded"
|
||||
if contentType == CONTENT_JSON {
|
||||
bodyType = "application/json"
|
||||
}
|
||||
for i := 0; i < attempts; i++ {
|
||||
var resp *http.Response
|
||||
if IsSliceMember([]string{CONTENT_JSON, CONTENT_TEXT}, contentType) {
|
||||
resp, err = client.Post(addr, bodyType, bytes.NewBuffer(body))
|
||||
} else if contentType == CONTENT_FORM {
|
||||
resp, err = client.PostForm(addr, urlData)
|
||||
}
|
||||
if err != nil {
|
||||
Logger.Warning(fmt.Sprintf("<HttpPoster> Posting to : <%s>, error: <%s>", url, err.Error()))
|
||||
Logger.Warning(fmt.Sprintf("<HttpPoster> Posting to : <%s>, error: <%s>", addr, err.Error()))
|
||||
time.Sleep(delay())
|
||||
continue
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
Logger.Warning(fmt.Sprintf("<HttpPoster> Posting to : <%s>, error: <%s>", url, err.Error()))
|
||||
Logger.Warning(fmt.Sprintf("<HttpPoster> Posting to : <%s>, error: <%s>", addr, err.Error()))
|
||||
time.Sleep(delay())
|
||||
continue
|
||||
}
|
||||
if resp.StatusCode > 299 {
|
||||
Logger.Warning(fmt.Sprintf("<HttpPoster> Posting to : <%s>, unexpected status code received: <%d>", url, resp.StatusCode))
|
||||
Logger.Warning(fmt.Sprintf("<HttpPoster> Posting to : <%s>, unexpected status code received: <%d>", addr, resp.StatusCode))
|
||||
time.Sleep(delay())
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
@@ -38,8 +39,8 @@ func TestHttpJsonPoster(t *testing.T) {
|
||||
return
|
||||
}
|
||||
content := &TestContent{Var1: "Val1", Var2: "Val2"}
|
||||
filePath := "/tmp/test_http_poster.cgr"
|
||||
if _, err := HttpJsonPoster("http://localhost:8080/invalid", true, content, 3, filePath); err != nil {
|
||||
filePath := "/tmp/cgr_test_http_poster.json"
|
||||
if _, err := HttpPoster("http://localhost:8080/invalid", true, content, true, 3, filePath); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
jsnContent, _ := json.Marshal(content)
|
||||
@@ -48,4 +49,28 @@ func TestHttpJsonPoster(t *testing.T) {
|
||||
} else if !reflect.DeepEqual(jsnContent, readBytes) {
|
||||
t.Errorf("Expecting: %q, received: %q", string(jsnContent), string(readBytes))
|
||||
}
|
||||
if err := os.Remove(filePath); err != nil {
|
||||
t.Error("Failed removing file: ", filePath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHttpBytesPoster(t *testing.T) {
|
||||
if !*testLocal {
|
||||
return
|
||||
}
|
||||
content := []byte(`Test
|
||||
Test2
|
||||
`)
|
||||
filePath := "/tmp/test_http_poster.http"
|
||||
if _, err := HttpPoster("http://localhost:8080/invalid", true, content, false, 3, filePath); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if readBytes, err := ioutil.ReadFile(filePath); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(content, readBytes) {
|
||||
t.Errorf("Expecting: %q, received: %q", string(content), string(readBytes))
|
||||
}
|
||||
if err := os.Remove(filePath); err != nil {
|
||||
t.Error("Failed removing file: ", filePath)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user