Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WHIP/WHEP: DELETE not properly handled #3177

Closed
12 tasks
neilyoung opened this issue Mar 29, 2024 · 29 comments · Fixed by #3240
Closed
12 tasks

WHIP/WHEP: DELETE not properly handled #3177

neilyoung opened this issue Mar 29, 2024 · 29 comments · Fixed by #3240
Labels
bug Something isn't working webrtc

Comments

@neilyoung
Copy link

neilyoung commented Mar 29, 2024

Which version are you using?

v1.6.0

Which operating system are you using?

  • Linux amd64 standard
  • Linux amd64 Docker
  • [x ] Linux arm64 standard
  • Linux arm64 Docker
  • Linux arm7 standard
  • Linux arm7 Docker
  • Linux arm6 standard
  • Linux arm6 Docker
  • Windows amd64 standard
  • Windows amd64 Docker (WSL backend)
  • macOS amd64 standard
  • macOS amd64 Docker
  • Other (please describe)

Describe the issue

Description

Describe how to replicate the issue

First of all: BIG, BIG THANKS for supporting WHIP/WHEP :) That's really a big improvement for this project. Didn't check that for a couple of months, but when did you add that?

However, I think I'm having a little issue with the current realisation. According the the standard a TEAR DOWN of a WHIP session (and most likely also a WHEP session, but not explicitly checked) has to be done with a HTTP DELETE to the resource published (or subscribed) so far.

I might do a mistake but it seems, this request is not properly handled at the moment. At least I'm catching a 404.

DELETE /whip/0822db29-fe7b-49e9-b8c5-8601c716e274 HTTP/1.1
Host: jetson:8889
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36
Accept: */*
dnt: 1
Origin: null
Accept-Encoding: gzip, deflate
Accept-Language: de-US,de;q=0.9,en-US;q=0.8,en;q=0.7,es-AR;q=0.6,es;q=0.5,de-DE;q=0.4

HTTP/1.1 404 Not Found
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Content-Type: text/plain
Server: mediamtx
Date: Fri, 29 Mar 2024 14:44:09 GMT
Content-Length: 18

404 page not found

EDIT: Updated the Wireshark trace. WHIP client is OBS Studio 30.1.0, MacOS

Just a minor issue, but should be corrected, I guess.

Did you attach the server logs?

no

Did you attach a network dump?

yes, inline

@neilyoung
Copy link
Author

neilyoung commented Apr 3, 2024

OK, I got some thumbs up, but no ack.

Anyway, MediaMTX definitely behaves incorrectly here. According to the RFC https://datatracker.ietf.org/doc/html/draft-murillo-whep-02 (here for WHEP, but that's no diff to WHIP) ...

To explicitly terminate a session, the WHEP Player MUST perform an HTTP DELETE request to the resource URL returned in the Location header field of the initial HTTP POST. Upon receiving the HTTP DELETE request, the WHEP resource will be removed and the resources freed on the Media Server, terminating the ICE and DTLS sessions.

That means to me - and the sample below this statement proves it-, that the returned "location" header MUST contain a FQDN, not only a part of a path. I think OBS - as strong reference - does see it the same way, because it does this to terminate a session initiated via WHIP (in my case to http://jetson:8889/mystream/whip):

  1. It gets a 201 CREATED from MediaMTX, containing just a part of the FQDN required: Location: whip/d470351d-1380-437b-9de9-6ad6aca49bc4
  2. It then issues a HTTP DELETE to this partly URL, which fails with 404

MediaMTX response to WHIP

HTTP/1.1 201 Created
Accept-Patch: application/trickle-ice-sdpfrag
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag, ID, Accept-Patch, Link, Location
Content-Type: application/sdp
Etag: *
Id: d4befba0-c478-4187-8a27-75efe24e99b6
Location: whip/d470351d-1380-437b-9de9-6ad6aca49bc4
Server: mediamtx
Date: Wed, 03 Apr 2024 11:16:23 GMT
Content-Length: 1301

OBS Delete request and MediaMTX response

DELETE /whip/d470351d-1380-437b-9de9-6ad6aca49bc4 HTTP/1.1
Host: jetson:8889
Accept: */*
User-Agent: Mozilla/5.0 (OBS-Studio/30.1.0; Mac OS X; en-US)

HTTP/1.1 404 Not Found
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Content-Type: text/plain
Server: mediamtx
Date: Wed, 03 Apr 2024 11:18:35 GMT
Content-Length: 18

404 page not found

In fact, if the DELETE request is directed to a string concatenation of the stream name plus the returned "location", it works.

Like:

curl http://jetson:8889/mystream/whip/d470351d-1380-437b-9de9-6ad6aca49bc4 -X DELETE

But as said: This is not correct and IMHO no client supports that.

@neilyoung
Copy link
Author

neilyoung commented Apr 3, 2024

An - admittedly clumsy - attempt to solve this. What do you think? Worth a PR?

diff --git a/internal/servers/webrtc/http_server.go b/internal/servers/webrtc/http_server.go
index 95bddf2b..1b6614d5 100644
--- a/internal/servers/webrtc/http_server.go
+++ b/internal/servers/webrtc/http_server.go
@@ -203,7 +203,11 @@ func (s *httpServer) onWHIPPost(ctx *gin.Context, path string, publish bool) {
        ctx.Writer.Header().Set("ID", res.sx.uuid.String())
        ctx.Writer.Header().Set("Accept-Patch", "application/trickle-ice-sdpfrag")
        ctx.Writer.Header()["Link"] = webrtc.LinkHeaderMarshal(servers)
-       ctx.Writer.Header().Set("Location", sessionLocation(publish, res.sx.secret))
+       scheme := ctx.Request.URL.Scheme
+       if scheme == "" {
+               scheme = "http"
+       }
+       ctx.Writer.Header().Set("Location", scheme + "://" + ctx.Request.Host + ctx.Request.URL.Path + "/" + res.sx.secret.String())
        ctx.Writer.WriteHeader(http.StatusCreated)
        ctx.Writer.Write(res.answer)
 }

EDIT: Obsolete, see #3195

@neilyoung
Copy link
Author

Created a PR with not much hope #3195

@aler9
Copy link
Member

aler9 commented Apr 9, 2024

Hello,

The WHIP/WHEP specification doesn't explicitly state that an absolute URL is required inside the location header, although in all examples there's an absolute URL, thus this behavior is recommended.

The OBS implementation is incorrect, since relative paths that don't start with a slash, inside the Location header, should be appended to the path of the request, discarding anything after the last slash, per RFC3986:

http://a/b/c/d;p?q + g;x = http://a/b/c/g;x

Therefore

http://jetson:8889/mystream/whip + whip/d470351d-1380-437b-9de9-6ad6aca49bc4 = http://jetson:8889/mystream/whip/d470351d-1380-437b-9de9-6ad6aca49bc4

Therefore this issue is not about specifications, but is about switching to a common practice in order to broaden compatibility.

The server prefers to send relative paths in place of absolute URLs in order to support reverse proxies and gateways, therefore the effects of switching to absolute URLs must be investigated further.

@aler9 aler9 added bug Something isn't working webrtc labels Apr 9, 2024
@neilyoung
Copy link
Author

neilyoung commented Apr 9, 2024

Thanks for the follow up. In fact, it's not only OBS. I have at least three OS projects which do exactly the same: Taking the location header "as is" and do a DELETE on this (or PATCH)

@aler9
Copy link
Member

aler9 commented Apr 9, 2024

Then i think that the best course of action is using full paths (prefixed with a slash) inside the location header:

Location: /mystream/whip/d470351d-1380-437b-9de9-6ad6aca49bc4

The problem of this approach is that we will lose compatibility with reverse proxies that serve MediaMTX in a subfolder. For instance, i usually use a gateway to proxy requests, originally addressed to /api/v1/live/video/mystream/whip, to MediaMTX in /mystream/whip, stripping the first 4 elements of the path. I guess the reverse proxy / gateway will also have to edit the Location header in order to change the path there too.

@neilyoung
Copy link
Author

But there should be a way to detect, if there is a reverse proxy, e.g. by header info, shouldn't that be possible?

@neilyoung
Copy link
Author

But let me check your proposal quickly...

@neilyoung
Copy link
Author

I think that's going to work. Let me adjust my PR accordingly. I have tested it with OBS and one of the mentioned OS apps and it worked for me. Even behind NGINX as reverse proxy

@neilyoung
Copy link
Author

@neilyoung
Copy link
Author

neilyoung commented Apr 9, 2024

That works for WHIP and WHEP

I found another little issue while testing this, maybe you have an idea. It's not 100% related, but because I ran into this, I will try to explain it here.

My server has a UFW firewall which is configured like so (just the IPv4 rules):

22/tcp                     ALLOW       Anywhere                  
8554/tcp                   ALLOW       Anywhere                  
8889/tcp                   ALLOW       Anywhere                  
8189/udp                   ALLOW       Anywhere                  
443/tcp                    ALLOW       Anywhere                  

With this setup I'm able to use all of the 3rdparty apps, but not OBS. OBS cannot establish the RTP connection as long as the firewall is up. Would you know, what else I would have to open for OBS?

As anticipated (from the OBS log):


14:40:27.289: [obs-webrtc] [whip_output: 'adv_stream'] PeerConnection state is now: Connecting
14:40:27.776: ==== Streaming Start ===============================================
14:41:07.375: [obs-webrtc] [whip_output: 'adv_stream'] PeerConnection state is now: Failed


@neilyoung
Copy link
Author

neilyoung commented Apr 9, 2024

Maybe worth another issue, but we are in a discussion here. OBS is not sending any PATCH request, which would point to that they where doing trickle-ice (even though the SDP contains that claim). They send my PC's local network address. That doesn't harm for WHIP, I guess, because they are just supposed to SEND to MediaMTX.

a=candidate:1 1 UDP 2130706431 fd00::876:d18e:36d:acaf 59791 typ host
a=candidate:2 1 UDP 2122317567 192.168.188.53 59791 typ host

I stumbled over the candidates MediaMTX is sending the the ANSWER. There is the local IP address of the AWS instance with port 8189, which will surely not be reachable. Then there are srflx candidates with public IP addresses on higher ports (>59000), which are OK. And relay candidates, also in that high port range, also public.

So I opened the UDP port range, which my STUN/TURN server is supposed to use (COTURN), and it worked. Maybe you add that info to the documentation somewhere: If one configures STUN/TURN then a possibly used server firewall has to be open for the potential port range, this server provides.

@neilyoung
Copy link
Author

neilyoung commented Apr 9, 2024

As another idea: Why don't just add a configuration for the public IP/port of the server? This would remove the requirement for STUN/TURN at all (like Mediasoup does it). This has two advantages: The public IP doesn't have to be retrieved by STUN server-side and there is no need for opening a potentially wide range of UDP ports.

Other than that - and unrelated again - I would suggest to publish the Link header for WHEP responses only. There is no need for publishing possibly volatile long term TURN credentials to everybody in the world.

It would also be great, if you would provide - maybe a callable interface - for creating time limited credentials instead of a fix TURN credential provisioning.

Sorry for the long post(s)

EDIT: Line 205 ff in http_server.go

	if !publish {
		ctx.Writer.Header()["Link"] = webrtc.LinkHeaderMarshal(servers)
	}

@aler9
Copy link
Member

aler9 commented Apr 12, 2024

As another idea: Why don't just add a configuration for the public IP/port of the server? This would remove the requirement for STUN/TURN at all (like Mediasoup does it). This has two advantages: The public IP doesn't have to be retrieved by STUN server-side and there is no need for opening a potentially wide range of UDP ports.

This is already implemented, enabled by default and can be tuned in this way:

# Address of a local UDP listener that will receive connections.
# Use a blank string to disable.
webrtcLocalUDPAddress: :8189
# Address of a local TCP listener that will receive connections.
# This is disabled by default since TCP is less efficient than UDP and
# introduces a progressive delay when network is congested.
webrtcLocalTCPAddress: ''

You also need to remove all STUN/TURN servers in order to use it.

Maybe worth another issue, but we are in a discussion here. OBS is not sending any PATCH request, which would point to that they where doing trickle-ice (even though the SDP contains that claim). They send my PC's local network address. That doesn't harm for WHIP, I guess, because they are just supposed to SEND to MediaMTX.

Remove all STUN/TURN servers and set the IP manually, as mentioned in the README:

webrtcAdditionalHosts: [my.ip.add.ress]

Other than that - and unrelated again - I would suggest to publish the Link header for WHEP responses only. There is no need for publishing possibly volatile long term TURN credentials to everybody in the world.

TURN credentials are not broadcasted publicly but are sent in WHIP and WHEP responses only. Turning them off for WHIP responses would have the effect of preventing people from publishing.

It would also be great, if you would provide - maybe a callable interface - for creating time limited credentials instead of a fix TURN credential provisioning.

This is already implemented, take a look at the part about "secret-based authentication" in the README.

@neilyoung
Copy link
Author

neilyoung commented Apr 12, 2024

This is already implemented, enabled by default and can be tuned in this way:

I think I don't understand. In which way is webrtcLocalTCPAddress the configuration point for the public IP of the server, given the comment provided?

webrtcAdditionalHosts

Sorry, what purpose has this setting?

TURN credentials are not broadcasted publicly but are sent in WHIP and WHEP responses only.

Well, that's what I meant by "broadcast"

Turning them off for WHIP responses would have the effect of preventing people from publishing.

Nope. In which way? It makes no sense to return TURN servers or even STUN servers in the 201 response header. This information is meant to support the client to gather its candidates. But for what purpose should a WHIP (I don't talk about WHEP) client send any candidate at all? There is no media traffic from the server back to the WHIP client, which would make this information necessary.

I have causally removed the provisioning of TURN/STUN information from the Link header in WHIP case and could not find any problem over various types of connections.

This is already implemented, take a look at the part about "secret-based authentication" in the README.

OK, this wasn't obvious to me. I'll give it a try.

BTW: GH Actions sent me a problem https://github.com/bluenviron/mediamtx/actions/runs/8615767557. The problem is: I was doing the changes completely in the web editor of github. How am I supposed to run the formatter on this after the change?

@aler9
Copy link
Member

aler9 commented Apr 12, 2024

You can configure a fixed public IP and a fixed port, either TCP or UDP, by using together webrtcLocalUDPAddress, webrtcLocalTCPAddress and webrtcAdditionalHosts, this is explained in the README:

https://github.com/bluenviron/mediamtx?tab=readme-ov-file#connectivity-issues

Nope. In which way? It makes no sense to return TURN servers or even STUN servers in the 201 response header.

Now i understand the observation, i checked the code and i think you're right! i will remove the Link header from POST responses ASAP.

@neilyoung
Copy link
Author

neilyoung commented Apr 12, 2024

Thank you very much for your patience with me and have a nice weekend :)

BTW: It could be helpful to have the TURN/STUN servers in the WHEP response, even though that would need to perform an ICE restart on the client side after having received the ANSWER. Anyway, this is the only relevant direction, which could make use of it

EDIT: Or to send it in the preflight response, which is allowed by the RFC

@aler9
Copy link
Member

aler9 commented Apr 13, 2024

I checked again all the places in which STUN/TURN servers are returned to users which are

Although it's apparently useless to send TURN servers in POST responses, this is dictated by the specification (https://datatracker.ietf.org/doc/draft-ietf-wish-whip/) and also helpful to perform ICE restarts, as you mentioned.

Therefore, the way in which servers are sent to users is right and the server doesn't seem to need any improvement.

TURN servers cannot be sent in preflight responses since the latter are unauthenticated.

@neilyoung
Copy link
Author

Although it's apparently useless to send TURN servers in POST responses, this is dictated by the specification

I think the spec says MAY...

TURN servers cannot be sent in preflight responses since the latter are unauthenticated.

TURN credentials are part of the response, and if it is sent in OPTIONS it could be used for the creation of the ICE candidates to be sent in the initial offer. When it comes to the offer it is too late.

in POST responses, after a successful authentication:

I'm not using auth at all at the moment and it is not sent in OPTIONS then. I don't see a reason for having to be authenticated just for that. If the TURN credentials are life-time limited there is no danger of abuse.

Regarding AUTH: Is it correct, you are supporting Basic Auth? I'm 10% sure, this is not part of the spec, since this requires token authentication.

@neilyoung
Copy link
Author

Remove all STUN/TURN servers and set the IP manually, as mentioned in the README:

webrtcAdditionalHosts: [my.ip.add.ress]

Confirmed. That works fine. Thanks for pointing me to that. IMHO, completely obsoletes STUN/TURN. However, there is still the IP of the used network propagated, which is misleading in my case, because AWS local. Can this be prevented?

@neilyoung
Copy link
Author

This is already implemented, take a look at the part about "secret-based authentication" in the README.

OK, this wasn't obvious to me. I'll give it a try.

That works too. The lifetime of 24h is a bit too generous in my eyes, but ok. That's fine.

Also thanks for pointing me on that :) You know, I'm a fan of your solution, so please don't get me wrong.

@aler9
Copy link
Member

aler9 commented Apr 13, 2024

Can this be prevented?

yes, there is webrtcIPsFromInterfaces: no, i think you better take a look at the configuration file and scroll through all available options.

Since both OBS and the Eyevinn WHIP demo are both working without changing anything, can you write the name of a WHIP client that is currently not liking relative Locations?

@neilyoung
Copy link
Author

Since both OBS and the Eyevinn WHIP demo are both working without changing anything, can you write the name of a WHIP client that is currently not liking relative Locations?

OBS 30.1.2 on Mac

@neilyoung
Copy link
Author

i think you better take a look at the configuration file and scroll through all available options.

Yes, you are right. Sorry for bother.

@neilyoung
Copy link
Author

Since both OBS and the Eyevinn WHIP demo are both working without changing anything, can you write the name of a WHIP client that is currently not liking relative Locations?

OBS 30.1.2 on Mac

DELETE is also and still not working with the latest commit a18bebf. I'm really sorry

@aler9
Copy link
Member

aler9 commented Apr 14, 2024

Hello, you're right and DELETE requests sent by both OBS and Eyevinn/whip were not working. This is definitively fixed by #3240. The reason why i forgot to check is that there are too many topics mentioned in this issue, therefore i just checked for connectivity and CORS.

Next time feel free to create an issue for each single issue (for instance, separate ones for connectivity, CORS and DELETE), in this way each topic can be handled separately.

@neilyoung
Copy link
Author

Hehe, sorry for the mess. Thanks for the fix. Will checkout later

Copy link
Contributor

This issue is mentioned in release v1.7.0 🚀
Check out the entire changelog by clicking here

@neilyoung
Copy link
Author

Working. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working webrtc
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants