func (w *WebsocketSession) Close(uint32, string) error {
	if atomic.CompareAndSwapInt32(&w.closed, 0, 1) {
		return w.conn.Close()
	}
	return ErrSessionClosed
}
Close closes the session with provided code and reason.
package sockjsclient
import (
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
	"github.com/gorilla/websocket"
	"github.com/koding/kite/config"
	"github.com/koding/kite/utils"
	"github.com/igm/sockjs-go/sockjs"
)
var ErrSessionClosed = &ErrSession{
	State: sockjs.SessionClosed,
}
type ErrSession struct {
	Type  config.Transport
	State sockjs.SessionState  
	Err   error                
}
var stateTexts = map[sockjs.SessionState]string{
	sockjs.SessionActive:  "session is active",
	sockjs.SessionOpening: "session is opening",
	sockjs.SessionClosing: "session is closing",
	sockjs.SessionClosed:  "session is closed",
}
func (err *ErrSession) Error() string {
	if err.Err == nil {
		return fmt.Sprintf("%s (%s)", stateTexts[err.State], err.Type)
	}
	return fmt.Sprintf("%s: %s (%s)", stateTexts[err.State], err.Err, err.Type)
}
func IsSessionClosed(err error) bool {
	switch err {
	case ErrSessionClosed, sockjs.ErrSessionNotOpen, websocket.ErrCloseSent:
		return true
	}
	if e, ok := err.(*ErrSession); ok && e.State > sockjs.SessionActive {
		return true
	}
	_, ok := err.(*websocket.CloseError)
	return ok
}
type WebsocketSession struct {
	id       string
	messages []string
	closed   int32
	req      *http.Request
	mu    sync.Mutex
	conn  *websocket.Conn
	state sockjs.SessionState
}
var _ sockjs.Session = (*WebsocketSession)(nil)
type DialOptions struct {
	BaseURL string
	ReadBufferSize int
	WriteBufferSize int
	Timeout time.Duration
	ClientFunc func(*DialOptions) *http.Client
}
func (opts *DialOptions) client() *http.Client {
	if opts.ClientFunc != nil {
		return opts.ClientFunc(opts)
	}
	return &http.Client{
		Timeout: opts.Timeout,
		Jar:     config.CookieJar,
	}
}
func DialWebsocket(uri string, cfg *config.Config) (*WebsocketSession, error) {
	u, err := url.Parse(uri)
	if err != nil {
		return nil, err
	}
	h := http.Header{
		"Origin": {u.Scheme + "://" + u.Host},
	}
	serverID := threeDigits()
	sessionID := utils.RandomString(20)
	u = makeWebsocketURL(u, serverID, sessionID)
	conn, _, err := cfg.Websocket.Dial(u.String(), h)
	if err != nil {
		return nil, err
	}
	session := NewWebsocketSession(conn)
	session.id = sessionID
	session.req = &http.Request{
		URL:    u,
		Header: h,
	}
	return session, nil
}
func ConnectWebsocketSession(opts *DialOptions) (*WebsocketSession, error) {
	cfg := config.New()
	cfg.Websocket.HandshakeTimeout = opts.Timeout
	cfg.Websocket.ReadBufferSize = opts.ReadBufferSize
	cfg.Websocket.WriteBufferSize = opts.WriteBufferSize
	return DialWebsocket(opts.BaseURL, cfg)
}
func NewWebsocketSession(conn *websocket.Conn) *WebsocketSession {
	return &WebsocketSession{
		conn: conn,
	}
}
func (w *WebsocketSession) RemoteAddr() string {
	return w.conn.RemoteAddr().String()
}
func (w *WebsocketSession) ID() string {
	return w.id
}
func (w *WebsocketSession) Recv() (string, error) {
	if len(w.messages) > 0 {
		msg := w.messages[0]
		w.messages = w.messages[1:]
		return msg, nil
	}
read_frame:
	if atomic.LoadInt32(&w.closed) == 1 {
		return "", ErrSessionClosed
	}
	_, buf, err := w.conn.ReadMessage()
	if err != nil {
		return "", err
	}
	if len(buf) == 0 {
		return "", errors.New("unexpected empty message")
	}
	frameType := buf[0]
	data := buf[1:]
	switch frameType {
	case 'o':
		w.setState(sockjs.SessionActive)
		goto read_frame
	case 'a':
		var messages []string
		err = json.Unmarshal(data, &messages)
		if err != nil {
			return "", err
		}
		w.messages = append(w.messages, messages...)
	case 'm':
		var message string
		err = json.Unmarshal(data, &message)
		if err != nil {
			return "", err
		}
		w.messages = append(w.messages, message)
	case 'c':
		w.setState(sockjs.SessionClosed)
		return "", ErrSessionClosed
	case 'h':
		goto read_frame
	default:
		return "", errors.New("invalid frame type")
	}
	if len(w.messages) == 0 {
		return "", errors.New("no message")
	}
	msg := w.messages[0]
	w.messages = w.messages[1:]
	return msg, nil
}
func (w *WebsocketSession) Send(str string) error {
	if atomic.LoadInt32(&w.closed) == 1 {
		return ErrSessionClosed
	}
	w.mu.Lock()
	defer w.mu.Unlock()
	b, _ := json.Marshal([]string{str})
	return w.conn.WriteMessage(websocket.TextMessage, b)
}