// ConnectWebsocketSession dials the remote specified in the opts and // creates new websocket session. func ConnectWebsocketSession(opts *DialOptions) (*WebsocketSession, error) { dialURL, err := url.Parse(opts.BaseURL) if err != nil { return nil, err } // will be used to set the origin header originalScheme := dialURL.Scheme if err := replaceSchemeWithWS(dialURL); err != nil { return nil, err } if err := addMissingPortAndSlash(dialURL); err != nil { return nil, err } serverID := threeDigits() sessionID := utils.RandomString(20) // Add server_id and session_id to the path. dialURL.Path += serverID + "/" + sessionID + "/websocket" requestHeader := http.Header{} requestHeader.Add("Origin", originalScheme+"://"+dialURL.Host) ws := websocket.Dialer{ ReadBufferSize: opts.ReadBufferSize, WriteBufferSize: opts.WriteBufferSize, } // if the user passed a custom HTTP client and its transport // is of *http.Transport type - we're using its Dial field // for connecting to remote host if t, ok := opts.Client().Transport.(*http.Transport); ok { ws.NetDial = t.Dial } // if the user passed a timeout, use a dial with a timeout if opts.Timeout != 0 && ws.NetDial == nil { // If ws.NetDial is non-nil then gorilla does not // use ws.HandshakeTimeout for the deadlines. // // Instead we're going to set it ourselves. ws.NetDial = (&net.Dialer{ Timeout: opts.Timeout, Deadline: time.Now().Add(opts.Timeout), }).Dial } conn, _, err := ws.Dial(dialURL.String(), requestHeader) if err != nil { return nil, err } session := NewWebsocketSession(conn) session.id = sessionID return session, nil }
// newRequest returns a new *Request from the method and arguments passed. func (c *Client) newRequest(method string, args *dnode.Partial) (*Request, func(interface{}, *Error)) { // Parse dnode method arguments: [options] var options callOptions args.One().MustUnmarshal(&options) // Notify the handlers registered with Kite.OnFirstRequest(). if _, ok := c.session.(*sockjsclient.WebsocketSession); !ok { c.firstRequestHandlersNotified.Do(func() { c.m.Lock() c.Kite = options.Kite c.m.Unlock() c.LocalKite.callOnFirstRequestHandlers(c) }) } request := &Request{ ID: utils.RandomString(16), Method: method, Args: options.WithArgs, LocalKite: c.LocalKite, Client: c, Auth: options.Auth, Context: cache.NewMemory(), } // Call response callback function, send back our response callFunc := func(result interface{}, err *Error) { if options.ResponseCallback.Caller == nil { return } // Only argument to the callback. response := Response{ Result: result, Error: err, } if err := options.ResponseCallback.Call(response); err != nil { c.LocalKite.Log.Error(err.Error()) } } return request, callFunc }
// NewXHRSession returns a new XHRSession, a SockJS client which supports // xhr-polling // http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-74 func NewXHRSession(opts *DialOptions) (*XHRSession, error) { client := opts.Client() // following /server_id/session_id should always be the same for every session serverID := threeDigits() sessionID := utils.RandomString(20) sessionURL := opts.BaseURL + "/" + serverID + "/" + sessionID // start the initial session handshake sessionResp, err := client.Post(sessionURL+"/xhr", "text/plain", nil) if err != nil { return nil, err } defer sessionResp.Body.Close() if sessionResp.StatusCode != http.StatusOK { return nil, fmt.Errorf("Starting new session failed. Want: %d Got: %d", http.StatusOK, sessionResp.StatusCode) } buf := bufio.NewReader(sessionResp.Body) frame, err := buf.ReadByte() if err != nil { return nil, err } if frame != 'o' { return nil, fmt.Errorf("can't start session, invalid frame: %s", frame) } return &XHRSession{ client: client, sessionID: sessionID, sessionURL: sessionURL, opened: true, abort: make(chan struct{}, 1), }, nil }