snapview / tokio-tungstenite

Future-based Tungstenite for Tokio. Lightweight stream-based WebSocket implementation
MIT License
1.88k stars 236 forks source link

Can not use Request to open new WebSocket connection #217

Closed sposnjak-cpot closed 2 years ago

sposnjak-cpot commented 2 years ago

I am trying to send custom header to the WS server. First I created a "regular" connection to check if it works correctly as:

use actix_rt::net::TcpStream;

use futures::stream::{SplitSink, SplitStream};
use futures_util::StreamExt;
use tokio_tungstenite::{
    connect_async, tungstenite::protocol::Message, MaybeTlsStream, WebSocketStream,
};

use crate::my_error::MyError;

pub struct MyWsClient;

type WsSink = SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>;
type WsStream = SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>;

impl MyWsClient {
    pub async fn connect(url: &str, _api_key: &str) -> Result<(WsSink, WsStream), MyError> {
        let (ws_stream, _) = connect_async(url).await?;

        Ok(ws_stream.split())
    }
}

This works nicely - it opens the connection and all. Next I need to supply the Authenticate header on the connection I tried to do it in this way:

use actix_rt::net::TcpStream;

use futures::stream::{SplitSink, SplitStream};
use futures_util::StreamExt;
use tokio_tungstenite::{
    connect_async, tungstenite::{protocol::Message, http::Request}, MaybeTlsStream, WebSocketStream,
};

use crate::my_error::MyError;

pub struct MyWsClient;

type WsSink = SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>;
type WsStream = SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>;

impl MyWsClient {
    pub async fn connect(url: &str, api_key: &str) -> Result<(WsSink, WsStream), MyError> {
        let request = Request::builder().uri(url).method("GET").header("Authorization", api_key).body("").unwrap();
        let (ws_stream, _) = connect_async(request).await?;

        Ok(ws_stream.split())
    }
}

This code will not compile as the compiler claims that:

$ cargo build      
   Compiling prog v0.1.0 (/home/simonp/work/prog.rs/prog)
error[E0277]: the trait bound `Request<&str>: IntoClientRequest` is not satisfied
  --> src/my_client.rs:19:44
   |
19 |         let (ws_stream, _) = connect_async(request).await?;
   |                              ------------- ^^^^^^^ the trait `IntoClientRequest` is not implemented for `Request<&str>`
   |                              |
   |                              required by a bound introduced by this call
   |
   = help: the following implementations were found:
             <Request<()> as IntoClientRequest>
note: required by a bound in `connect_async`
  --> /home/simonp/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-tungstenite-0.17.1/src/connect.rs:20:8
   |
20 |     R: IntoClientRequest + Unpin,
   |        ^^^^^^^^^^^^^^^^^ required by this bound in `connect_async`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `prog` due to previous error

I am not sure what I am doing wrong here? Some helpful hint would be appreciated.

sdroege commented 2 years ago
        let request = Request::builder().uri(url).method("GET").header("Authorization", api_key).body("").unwrap();

The body of the request must be empty, not an empty string. Use body(()) instead of body(""), then it should be OK.

sposnjak-cpot commented 2 years ago

Thank you! That solved the issue.