github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/igm/sockjs-go.v2/sockjs/session.go (about) 1 package sockjs 2 3 import ( 4 "encoding/gob" 5 "errors" 6 "io" 7 "net/http" 8 "sync" 9 "time" 10 ) 11 12 type sessionState uint32 13 14 const ( 15 // brand new session, need to send "h" to receiver 16 sessionOpening sessionState = iota 17 // active session 18 sessionActive 19 // session being closed, sending "closeFrame" to receivers 20 sessionClosing 21 // closed session, no activity at all, should be removed from handler completely and not reused 22 sessionClosed 23 ) 24 25 var ( 26 // ErrSessionNotOpen error is used to denote session not in open state. 27 // Recv() and Send() operations are not suppored if session is closed. 28 ErrSessionNotOpen = errors.New("sockjs: session not in open state") 29 errSessionReceiverAttached = errors.New("sockjs: another receiver already attached") 30 ) 31 32 type session struct { 33 sync.Mutex 34 id string 35 req *http.Request 36 state sessionState 37 // protocol dependent receiver (xhr, eventsource, ...) 38 recv receiver 39 // messages to be sent to client 40 sendBuffer []string 41 // messages received from client to be consumed by application 42 // receivedBuffer chan string 43 msgReader *io.PipeReader 44 msgWriter *io.PipeWriter 45 msgEncoder *gob.Encoder 46 msgDecoder *gob.Decoder 47 48 // closeFrame to send after session is closed 49 closeFrame string 50 51 // internal timer used to handle session expiration if no receiver is attached, or heartbeats if recevier is attached 52 sessionTimeoutInterval time.Duration 53 heartbeatInterval time.Duration 54 timer *time.Timer 55 // once the session timeouts this channel also closes 56 closeCh chan struct{} 57 } 58 59 type receiver interface { 60 // sendBulk send multiple data messages in frame frame in format: a["msg 1", "msg 2", ....] 61 sendBulk(...string) 62 // sendFrame sends given frame over the wire (with possible chunking depending on receiver) 63 sendFrame(string) 64 // close closes the receiver in a "done" way (idempotent) 65 close() 66 canSend() bool 67 // done notification channel gets closed whenever receiver ends 68 doneNotify() <-chan struct{} 69 // interrupted channel gets closed whenever receiver is interrupted (i.e. http connection drops,...) 70 interruptedNotify() <-chan struct{} 71 } 72 73 // Session is a central component that handles receiving and sending frames. It maintains internal state 74 func newSession(req *http.Request, sessionID string, sessionTimeoutInterval, heartbeatInterval time.Duration) *session { 75 r, w := io.Pipe() 76 s := &session{ 77 id: sessionID, 78 req: req, 79 msgReader: r, 80 msgWriter: w, 81 msgEncoder: gob.NewEncoder(w), 82 msgDecoder: gob.NewDecoder(r), 83 sessionTimeoutInterval: sessionTimeoutInterval, 84 heartbeatInterval: heartbeatInterval, 85 closeCh: make(chan struct{})} 86 s.Lock() // "go test -race" complains if ommited, not sure why as no race can happen here 87 s.timer = time.AfterFunc(sessionTimeoutInterval, s.close) 88 s.Unlock() 89 return s 90 } 91 92 func (s *session) sendMessage(msg string) error { 93 s.Lock() 94 defer s.Unlock() 95 if s.state > sessionActive { 96 return ErrSessionNotOpen 97 } 98 s.sendBuffer = append(s.sendBuffer, msg) 99 if s.recv != nil && s.recv.canSend() { 100 s.recv.sendBulk(s.sendBuffer...) 101 s.sendBuffer = nil 102 } 103 return nil 104 } 105 106 func (s *session) attachReceiver(recv receiver) error { 107 s.Lock() 108 defer s.Unlock() 109 if s.recv != nil { 110 return errSessionReceiverAttached 111 } 112 s.recv = recv 113 go func(r receiver) { 114 select { 115 case <-r.doneNotify(): 116 s.detachReceiver() 117 case <-r.interruptedNotify(): 118 s.detachReceiver() 119 s.close() 120 } 121 }(recv) 122 123 if s.state == sessionClosing { 124 s.recv.sendFrame(s.closeFrame) 125 s.recv.close() 126 return nil 127 } 128 if s.state == sessionOpening { 129 s.recv.sendFrame("o") 130 s.state = sessionActive 131 } 132 s.recv.sendBulk(s.sendBuffer...) 133 s.sendBuffer = nil 134 s.timer.Stop() 135 s.timer = time.AfterFunc(s.heartbeatInterval, s.heartbeat) 136 return nil 137 } 138 139 func (s *session) detachReceiver() { 140 s.Lock() 141 defer s.Unlock() 142 s.timer.Stop() 143 s.timer = time.AfterFunc(s.sessionTimeoutInterval, s.close) 144 s.recv = nil 145 } 146 147 func (s *session) heartbeat() { 148 s.Lock() 149 defer s.Unlock() 150 if s.recv != nil { // timer could have fired between Lock and timer.Stop in detachReceiver 151 s.recv.sendFrame("h") 152 s.timer = time.AfterFunc(s.heartbeatInterval, s.heartbeat) 153 } 154 } 155 156 func (s *session) accept(messages ...string) error { 157 for _, msg := range messages { 158 if err := s.msgEncoder.Encode(msg); err != nil { 159 return err 160 } 161 } 162 return nil 163 } 164 165 // idempotent operation 166 func (s *session) closing() { 167 s.Lock() 168 defer s.Unlock() 169 if s.state < sessionClosing { 170 s.msgReader.Close() 171 s.msgWriter.Close() 172 s.state = sessionClosing 173 if s.recv != nil { 174 s.recv.sendFrame(s.closeFrame) 175 s.recv.close() 176 } 177 } 178 } 179 180 // idempotent operation 181 func (s *session) close() { 182 s.closing() 183 s.Lock() 184 defer s.Unlock() 185 if s.state < sessionClosed { 186 s.state = sessionClosed 187 s.timer.Stop() 188 close(s.closeCh) 189 } 190 } 191 192 func (s *session) closedNotify() <-chan struct{} { return s.closeCh } 193 194 // Conn interface implementation 195 func (s *session) Close(status uint32, reason string) error { 196 s.Lock() 197 if s.state < sessionClosing { 198 s.closeFrame = closeFrame(status, reason) 199 s.Unlock() 200 s.closing() 201 return nil 202 } 203 s.Unlock() 204 return ErrSessionNotOpen 205 } 206 207 func (s *session) Recv() (string, error) { 208 var msg string 209 err := s.msgDecoder.Decode(&msg) 210 if err == io.ErrClosedPipe { 211 err = ErrSessionNotOpen 212 } 213 return msg, err 214 } 215 216 func (s *session) Send(msg string) error { 217 return s.sendMessage(msg) 218 } 219 220 func (s *session) ID() string { return s.id } 221 222 func (s *session) Request() *http.Request { 223 return s.req 224 }