bytebeamio / rumqtt

The MQTT ecosystem in rust
Apache License 2.0
1.58k stars 246 forks source link

[rumqttc] MqttOptions::parse_url broken for websocket urls #808

Open brocaar opened 6 months ago

brocaar commented 6 months ago

I believe these two options are equal:

let options = MqttOptions::new("123", "localhost", 1883);
let options = MqttOptions::parse_url("mqtt://example.com:1883?client_id=123").unwrap();

The url crate is used to parse the transport, hostname and port. Then the client_id is retrieved by reading the URL parameters, etc...

let host = url.host_str().unwrap_or_default().to_owned();

https://github.com/bytebeamio/rumqtt/blob/main/rumqttc/src/v5/mod.rs#L581

However, when looking at the websocket example, the host argument of MqttOptions::new(...) is not the hostname, but the full websocket URL:

    // port parameter is ignored when scheme is websocket
    let mut mqttoptions = MqttOptions::new(
        "clientId-aSziq39Bp3",
        "ws://broker.mqttdashboard.com:8000/mqtt",
        8000,
    );
    mqttoptions.set_transport(Transport::Ws);

https://github.com/bytebeamio/rumqtt/blob/main/rumqttc/examples/websocket.rs#L10

If using the parse_url for ws://broker.mqttdashboard.com:8000/mqtt?client_id=clientId-aSziq39Bp3 then this would result in host being set to broker.mqttdashboard.com, not ws://broker.mqttdashboard.com:8000/mqtt.

The result is the following error being returned by the eventloop:

swanandx commented 6 months ago

Hey, thanks for reporting, the way we handle ws / wss differently from tcp is the root of this.

I think it is handled that way because when using websockets, address can have path like /mqtt, which isn't case with normal tcp. So when we treat host as full URL, this can be done: https://github.com/bytebeamio/rumqtt/blob/b8766e109edf5caaa579a89aa71199638690fa11/rumqttc/src/eventloop.rs#L403

to work around it, instead of using broker_addr directly to convert to request, we can separately extract the path ( if present ) and do:

let mut request = format!("ws://{domain}:{port}/{path}").into_client_request()?;

^ this would allow us to not ignore the port when using websocket and to address this issue.

PS:

I have opened a PR to refactor MqttOptions: #789

this PR would allow users to provide partial / full urls for both tcp & ws! it is still draft because I'm waiting for feedback on design, like shall we return error if we fail to parse, or shall we panic. ( more details in PR desc )

please have a look at that PR as well!

Thank you!