forked from mocktools/go-smtp-mock
-
Notifications
You must be signed in to change notification settings - Fork 1
/
session.go
168 lines (142 loc) · 4.07 KB
/
session.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package smtpmock
import (
"bufio"
"fmt"
"net"
"strings"
"time"
)
// Returns time.Time with current time. Allows to stub time.Now()
var timeNow = func() time.Time { return time.Now() }
// Allows to stub time.Sleep()
var timeSleep = func(delay int) int {
time.Sleep(time.Duration(delay) * time.Second)
return int(delay)
}
// SMTP client-server session interface
type sessionInterface interface {
setTimeout(int)
readRequest() (string, error)
writeResponse(string, int)
addError(error)
clearError()
discardBufin()
readBytes() ([]byte, error)
isErrorFound() bool
finish()
}
// session interfaces
type bufin interface {
ReadString(byte) (string, error)
Buffered() int
Discard(int) (int, error)
ReadBytes(byte) ([]byte, error)
}
type bufout interface {
WriteString(string) (int, error)
Flush() error
}
// SMTP client-server session
type session struct {
connection net.Conn
address string
bufin bufin
bufout bufout
err error
logger logger
}
// SMTP session builder. Creates new session
func newSession(connection net.Conn, logger logger) *session {
return &session{
connection: connection,
address: connection.RemoteAddr().String(),
bufin: bufio.NewReader(connection),
bufout: bufio.NewWriter(connection),
logger: logger,
}
}
// SMTP session methods
// Returns true if session error exists, otherwise returns false
func (session *session) isErrorFound() bool {
return session.err != nil
}
// session.err setter
func (session *session) addError(err error) {
session.err = err
}
// Sets session.err = nil
func (session *session) clearError() {
session.err = nil
}
// Sets session timeout from now to the specified duration in seconds
func (session *session) setTimeout(timeout int) {
err := session.connection.SetDeadline(
timeNow().Add(time.Duration(timeout) * time.Second),
)
if err != nil {
session.err = err
session.logger.error(err.Error())
}
}
// Discardes the bufin remnants
func (session *session) discardBufin() {
bufin := session.bufin
_, err := bufin.Discard(bufin.Buffered())
if err != nil {
session.err = err
session.logger.error(err.Error())
}
}
// Reades client request from the session, returns trimmed string.
// When error case happened writes it to session.err and triggers logger with error level
func (session *session) readRequest() (string, error) {
request, err := session.bufin.ReadString('\n')
if err == nil {
trimmedRequest := strings.TrimSpace(request)
session.logger.infoActivity(sessionRequestMsg + trimmedRequest)
return trimmedRequest, err
}
session.err = err
session.logger.error(err.Error())
return emptyString, err
}
// Reades client request from the session, returns bytes.
// When error case happened writes it to session.err and triggers logger with error level
func (session *session) readBytes() ([]byte, error) {
var request []byte
request, err := session.bufin.ReadBytes('\n')
if err == nil {
session.logger.infoActivity(sessionRequestMsg + sessionBinaryDataMsg)
return request, err
}
session.err = err
session.logger.error(err.Error())
return request, err
}
// Activates session response delay for case when delay > 0.
// Otherwise skipes this feature
func (session *session) responseDelay(delay int) int {
if delay == defaultSessionResponseDelay {
return delay
}
session.logger.infoActivity(fmt.Sprintf("%s: %d sec", sessionResponseDelayMsg, delay))
return timeSleep(delay)
}
// Writes server response to the client session. When error case happened triggers
// logger with warning level
func (session *session) writeResponse(response string, responseDelay int) {
session.responseDelay(responseDelay)
bufout := session.bufout
if _, err := bufout.WriteString(response + "\r\n"); err != nil {
session.logger.warning(err.Error())
}
bufout.Flush()
session.logger.infoActivity(sessionResponseMsg + response)
}
// Finishes SMTP session. When error case happened triggers logger with warning level
func (session *session) finish() {
if err := session.connection.Close(); err != nil {
session.logger.warning(err.Error())
}
session.logger.infoActivity(sessionEndMsg)
}