github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/igm/sockjs-go.v2/sockjs/session.gon-place (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  }