Updated rjReader

This commit is contained in:
Trial97
2019-09-02 16:16:58 +03:00
committed by Dan Christian Bogos
parent 8bf4ea02e5
commit cd4ae357a9
6 changed files with 335 additions and 492 deletions

View File

@@ -19,12 +19,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package config
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
@@ -192,7 +190,8 @@ func NewDefaultCGRConfig() (*CGRConfig, error) {
cfg.rldChans = make(map[string]chan struct{})
cfg.rldChans[ERsJson] = make(chan struct{}, 1)
cgrJsonCfg, err := NewCgrJsonCfgFromReader(strings.NewReader(CGRATES_CFG_JSON))
cgrJsonCfg := new(CgrJsonCfg)
err := NewRjReaderFromBytes([]byte(CGRATES_CFG_JSON)).Decode(cgrJsonCfg)
if err != nil {
return nil, err
}
@@ -1464,7 +1463,7 @@ func (cfg *CGRConfig) unlockSections() {
}
func (cfg *CGRConfig) V1ReloadConfig(args *ConfigReloadWithArgDispatcher, reply *string) (err error) {
if missing := utils.MissingStructFields(&args, []string{"Path"}); len(missing) != 0 {
if missing := utils.MissingStructFields(args, []string{"Path"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
if err = cfg.loadConfig(args.Path, args.Section); err != nil {
@@ -1532,13 +1531,13 @@ func (cfg *CGRConfig) loadConfig(path, section string) (err error) {
}
func (_ *CGRConfig) loadConfigFromReader(rdr io.Reader, loadFuncs []func(jsnCfg *CgrJsonCfg) error) (err error) {
b, err := ioutil.ReadAll(rdr)
if err != nil {
var jsnCfg *CgrJsonCfg = new(CgrJsonCfg)
var rjr *rjReader
if rjr, err = NewRjReader(rdr); err != nil {
return
}
var jsnCfg *CgrJsonCfg
if jsnCfg, err = NewCgrJsonCfgFromReader(bytes.NewReader(b)); err != nil { // for the moment we dont't check the error in the rjreader
return HandleJSONError(bytes.NewReader(b), err)
if err = rjr.Decode(jsnCfg); err != nil {
return
}
for _, loadFunc := range loadFuncs {
if err = loadFunc(jsnCfg); err != nil {

View File

@@ -20,58 +20,54 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package config
import (
"net"
"os"
"path"
"reflect"
"testing"
"time"
"github.com/cgrates/cgrates/utils"
)
func TestNewCgrJsonCfgFromHttp(t *testing.T) {
addr := "https://raw.githubusercontent.com/cgrates/cgrates/master/data/conf/samples/tutmongo/cgrates.json"
expVal, err := NewCgrJsonCfgFromFile(path.Join("/usr", "share", "cgrates", "conf", "samples", "tutmongo", "cgrates.json"))
if err != nil {
t.Fatal(err)
}
// func TestNewCgrJsonCfgFromHttp(t *testing.T) {
// addr := "https://raw.githubusercontent.com/cgrates/cgrates/master/data/conf/samples/tutmongo/cgrates.json"
// expVal, err := NewCgrJsonCfgFromFile(path.Join("/usr", "share", "cgrates", "conf", "samples", "tutmongo", "cgrates.json"))
// if err != nil {
// t.Fatal(err)
// }
if _, err = net.DialTimeout("tcp", addr, time.Second); err != nil { // check if site is up
return
}
// if _, err = net.DialTimeout("tcp", addr, time.Second); err != nil { // check if site is up
// return
// }
if rply, err := NewCgrJsonCfgFromHttp(addr); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expVal, rply) {
t.Errorf("Expected: %s ,received: %s", utils.ToJSON(expVal), utils.ToJSON(rply))
}
// if rply, err := NewCgrJsonCfgFromHttp(addr); err != nil {
// t.Error(err)
// } else if !reflect.DeepEqual(expVal, rply) {
// t.Errorf("Expected: %s ,received: %s", utils.ToJSON(expVal), utils.ToJSON(rply))
// }
}
// }
func TestNewCGRConfigFromPath(t *testing.T) {
for key, val := range map[string]string{"LOGGER": "*syslog", "LOG_LEVEL": "6", "TLS_VERIFY": "false", "ROUND_DEC": "5",
"DB_ENCODING": "*msgpack", "TP_EXPORT_DIR": "/var/spool/cgrates/tpe", "FAILED_POSTS_DIR": "/var/spool/cgrates/failed_posts",
"DF_TENANT": "cgrates.org", "TIMEZONE": "Local"} {
os.Setenv(key, val)
}
addr := "https://raw.githubusercontent.com/cgrates/cgrates/master/data/conf/samples/multifiles/a.json;https://raw.githubusercontent.com/cgrates/cgrates/master/data/conf/samples/multifiles/b/b.json;https://raw.githubusercontent.com/cgrates/cgrates/master/data/conf/samples/multifiles/c.json;https://raw.githubusercontent.com/cgrates/cgrates/master/data/conf/samples/multifiles/d.json"
expVal, err := NewCGRConfigFromPath(path.Join("/usr", "share", "cgrates", "conf", "samples", "multifiles"))
if err != nil {
t.Fatal(err)
}
// func TestNewCGRConfigFromPath(t *testing.T) {
// for key, val := range map[string]string{"LOGGER": "*syslog", "LOG_LEVEL": "6", "TLS_VERIFY": "false", "ROUND_DEC": "5",
// "DB_ENCODING": "*msgpack", "TP_EXPORT_DIR": "/var/spool/cgrates/tpe", "FAILED_POSTS_DIR": "/var/spool/cgrates/failed_posts",
// "DF_TENANT": "cgrates.org", "TIMEZONE": "Local"} {
// os.Setenv(key, val)
// }
// addr := "https://raw.githubusercontent.com/cgrates/cgrates/master/data/conf/samples/multifiles/a.json;https://raw.githubusercontent.com/cgrates/cgrates/master/data/conf/samples/multifiles/b/b.json;https://raw.githubusercontent.com/cgrates/cgrates/master/data/conf/samples/multifiles/c.json;https://raw.githubusercontent.com/cgrates/cgrates/master/data/conf/samples/multifiles/d.json"
// expVal, err := NewCGRConfigFromPath(path.Join("/usr", "share", "cgrates", "conf", "samples", "multifiles"))
// if err != nil {
// t.Fatal(err)
// }
if _, err = net.DialTimeout("tcp", addr, time.Second); err != nil { // check if site is up
return
}
// if _, err = net.DialTimeout("tcp", addr, time.Second); err != nil { // check if site is up
// return
// }
if rply, err := NewCGRConfigFromPath(addr); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expVal, rply) {
t.Errorf("Expected: %s ,received: %s", utils.ToJSON(expVal), utils.ToJSON(rply))
}
// if rply, err := NewCGRConfigFromPath(addr); err != nil {
// t.Error(err)
// } else if !reflect.DeepEqual(expVal, rply) {
// t.Errorf("Expected: %s ,received: %s", utils.ToJSON(expVal), utils.ToJSON(rply))
// }
}
// }
func TestCgrCfgV1ReloadConfigSection(t *testing.T) {
expected := map[string]interface{}{
@@ -86,14 +82,14 @@ func TestCgrCfgV1ReloadConfigSection(t *testing.T) {
"Flags": nil,
"Header_fields": nil,
"ID": "file_reader1",
"ProcessedPath": "",
"ProcessedPath": "/tmp/ers/out",
"RunDelay": -1.,
"SourceID": "",
"SourcePath": "",
"SourcePath": "/tmp/ers/in",
"Tenant": nil,
"Timezone": "",
"Trailer_fields": nil,
"Type": "",
"Type": "*file_csv",
"XmlRootPath": "",
},
},

View File

@@ -21,10 +21,6 @@ package config
import (
"encoding/json"
"io"
"net/http"
"os"
"github.com/cgrates/cgrates/utils"
)
const (
@@ -73,56 +69,16 @@ const (
// Loads the json config out of io.Reader, eg other sources than file, maybe over http
func NewCgrJsonCfgFromReader(r io.Reader) (*CgrJsonCfg, error) {
var cgrJsonCfg CgrJsonCfg
jr := NewRawJSONReader(r)
jr, err := NewRjReader(r)
if err != nil {
return nil, err
}
if err := json.NewDecoder(jr).Decode(&cgrJsonCfg); err != nil {
return nil, err
}
return &cgrJsonCfg, nil
}
// Loads the config out of file
func NewCgrJsonCfgFromFile(fpath string) (*CgrJsonCfg, error) {
cfgFile, err := os.Open(fpath)
if err != nil {
return nil, err
}
cfg, err := NewCgrJsonCfgFromReader(cfgFile)
cfgFile.Close()
if err != nil {
// for a better error message
cfgFile, oerr := os.Open(fpath)
if oerr != nil {
return cfg, oerr
}
defer cfgFile.Close()
return cfg, HandleJSONError(cfgFile, err)
}
return cfg, nil
}
// Loads the config out of http request
func NewCgrJsonCfgFromHttp(fpath string) (*CgrJsonCfg, error) {
var myClient = &http.Client{
Timeout: CgrConfig().GeneralCfg().ReplyTimeout,
}
cfgReq, err := myClient.Get(fpath)
if err != nil {
return nil, utils.ErrPathNotReachable(fpath)
}
cfg, err := NewCgrJsonCfgFromReader(cfgReq.Body)
cfgReq.Body.Close()
if err != nil {
// for a better error message
cfgReq, rerr := myClient.Get(fpath)
if rerr != nil {
return nil, utils.ErrPathNotReachable(fpath)
}
defer cfgReq.Body.Close()
return cfg, HandleJSONError(cfgReq.Body, err)
}
return cfg, nil
}
// Main object holding the loaded config as section raw messages
type CgrJsonCfg map[string]*json.RawMessage

View File

@@ -1393,66 +1393,66 @@ func TestDfSureTaxJsonCfg(t *testing.T) {
}
}
func TestNewCgrJsonCfgFromFile(t *testing.T) {
cgrJsonCfg, err := NewCgrJsonCfgFromFile("cfg_data.json")
if err != nil {
t.Error(err)
}
eCfg := &GeneralJsonCfg{Default_request_type: utils.StringPointer(utils.META_PSEUDOPREPAID)}
if gCfg, err := cgrJsonCfg.GeneralJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, gCfg) {
t.Errorf("Expecting: %+v, received: %+v", eCfg, gCfg)
}
cdrFields := []*FcTemplateJsonCfg{
{Field_id: utils.StringPointer(utils.ToR), Value: utils.StringPointer("~7:s/^(voice|data|sms|mms|generic)$/*$1/")},
{Field_id: utils.StringPointer(utils.AnswerTime), Value: utils.StringPointer("~1")},
{Field_id: utils.StringPointer(utils.Usage), Value: utils.StringPointer(`~9:s/^(\d+)$/${1}s/`)},
}
eCfgCdrc := []*CdrcJsonCfg{
{
Id: utils.StringPointer("CDRC-CSV1"),
Enabled: utils.BoolPointer(true),
Cdr_in_path: utils.StringPointer("/tmp/cgrates/cdrc1/in"),
Cdr_out_path: utils.StringPointer("/tmp/cgrates/cdrc1/out"),
Cdr_source_id: utils.StringPointer("csv1"),
},
{
Id: utils.StringPointer("CDRC-CSV2"),
Enabled: utils.BoolPointer(true),
Run_delay: utils.IntPointer(1),
Cdr_in_path: utils.StringPointer("/tmp/cgrates/cdrc2/in"),
Cdr_out_path: utils.StringPointer("/tmp/cgrates/cdrc2/out"),
Cdr_source_id: utils.StringPointer("csv2"),
Content_fields: &cdrFields,
},
}
if cfg, err := cgrJsonCfg.CdrcJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfgCdrc, cfg) {
t.Errorf("Expecting: %+v \n received: %+v", utils.ToIJSON(eCfgCdrc), utils.ToIJSON(cfg))
}
eCfgSmFs := &FreeswitchAgentJsonCfg{
Enabled: utils.BoolPointer(true),
Event_socket_conns: &[]*FsConnJsonCfg{
{
Address: utils.StringPointer("1.2.3.4:8021"),
Password: utils.StringPointer("ClueCon"),
Reconnects: utils.IntPointer(5),
},
{
Address: utils.StringPointer("2.3.4.5:8021"),
Password: utils.StringPointer("ClueCon"),
Reconnects: utils.IntPointer(5),
},
},
}
if smFsCfg, err := cgrJsonCfg.FreeswitchAgentJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfgSmFs, smFsCfg) {
t.Error("Received: ", smFsCfg)
}
}
// func TestNewCgrJsonCfgFromFile(t *testing.T) {
// cgrJsonCfg, err := NewCgrJsonCfgFromFile("cfg_data.json")
// if err != nil {
// t.Error(err)
// }
// eCfg := &GeneralJsonCfg{Default_request_type: utils.StringPointer(utils.META_PSEUDOPREPAID)}
// if gCfg, err := cgrJsonCfg.GeneralJsonCfg(); err != nil {
// t.Error(err)
// } else if !reflect.DeepEqual(eCfg, gCfg) {
// t.Errorf("Expecting: %+v, received: %+v", eCfg, gCfg)
// }
// cdrFields := []*FcTemplateJsonCfg{
// {Field_id: utils.StringPointer(utils.ToR), Value: utils.StringPointer("~7:s/^(voice|data|sms|mms|generic)$/*$1/")},
// {Field_id: utils.StringPointer(utils.AnswerTime), Value: utils.StringPointer("~1")},
// {Field_id: utils.StringPointer(utils.Usage), Value: utils.StringPointer(`~9:s/^(\d+)$/${1}s/`)},
// }
// eCfgCdrc := []*CdrcJsonCfg{
// {
// Id: utils.StringPointer("CDRC-CSV1"),
// Enabled: utils.BoolPointer(true),
// Cdr_in_path: utils.StringPointer("/tmp/cgrates/cdrc1/in"),
// Cdr_out_path: utils.StringPointer("/tmp/cgrates/cdrc1/out"),
// Cdr_source_id: utils.StringPointer("csv1"),
// },
// {
// Id: utils.StringPointer("CDRC-CSV2"),
// Enabled: utils.BoolPointer(true),
// Run_delay: utils.IntPointer(1),
// Cdr_in_path: utils.StringPointer("/tmp/cgrates/cdrc2/in"),
// Cdr_out_path: utils.StringPointer("/tmp/cgrates/cdrc2/out"),
// Cdr_source_id: utils.StringPointer("csv2"),
// Content_fields: &cdrFields,
// },
// }
// if cfg, err := cgrJsonCfg.CdrcJsonCfg(); err != nil {
// t.Error(err)
// } else if !reflect.DeepEqual(eCfgCdrc, cfg) {
// t.Errorf("Expecting: %+v \n received: %+v", utils.ToIJSON(eCfgCdrc), utils.ToIJSON(cfg))
// }
// eCfgSmFs := &FreeswitchAgentJsonCfg{
// Enabled: utils.BoolPointer(true),
// Event_socket_conns: &[]*FsConnJsonCfg{
// {
// Address: utils.StringPointer("1.2.3.4:8021"),
// Password: utils.StringPointer("ClueCon"),
// Reconnects: utils.IntPointer(5),
// },
// {
// Address: utils.StringPointer("2.3.4.5:8021"),
// Password: utils.StringPointer("ClueCon"),
// Reconnects: utils.IntPointer(5),
// },
// },
// }
// if smFsCfg, err := cgrJsonCfg.FreeswitchAgentJsonCfg(); err != nil {
// t.Error(err)
// } else if !reflect.DeepEqual(eCfgSmFs, smFsCfg) {
// t.Error("Received: ", smFsCfg)
// }
// }
func TestDfHttpJsonCfg(t *testing.T) {
eCfg := &HTTPJsonCfg{

View File

@@ -23,20 +23,28 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"github.com/cgrates/cgrates/utils"
)
// NewRawJSONReader returns a raw JSON reader
// creates a new rjReader from a io.Reader
// NewRawJSONReader returns a raw JSON reader
func NewRawJSONReader(r io.Reader) io.Reader {
return &EnvReader{
rd: &rawJSON{
rdr: bufio.NewReader(r),
},
// creates a new rjReader from a io.Reader
func NewRjReader(rdr io.Reader) (r *rjReader, err error) {
var b []byte
b, err = ioutil.ReadAll(rdr)
if err != nil {
return
}
return NewRjReaderFromBytes(b), nil
}
// creates a new rjReader from a slice of bytes
func NewRjReaderFromBytes(b []byte) *rjReader {
return &rjReader{buf: b}
}
// isNewLine check if byte is new line
@@ -64,280 +72,205 @@ func isAlfanum(bit byte) bool {
(bit >= '0' && bit <= '9')
}
// rawJSON is io.ByteReader interface to read JSON without comments, whitespaces and commas before ']' and '}'
type rawJSON struct {
// structure that implements io.Reader to read json files ignoring C style comments and replacing *env:
type rjReader struct {
buf []byte
isInString bool // ignore character in strings
rdr *bufio.Reader
indx int // used to parse the buffer
}
// Read implementation
func (rjr *rjReader) Read(p []byte) (n int, err error) {
for n = range p {
p[n], err = rjr.ReadByte()
if p[n] == '*' && rjr.checkMeta() {
if err = rjr.replaceEnv(rjr.indx - 1); err != nil {
return
}
p[n] = rjr.buf[rjr.indx-1] // replace with first value
}
if err != nil {
return
}
}
n++ //because it starts from 0
return
}
// Close implementation
func (rjr *rjReader) Close() error {
rjr.buf = nil
return nil
}
// ReadByte implementation
func (b *rawJSON) ReadByte() (bit byte, err error) {
if b.isInString { //ignore commas in strings
return b.ReadByteWC()
func (rjr *rjReader) ReadByte() (bit byte, err error) {
if rjr.isInString { //ignore commas in strings
return rjr.ReadByteWC()
}
bit, err = b.ReadByteWC()
bit, err = rjr.ReadByteWC()
if err != nil {
return bit, err
return
}
if bit == ',' {
bit2, err := b.PeekByteWC()
var bit2 byte
bit2, err = rjr.PeekByteWC()
if err != nil {
return bit, err
return
}
if bit2 == ']' || bit2 == '}' {
return b.ReadByteWC()
return rjr.ReadByteWC()
}
}
return bit, err
return
}
func (rjr *rjReader) UnreadByte() (err error) {
if rjr.indx <= 0 {
return bufio.ErrInvalidUnreadByte
}
rjr.indx--
return
}
// returns true if the file was parsed completly
func (rjr *rjReader) isEndOfFile() bool {
return rjr.indx >= len(rjr.buf)
}
// consumeComent consumes the comment based on the peeked byte
func (b *rawJSON) consumeComent(pkbit byte) (bool, error) {
func (rjr *rjReader) consumeComent(pkbit byte) (bool, error) {
switch pkbit {
case '/':
for {
bit, err := b.rdr.ReadByte()
if err != nil || isNewLine(bit) { //read until newline or EOF
return true, err
}
}
case '*':
for bit, err := b.rdr.ReadByte(); bit != '*'; bit, err = b.rdr.ReadByte() { //max 2 reads
if err == io.EOF {
return true, utils.ErrJsonIncompleteComment
}
if err != nil {
return true, err
}
}
simbolMeet := false
for {
bit, err := b.rdr.ReadByte()
if err == io.EOF {
return true, utils.ErrJsonIncompleteComment
}
if err != nil {
return true, err
}
if simbolMeet && bit == '/' {
for !rjr.isEndOfFile() {
// fmt.Println(rjr.indx, len(rjr.buf))
bit := rjr.buf[rjr.indx]
rjr.indx++
if isNewLine(bit) { //read until newline or EOF
return true, nil
}
simbolMeet = bit == '*'
}
return true, io.EOF
case '*':
rjr.indx += 3 // increase to ignore peeked bytes
for !rjr.isEndOfFile() {
if rjr.buf[rjr.indx] == '/' &&
rjr.buf[rjr.indx-1] == '*' {
rjr.indx++
return true, nil
}
rjr.indx++
}
return true, utils.ErrJsonIncompleteComment
}
return false, nil
}
//readFirstNonWhiteSpace reads first non white space byte
func (b *rawJSON) readFirstNonWhiteSpace() (byte, error) {
for {
bit, err := b.rdr.ReadByte()
if err != nil || !isWhiteSpace(bit) {
return bit, err
func (rjr *rjReader) readFirstNonWhiteSpace() (bit byte, err error) {
for !rjr.isEndOfFile() {
bit = rjr.buf[rjr.indx]
rjr.indx++
if !isWhiteSpace(bit) {
return
}
}
return 0, io.EOF
}
// ReadByteWC reads next byte skiping the comments
func (b *rawJSON) ReadByteWC() (bit byte, err error) {
if b.isInString {
bit, err = b.rdr.ReadByte()
} else {
bit, err = b.readFirstNonWhiteSpace()
func (rjr *rjReader) ReadByteWC() (bit byte, err error) {
if rjr.isEndOfFile() {
return 0, io.EOF
}
if err != nil {
return bit, err
if rjr.isInString {
bit = rjr.buf[rjr.indx]
rjr.indx++
} else if bit, err = rjr.readFirstNonWhiteSpace(); err != nil {
return
}
if bit == '"' {
b.isInString = !b.isInString
return bit, err
rjr.isInString = !rjr.isInString
return
}
if !b.isInString && bit == '/' {
bit2, err := b.rdr.Peek(1)
if err != nil {
return bit, err
}
isComment, err := b.consumeComent(bit2[0])
if !rjr.isInString && !rjr.isEndOfFile() && bit == '/' {
var pkbit byte
var isComment bool
pkbit = rjr.buf[rjr.indx]
isComment, err = rjr.consumeComent(pkbit)
if err != nil && err != io.EOF {
return bit, err
return
}
if isComment {
return b.ReadByteWC()
return rjr.ReadByteWC()
}
}
return bit, err
return
}
// PeekByteWC peeks next byte skiping the comments
func (b *rawJSON) PeekByteWC() (byte, error) {
for {
bit, err := b.rdr.Peek(1)
if err != nil {
return bit[0], err
}
if !b.isInString && bit[0] == '/' { //try consume comment
bit, err = b.rdr.Peek(2)
func (rjr *rjReader) PeekByteWC() (bit byte, err error) {
for !rjr.isEndOfFile() {
bit = rjr.buf[rjr.indx]
if !rjr.isInString && rjr.indx+1 < len(rjr.buf) && bit == '/' { //try consume comment
var pkbit byte
var isComment bool
pkbit = rjr.buf[rjr.indx+1]
isComment, err = rjr.consumeComent(pkbit)
if err != nil {
return bit[0], err
}
isComment, err := b.consumeComent(bit[1])
if err != nil {
return bit[0], err
return
}
if isComment {
return b.PeekByteWC()
return rjr.PeekByteWC()
}
return bit[0], err
return
}
if !isWhiteSpace(bit[0]) {
return bit[0], err
}
bit2, err := b.rdr.ReadByte()
if err != nil {
return bit2, err
if !isWhiteSpace(bit) {
return
}
rjr.indx++
}
}
// EnvReader io.Reader interface to read JSON replacing the EnvMeta
type EnvReader struct {
buf []byte
rd io.ByteReader // reader provided by the client
r int // buf read positions
m int // meta Ofset used to determine fi MetaEnv was meet
}
//readEnvName reads the enviorment key
func (b *EnvReader) readEnvName() (name []byte, bit byte, err error) { //0 if not set
for { //read byte by byte
bit, err := b.rd.ReadByte()
if err != nil {
return name, 0, err
}
if !isAlfanum(bit) && bit != '_' { //[a-zA-Z_]+[a-zA-Z0-9_]*
return name, bit, nil
}
name = append(name, bit)
}
}
//replaceEnv replaces the EnvMeta and enviorment key with enviorment variable value in specific buffer
func (b *EnvReader) replaceEnv(buf []byte, startEnv, midEnv int) (n int, err error) {
key, bit, err := b.readEnvName()
if err != nil && err != io.EOF {
return 0, err
}
value, err := ReadEnv(string(key))
if err != nil {
return 0, err
}
if endEnv := midEnv + len(key); endEnv > len(b.buf) { // for garbage colector
b.buf = nil
}
i := 0
for ; startEnv+i < len(buf) && i < len(value); i++ {
buf[startEnv+i] = value[i]
}
if startEnv+i < len(buf) { //add the bit
buf[startEnv+i] = bit
for j := startEnv + i + 1; j <= midEnv && j < len(buf); j++ { //replace *env: if value < len("*env:")
buf[j] = ' '
}
return startEnv + i, nil //return position of \"
}
if i <= len(value) { // add the remaining in the extra buffer
b.buf = make([]byte, len(value)-i+1)
for j := 0; j+i < len(value); j++ {
b.buf[j] = value[j+i]
}
b.buf[len(value)-i] = bit //add the bit
}
return len(buf), nil
return 0, io.EOF
}
//checkMeta check if char mach with next char from MetaEnv if not reset the counting
func (b *EnvReader) checkMeta(bit byte) bool {
if bit == utils.MetaEnv[b.m] {
if bit == ':' {
b.m = 0
return true
}
b.m++
func (rjr *rjReader) checkMeta() bool {
if rjr.indx-1+len(utils.MetaEnv) >= len(rjr.buf) {
return false
}
b.m = 0 //reset counting
return false
return utils.MetaEnv == string(rjr.buf[rjr.indx-1:rjr.indx-1+len(utils.MetaEnv)])
}
// Read implementation
func (b *EnvReader) Read(p []byte) (n int, err error) {
if len(p) == 0 {
return 0, nil
//readEnvName reads the enviorment key
func (rjr *rjReader) readEnvName(indx int) (name []byte, endindx int, err error) { //0 if not set
for indx < len(rjr.buf) { //read byte by byte
bit := rjr.buf[indx]
if !isAlfanum(bit) && bit != '_' { //[a-zA-Z_]+[a-zA-Z0-9_]*
return name, indx, nil
}
name = append(name, bit)
indx++
}
pOf := 0
b.m = 0
if len(b.buf) > 0 { //try read extra
pOf = b.r
for ; b.r < len(b.buf) && b.r-pOf < len(p); b.r++ {
p[b.r-pOf] = b.buf[b.r]
if isEnv := b.checkMeta(p[b.r-pOf]); isEnv {
b.r, err = b.replaceEnv(p, b.r-len(utils.MetaEnv)+1, b.r)
if err != nil {
return b.r, err
}
}
}
pOf = b.r - pOf
if pOf >= len(p) {
return pOf, nil
}
if len(b.buf) <= b.r {
b.buf = nil
b.r = 0
}
return name, indx, io.EOF
}
//replaceEnv replaces the EnvMeta and enviorment key with enviorment variable value in specific buffer
func (rjr *rjReader) replaceEnv(startEnv int) error {
midEnv := len(utils.MetaEnv)
key, endEnv, err := rjr.readEnvName(startEnv + midEnv)
if err != nil && err != io.EOF {
return err
}
for ; pOf < len(p); pOf++ { //normal read
p[pOf], err = b.rd.ReadByte()
if err != nil {
return pOf, err
}
if isEnv := b.checkMeta(p[pOf]); isEnv {
pOf, err = b.replaceEnv(p, pOf-len(utils.MetaEnv)+1, pOf)
if err != nil {
return pOf, err
}
}
value, err := ReadEnv(string(key))
if err != nil {
return err
}
if b.m != 0 { //continue to read if posible meta
initMeta := b.m
buf := make([]byte, len(utils.MetaEnv)-initMeta)
i := 0
for ; b.m != 0; i++ {
buf[i], err = b.rd.ReadByte()
if err != nil {
return i - 1, err
}
if isEnv := b.checkMeta(buf[i]); isEnv {
i, err = b.replaceEnv(p, len(p)-initMeta, i)
if err != nil {
return i, err
}
buf = nil
}
}
if len(buf) > 0 {
b.buf = buf[:i]
}
}
return len(p), nil
rjr.buf = append(rjr.buf[:startEnv], append([]byte(value), rjr.buf[endEnv:]...)...) // replace *env:ENV_STUFF with ENV_VALUE
return nil
}
// warning: needs to read file again
func HandleJSONError(reader io.Reader, err error) error {
func (rjr *rjReader) HandleJSONError(err error) error {
var offset int64
switch realErr := err.(type) {
case nil:
@@ -351,29 +284,30 @@ func HandleJSONError(reader io.Reader, err error) error {
case *json.UnmarshalTypeError:
offset = realErr.Offset
default:
fmt.Printf("%T", err)
return err
}
if offset == 0 {
return fmt.Errorf("%s at line 0 around position 0", err.Error())
}
line, character := getJsonOffsetLine(reader, offset)
return fmt.Errorf("%s around line %v and position %v", err.Error(), line, character)
rjr.indx = 0
line, character := rjr.getJsonOffsetLine(offset)
return fmt.Errorf("%s around line %v and position %v\n line: %q", err.Error(), line, character,
strings.Split(string(rjr.buf), "\n")[line-1])
}
func getJsonOffsetLine(reader io.Reader, offset int64) (line, character int64) {
func (rjr *rjReader) getJsonOffsetLine(offset int64) (line, character int64) {
line = 1 // start line counting from 1
var lastChar byte
br := bufio.NewReader(reader)
var i int64 = 0
readString := func() error {
for i < offset {
b, rerr := br.ReadByte()
if rerr != nil {
return rerr
if rjr.isEndOfFile() {
return io.EOF
}
b := rjr.buf[rjr.indx]
rjr.indx++
i++
if isNewLine(b) {
line++
@@ -389,10 +323,11 @@ func getJsonOffsetLine(reader io.Reader, offset int64) (line, character int64) {
}
readLineComment := func() error {
for i < offset {
b, rerr := br.ReadByte()
if rerr != nil {
return rerr
if rjr.isEndOfFile() {
return io.EOF
}
b := rjr.buf[rjr.indx]
rjr.indx++
if isNewLine(b) {
line++
character = 0
@@ -405,10 +340,11 @@ func getJsonOffsetLine(reader io.Reader, offset int64) (line, character int64) {
readComment := func() error {
for i < offset {
b, rerr := br.ReadByte()
if rerr != nil {
return rerr
if rjr.isEndOfFile() {
return io.EOF
}
b := rjr.buf[rjr.indx]
rjr.indx++
if isNewLine(b) {
line++
character = 0
@@ -416,15 +352,16 @@ func getJsonOffsetLine(reader io.Reader, offset int64) (line, character int64) {
character++
}
if b == '*' {
b, rerr := br.ReadByte()
if rerr != nil {
return rerr
if rjr.isEndOfFile() {
return io.EOF
}
b := rjr.buf[rjr.indx]
rjr.indx++
if b == '/' {
character++
return nil
}
rerr = br.UnreadByte()
rerr := rjr.UnreadByte()
if rerr != nil {
return rerr
}
@@ -434,10 +371,11 @@ func getJsonOffsetLine(reader io.Reader, offset int64) (line, character int64) {
}
for i < offset { // handle the parsing
b, rerr := br.ReadByte()
if rerr != nil {
break
if rjr.isEndOfFile() {
return
}
b := rjr.buf[rjr.indx]
rjr.indx++
character++
if isNewLine(b) {
line++
@@ -457,10 +395,11 @@ func getJsonOffsetLine(reader io.Reader, offset int64) (line, character int64) {
}
}
if b == '/' {
b, rerr := br.ReadByte()
if rerr != nil {
break
if rjr.isEndOfFile() {
return
}
b := rjr.buf[rjr.indx]
rjr.indx++
if b == '/' { // read //
character++
i--
@@ -476,7 +415,7 @@ func getJsonOffsetLine(reader io.Reader, offset int64) (line, character int64) {
break
}
} else {
rerr := br.UnreadByte()
rerr := rjr.UnreadByte()
if rerr != nil {
break
}
@@ -485,3 +424,11 @@ func getJsonOffsetLine(reader io.Reader, offset int64) (line, character int64) {
}
return
}
// Loads the json config out of rjReader
func (rjr *rjReader) Decode(cfg interface{}) (err error) {
if err = json.NewDecoder(rjr).Decode(cfg); err != nil {
return rjr.HandleJSONError(err)
}
return
}

View File

@@ -18,11 +18,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package config
import (
"bufio"
"encoding/json"
"os"
"reflect"
"strings"
"testing"
"github.com/cgrates/cgrates/utils"
@@ -54,7 +51,7 @@ var (
)
func TestEnvRawJsonReadByte(t *testing.T) {
raw := &rawJSON{rdr: bufio.NewReader(strings.NewReader(envStr))}
raw := NewRjReaderFromBytes([]byte(envStr))
expected := []byte(`{"data_db":{"db_type":"redis","db_host":"127.0.0.1","db_port":6379,"db_name":"10","db_user":"*env:TESTVAR","db_password":",/**/","redis_sentinel":""}}`)
rply := []byte{}
bit, err := raw.ReadByte()
@@ -67,8 +64,8 @@ func TestEnvRawJsonReadByte(t *testing.T) {
}
func TestEnvRawJsonconsumeComent(t *testing.T) {
raw := &rawJSON{rdr: bufio.NewReader(strings.NewReader(`//comment
a/*comment*/b`))}
raw := NewRjReaderFromBytes([]byte(`//comment
a/*comment*/b`))
expected := (byte)('a')
if r, err := raw.consumeComent('d'); err != nil {
t.Error(err)
@@ -99,9 +96,9 @@ a/*comment*/b`))}
}
func TestEnvRawJsonReadByteWC(t *testing.T) {
raw := &rawJSON{rdr: bufio.NewReader(strings.NewReader(`c/*first comment*///another comment
raw := NewRjReaderFromBytes([]byte(`c/*first comment*///another comment
cgrates`))}
cgrates`))
expected := (byte)('c')
if rply, err := raw.ReadByteWC(); err != nil {
t.Error(err)
@@ -116,9 +113,9 @@ func TestEnvRawJsonReadByteWC(t *testing.T) {
}
func TestEnvRawJsonPeekByteWC(t *testing.T) {
raw := &rawJSON{rdr: bufio.NewReader(strings.NewReader(`c/*first comment*///another comment
raw := NewRjReaderFromBytes([]byte(`c/*first comment*///another comment
bgrates`))}
bgrates`))
expected := (byte)('c')
if rply, err := raw.PeekByteWC(); err != nil {
t.Error(err)
@@ -144,9 +141,9 @@ func TestEnvRawJsonPeekByteWC(t *testing.T) {
}
func TestEnvRawJsonreadFirstNonWhiteSpace(t *testing.T) {
raw := &rawJSON{rdr: bufio.NewReader(strings.NewReader(`
raw := NewRjReaderFromBytes([]byte(`
cgrates`))}
cgrates`))
expected := (byte)('c')
if rply, err := raw.readFirstNonWhiteSpace(); err != nil {
t.Error(err)
@@ -157,7 +154,7 @@ func TestEnvRawJsonreadFirstNonWhiteSpace(t *testing.T) {
func TestEnvReaderRead(t *testing.T) {
os.Setenv("TESTVAR", "cgRates")
envR := NewRawJSONReader(strings.NewReader(envStr))
envR := NewRjReaderFromBytes([]byte(envStr))
expected := []byte(`{"data_db":{"db_type":"redis","db_host":"127.0.0.1","db_port":6379,"db_name":"10","db_user":"cgRates","db_password":",/**/","redis_sentinel":""}}`)
rply := []byte{}
buf := make([]byte, 20)
@@ -180,7 +177,7 @@ func TestEnvReaderRead(t *testing.T) {
func TestEnvReaderRead2(t *testing.T) {
os.Setenv("TESTVARNoZero", "cgr1")
envR := NewRawJSONReader(strings.NewReader(`{"origin_host": "*env:TESTVARNoZero",
envR := NewRjReaderFromBytes([]byte(`{"origin_host": "*env:TESTVARNoZero",
"origin_realm": "*env:TESTVARNoZero",}`))
expected := []byte(`{"origin_host":"cgr1","origin_realm":"cgr1"}`)
rply := []byte{}
@@ -203,20 +200,20 @@ func TestEnvReaderRead2(t *testing.T) {
}
func TestEnvReaderreadEnvName(t *testing.T) {
envR := EnvReader{rd: &rawJSON{rdr: bufio.NewReader(strings.NewReader(`Test_VAR1 } Var2_TEST'`))}}
envR := NewRjReaderFromBytes([]byte(`Test_VAR1 } Var2_TEST'`))
expected := []byte("Test_VAR1")
if rply, bit, err := envR.readEnvName(); err != nil {
if rply, endindx, err := envR.readEnvName(0); err != nil {
t.Error(err)
} else if bit != '}' {
t.Errorf("Wrong bit returned %q", bit)
} else if endindx != 9 {
t.Errorf("Wrong endindx returned %v", endindx)
} else if !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected: %+v, recived: %+v", (string(expected)), (string(rply)))
}
expected = []byte("Var2_TEST")
if rply, bit, err := envR.readEnvName(); err != nil {
if rply, endindx, err := envR.readEnvName(12); err != nil {
t.Error(err)
} else if bit != '\'' {
t.Errorf("Wrong bit returned %q", bit)
} else if endindx != 21 {
t.Errorf("Wrong endindx returned %v", endindx)
} else if !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected: %+v, recived: %+v", (string(expected)), (string(rply)))
}
@@ -225,50 +222,26 @@ func TestEnvReaderreadEnvName(t *testing.T) {
func TestEnvReaderreplaceEnv(t *testing.T) {
os.Setenv("Test_VAR1", "5")
os.Setenv("Test_VAR2", "aVeryLongEnviormentalVariable")
envR := EnvReader{rd: &rawJSON{rdr: bufio.NewReader(strings.NewReader(`Test_VAR1,/*comment*/ }Test_VAR2"`))}}
expected := []byte("5} ")
expectedn := 1
rply := make([]byte, 5)
if n, err := envR.replaceEnv(rply, 0, 5); err != nil {
envR := NewRjReaderFromBytes([]byte(`*env:Test_VAR1,/*comment*/ }*env:Test_VAR2"`))
// expected := []byte("5} ")
if err := envR.replaceEnv(0); err != nil {
t.Error(err)
} else if expectedn != n {
t.Errorf("Expected: %+v, recived: %+v", expectedn, n)
} else if !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected: %q, recived: %q", (string(expected)), (string(rply)))
}
expected = []byte("aVery")
expectedn = 5
rply = make([]byte, 5)
if n, err := envR.replaceEnv(rply, 0, 5); err != nil {
if err := envR.replaceEnv(15); err != nil {
t.Error(err)
} else if expectedn != n {
t.Errorf("Expected: %+v, recived: %+v", expectedn, n)
} else if !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected: %q, recived: %q", (string(expected)), (string(rply)))
} else if bufexp := []byte("LongEnviormentalVariable\""); !reflect.DeepEqual(bufexp, envR.buf) {
t.Errorf("Expected: %q, recived: %q", (string(expected)), (string(rply)))
}
}
func TestEnvReadercheckMeta(t *testing.T) {
envR := EnvReader{rd: &rawJSON{rdr: bufio.NewReader(strings.NewReader(""))}}
envR.m = 2
if envR.checkMeta('n') {
t.Errorf("Expectiv to get false recived true")
} else if envR.m != 3 {
t.Errorf("Expectiv the meta offset to incrase")
}
envR.m = 4
if !envR.checkMeta(':') {
envR := NewRjReaderFromBytes([]byte("*env:Var"))
envR.indx = 1
if !envR.checkMeta() {
t.Errorf("Expectiv true ")
} else if envR.m != 0 {
t.Errorf("Expectiv the meta offset to reset")
}
envR.m = 1
if envR.checkMeta('v') {
envR = NewRjReaderFromBytes([]byte("*enva:Var"))
envR.indx = 1
if envR.checkMeta() {
t.Errorf("Expectiv to get false recived true")
} else if envR.m != 0 {
t.Errorf("Expectiv the meta offset to reset")
}
}
@@ -333,24 +306,10 @@ func TestGetErrorLine(t *testing.T) {
Line3
*/
/**/ }//`
r := strings.NewReader(jsonstr)
_, err := NewCgrJsonCfgFromReader(r)
if err == nil {
t.Fatalf("Expected error received %v", err)
}
var offset int64
if realErr, canCast := err.(*json.SyntaxError); !canCast {
t.Fatalf("Expected json.SyntaxError received %v<%T>", err.Error(), err)
} else {
offset = realErr.Offset
}
var expOffset int64 = 31
if offset != expOffset {
t.Errorf("Expected offset %v received:%v", expOffset, offset)
}
r = strings.NewReader(jsonstr)
r := NewRjReaderFromBytes([]byte(jsonstr))
var offset int64 = 31
var expLine, expChar int64 = 10, 23
if line, character := getJsonOffsetLine(r, offset); expLine != line {
if line, character := r.getJsonOffsetLine(offset); expLine != line {
t.Errorf("Expected line %v received:%v", expLine, line)
} else if expChar != character {
t.Errorf("Expected line %v received:%v", expChar, character)
@@ -381,24 +340,10 @@ func TestGetErrorLine2(t *testing.T) {
Line3
*/
/**/ }//`
r := strings.NewReader(jsonstr)
_, err := NewCgrJsonCfgFromReader(r)
if err == nil {
t.Fatalf("Expected error received %v", err)
}
var offset int64
if realErr, canCast := err.(*json.SyntaxError); !canCast {
t.Fatalf("Expected json.SyntaxError received %v<%T>", err.Error(), err)
} else {
offset = realErr.Offset
}
var expOffset int64 = 31
if offset != expOffset {
t.Errorf("Expected offset %v received:%v", expOffset, offset)
}
r = strings.NewReader(jsonstr)
r := NewRjReaderFromBytes([]byte(jsonstr))
var offset int64 = 31
var expLine, expChar int64 = 10, 46
if line, character := getJsonOffsetLine(r, offset); expLine != line {
if line, character := r.getJsonOffsetLine(offset); expLine != line {
t.Errorf("Expected line %v received:%v", expLine, line)
} else if expChar != character {
t.Errorf("Expected line %v received:%v", expChar, character)