// Connect opens a connection to the backend and upgrades it to a websocket. Calls to // 'MakeRequest' can be made after calling this, but responss will not be // receivable until 'Serve' is also called. func (cs *ClientServerImpl) Connect() error { parsedURL, err := url.Parse(cs.URL) if err != nil { return err } signer := authv4.NewHttpSigner(cs.Region, ServiceName, cs.CredentialProvider, nil) // NewRequest never returns an error if the url parses and we just verified // it did above request, _ := http.NewRequest("GET", cs.URL, nil) signer.SignHttpRequest(request) // url.Host might not have the port, but tls.Dial needs it dialHost := parsedURL.Host if !strings.Contains(dialHost, ":") { dialHost += ":443" } timeoutDialer := &net.Dialer{Timeout: wsConnectTimeout} log.Info("Creating poll dialer", "host", parsedURL.Host) wsConn, err := tls.DialWithDialer(timeoutDialer, "tcp", dialHost, &tls.Config{InsecureSkipVerify: cs.AcceptInvalidCert}) if err != nil { return err } websocketConn, httpResponse, err := websocket.NewClient(wsConn, parsedURL, request.Header, readBufSize, writeBufSize) if httpResponse != nil { defer httpResponse.Body.Close() } if err != nil { var resp []byte if httpResponse != nil { var readErr error resp, readErr = ioutil.ReadAll(httpResponse.Body) if readErr != nil { return errors.New("Unable to read websocket connection: " + readErr.Error() + ", " + err.Error()) } // If there's a response, we can try to unmarshal it into one of the // modeled error types possibleError, _, decodeErr := DecodeData(resp, cs.TypeDecoder) if decodeErr == nil { return cs.NewError(possibleError) } } log.Warn("Error creating a websocket client", "err", err) return errors.New(string(resp) + ", " + err.Error()) } cs.Conn = websocketConn return nil }
// New returns a client/server to bidirectionally communicate with the backend. // The returned struct should have both 'Connect' and 'Serve' called upon it // before being used. func New(url string, region string, credentialProvider credentials.AWSCredentialProvider, acceptInvalidCert bool, statsEngine stats.Engine, publishMetricsInterval time.Duration) wsclient.ClientServer { cs := &clientServer{ statsEngine: statsEngine, publishTicker: nil, publishMetricsInterval: publishMetricsInterval, signer: authv4.NewHttpSigner(region, wsclient.ServiceName, credentialProvider, nil), } cs.URL = url cs.Region = region cs.CredentialProvider = credentialProvider cs.AcceptInvalidCert = acceptInvalidCert cs.ServiceError = &tcsError{} cs.RequestHandlers = make(map[string]wsclient.RequestHandler) cs.TypeDecoder = &TcsDecoder{} return cs }
func (cs *clientServer) signRequest(payload []byte) []byte { signer := authv4.NewHttpSigner(cs.Region, "ecs", cs.CredentialProvider, nil) reqBody := bytes.NewBuffer(payload) // NewRequest never returns an error if the url parses and we just verified // it did above request, _ := http.NewRequest("GET", cs.URL, reqBody) signer.SignHttpRequest(request) var data []byte for k, vs := range request.Header { for _, v := range vs { data = append(data, k...) data = append(data, ": "...) data = append(data, v...) data = append(data, "\r\n"...) } } data = append(data, "\r\n"...) data = append(data, payload...) return data }