Test Failed
Push — master ( 219109...51ce82 )
by Vyacheslav
01:35
created

interface.go (1 issue)

Severity
1
package goagi
2
3
import (
4
	"bufio"
5
	"fmt"
6
	"strings"
7
	"time"
8
)
9
10
// AGI interface structure
11
type AGI struct {
12
	env map[string]string
13
	arg []string
14
	io  *bufio.ReadWriter
15
}
16
17
const respTout = time.Millisecond * 100
18
19
var (
20
	// error returned when AGI environment header is not valid
21
	EInvalEnv = errorNew("Invalid AGI env variable")
22
	// error returned when response read is timed out
0 ignored issues
show
comment on exported var ERespTout should be of the form "ERespTout ..."
Loading history...
23
	ERespTout = errorNew("Response receive timeout")
24
)
25
26
func newInterface(iodev *bufio.ReadWriter) (*AGI, error) {
27
	agi := &AGI{make(map[string]string), make([]string, 0), iodev}
28
	scanner := bufio.NewScanner(iodev)
29
	for scanner.Scan() {
30
		line := scanner.Text()
31
		if line == "" {
32
			break
33
		}
34
		if err := agi.setEnv(line); err != nil {
35
			return nil, err
36
		}
37
	}
38
	if err := scanner.Err(); err != nil {
39
		return nil, err
40
	}
41
	return agi, nil
42
}
43
44
// Env returns AGI environment variable by key
45
func (agi *AGI) Env(key string) string {
46
	val, ok := agi.env[key]
47
	if ok {
48
		return val
49
	}
50
	return ""
51
}
52
53
// EnvArgs returns list of environment arguments
54
func (agi *AGI) EnvArgs() []string {
55
	return agi.arg
56
}
57
58
func (agi *AGI) setEnv(line string) error {
59
	if !strings.HasPrefix(line, "agi_") {
60
		return EInvalEnv.withInfo(line)
61
	}
62
	idx := strings.Index(line, ": ")
63
	if idx == -1 {
64
		return EInvalEnv.withInfo(line)
65
	}
66
	if strings.HasPrefix(line, "agi_arg_") {
67
		agi.arg = append(agi.arg, line[idx+2:])
68
	} else {
69
		agi.env[line[4:idx]] = line[idx+2:]
70
	}
71
	return nil
72
}
73
74
func (agi *AGI) execute(cmd string, args ...interface{}) (*agiResp, error) {
75
	_, err := agi.io.WriteString(compileCmd(cmd, args...))
76
	if err != nil {
77
		return nil, err
78
	}
79
	agi.io.Flush()
80
81
	chStr, chErr := agi.read()
82
	select {
83
	case str := <-chStr:
84
		return parseResponse(str)
85
	case err := <-chErr:
86
		return nil, err
87
	case <-time.After(respTout):
88
		return nil, ERespTout
89
	}
90
}
91
92
func (agi *AGI) read() (chan string, chan error) {
93
	chStr := make(chan string)
94
	chErr := make(chan error)
95
	go func() {
96
		defer close(chStr)
97
		defer close(chErr)
98
99
		str, err := agi.io.ReadString('\n')
100
		if err != nil {
101
			chErr <- err
102
			return
103
		}
104
		if !strings.HasPrefix(str, "520-") {
105
			chStr <- str
106
			return
107
		}
108
		for {
109
			s, err := agi.io.ReadString('\n')
110
			if err != nil {
111
				chErr <- err
112
				return
113
			}
114
			str += s
115
			if strings.HasPrefix(s, "520 End") {
116
				chStr <- str
117
				return
118
			}
119
		}
120
	}()
121
	return chStr, chErr
122
}
123
124
func compileCmd(cmd string, args ...interface{}) string {
125
	for _, arg := range args {
126
		val := fmt.Sprintf("%v", arg)
127
		if len(val) > 0 {
128
			cmd = fmt.Sprintf("%s %s", cmd, val)
129
		} else {
130
			cmd = fmt.Sprintf("%s \"\"", cmd)
131
		}
132
	}
133
134
	return fmt.Sprintf("%s\n", cmd)
135
}
136