Eyevinn / whip

Client and Server modules for WebRTC HTTP Ingestion Protocol (WHIP)
Apache License 2.0
94 stars 15 forks source link

Access-Control-Allow-Method #133

Closed neilyoung closed 7 months ago

neilyoung commented 7 months ago

I'm after a problem with your way to determine, if PATCH is allowed or not.

If I interpret this line in the SDK's index.ts correctly you are firing an OPTIONS request to the server.

      const config = await this.whipProtocol.getConfiguration(this.whipEndpoint.toString(), this.opts.authkey);

and later on you are trying to determine, if PATCH is allowed.

I'm wondering, why config.headers.get("access-control-allow-methods") return null in my case. Is it due to the camel-case? I think MediaMTX clearly states, it would support PATCH...

OPTIONS /stream/whip HTTP/1.1
Host: myserver
Connection: keep-alive
Accept: */*
Access-Control-Request-Method: OPTIONS
Access-Control-Request-Headers: authorization
Origin: http://localhost:1234
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
Sec-Fetch-Mode: cors
Referer: http://localhost:1234/
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 204 No Content
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Authorization, Content-Type, If-Match
Access-Control-Allow-Methods: OPTIONS, GET, POST, PATCH, DELETE
Access-Control-Allow-Origin: *
Server: mediamtx
Date: Sat, 06 Apr 2024 08:12:10 GMT
birme commented 7 months ago

I am not hundred percent sure but perhaps the header “Access-Control-Allow-Methods” also need to be included in the “Access-Control-Allow-Headers” in order to get and parse that value?

neilyoung commented 7 months ago

Could be worth a try. Indeed. Will try to patch the MediaMTX sources accordingly

neilyoung commented 7 months ago

….although I think it’s better to go with Ice-Lite, since these PATCHes and Ice Restarts add another level of complexity for server and clients

neilyoung commented 7 months ago

Hmm. No. Even with this response for the preflight the result is null:

HTTP/1.1 204 No Content
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: OPTIONS, GET, POST, PATCH, DELETE
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Link
Server: mediamtx
Date: Sat, 06 Apr 2024 10:15:39 GMT
neilyoung commented 7 months ago
image
birme commented 7 months ago

Sorry, I realized it probably need to be in Access-Control-Expose-Headers as well

neilyoung commented 7 months ago

Can I do something to check this out? Disregard, you gave the answer.

neilyoung commented 7 months ago

Yes, that works now. Great.

But my change is a bit clumsy, since I enabled all for both headers:

//      ctx.Writer.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type, If-Match")
    ctx.Writer.Header().Set("Access-Control-Allow-Headers", "*")
    ctx.Writer.Header().Set("Access-Control-Expose-Headers", "*")
//  ctx.Writer.Header().Set("Access-Control-Expose-Headers", "Link")

Would you know a "less generic" setup?

birme commented 7 months ago

That would be to explicitly add Access-Control-Allow-Methods to the list in both Expose and Allow headers directive

neilyoung commented 7 months ago

No, doesn't work like so. I guess only "*" works

neilyoung commented 7 months ago

Also some are sceptical, why this is necessary at all. However, w/o these "" here and there your code doesn't correctly determine the state of the allow header. I also don't have an explanation, why these "" are required, but I find it too generic and possibly problematic for security reasons.

birme commented 7 months ago

So

ctx.Writer.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type, If-Match, Access-Control-Allow-Methods")

and

ctx.Writer.Header().Set("Access-Control-Expose-Headers", "Authorization, Content-Type, If-Match, Access-Control-Allow-Methods, Link") does not work?

neilyoung commented 7 months ago

Exactly

birme commented 7 months ago

Ok, what is the recommended way to determine in browser space what methods are available / allowed without actually trying the method?

neilyoung commented 7 months ago

Good question... .:) BTW: ChatGPT is also having the opinion, that any "*" or something else is not required. I should be able to read the header, but I'm not w/o this trick

birme commented 7 months ago

I am reading https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_response_header

neilyoung commented 7 months ago

Let me know if I can help with something

birme commented 7 months ago

This is what we do in the WHIP endpoint side in one of our server implementations

this.server.register(require("fastify-cors"), { exposedHeaders: ["Location", "ETag", "Link", "Access-Control-Allow-Methods"], methods: ["POST", "GET", "OPTIONS", "DELETE", "PATCH"], preflightContinue: true, strictPreflight: false, });

neilyoung commented 7 months ago

OK, tried again to patch the MediaMTX GOLANG sources. This time it seems to work, the headers are accepted now:

WHIPClient Endpoint says it supports Trickle ICE as PATCH is an allowed method

But down the road I'm running into problems:

image
OPTIONS /stream/whip HTTP/1.1
Host: myserver
Connection: keep-alive
Accept: */*
Access-Control-Request-Method: OPTIONS
Access-Control-Request-Headers: authorization
Origin: http://localhost:1234
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
Sec-Fetch-Mode: cors
Referer: http://localhost:1234/
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 204 No Content
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Authorization, Content-Type, If-Match, Access-Control-Allow-Methods
Access-Control-Allow-Methods: OPTIONS, GET, POST, PATCH, DELETE
Access-Control-Allow-Origin: *
Server: mediamtx
Date: Sat, 06 Apr 2024 15:29:42 GMT

OPTIONS /stream/whip HTTP/1.1
Host: myserver
Connection: keep-alive
Authorization: devkey
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
Accept: */*
dnt: 1
Origin: http://localhost:1234
Referer: http://localhost:1234/
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 204 No Content
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Authorization, Content-Type, If-Match, Access-Control-Allow-Methods
Access-Control-Allow-Methods: OPTIONS, GET, POST, PATCH, DELETE
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Link, Access-Control-Allow-Methods
Link: <stun:turn-ms.votix.com:3478>; rel="ice-server"
Link: <turn:turn-ms.votix.com:3478>; rel="ice-server"; username="2500804448:"; credential="SRun7f1vSTpRb5cjSqJJJ1098UY="; credential-type="password"
Server: mediamtx
Date: Sat, 06 Apr 2024 15:29:42 GMT

POST /stream/whip HTTP/1.1
Host: myserver
Connection: keep-alive
Content-Length: 5618
Authorization: devkey
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
Content-Type: application/sdp
Accept: */*
dnt: 1
Origin: http://localhost:1234
Referer: http://localhost:1234/
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

v=0
o=- 1932061534694614557 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1
a=extmap-allow-mixed
a=msid-semantic: WMS 04bb8378-1f08-4ac1-a6b5-a527a0a56607
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 0 8 13 110 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:4rg9
a=ice-pwd:w2+w3IsOlsEkLEHibsLncmB3
a=ice-options:trickle
a=fingerprint:sha-256 E1:79:86:ED:25:B5:78:54:6E:71:23:F3:97:E8:46:D7:43:C6:C5:79:40:C7:68:F2:83:36:3A:DF:40:95:81:19
a=setup:actpass
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=sendonly
a=msid:04bb8378-1f08-4ac1-a6b5-a527a0a56607 8ef6695c-9a82-4a63-b704-4953a69ff2f0
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:63 red/48000/2
a=fmtp:63 111/111
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:126 telephone-event/8000
a=ssrc:3790054637 cname:ZojFAfumNf7nE43g
a=ssrc:3790054637 msid:04bb8378-1f08-4ac1-a6b5-a527a0a56607 8ef6695c-9a82-4a63-b704-4953a69ff2f0
m=video 9 UDP/TLS/RTP/SAVPF 96 97 102 103 104 105 106 107 108 109 127 125 39 40 45 46 98 99 100 101 112 113 116 117 118
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:4rg9
a=ice-pwd:w2+w3IsOlsEkLEHibsLncmB3
a=ice-options:trickle
a=fingerprint:sha-256 E1:79:86:ED:25:B5:78:54:6E:71:23:F3:97:E8:46:D7:43:C6:C5:79:40:C7:68:F2:83:36:3A:DF:40:95:81:19
a=setup:actpass
a=mid:1
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendonly
a=msid:04bb8378-1f08-4ac1-a6b5-a527a0a56607 16c782df-7138-40b9-8d4c-2c688d77d374
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:102 H264/90000
a=rtcp-fb:102 goog-remb
a=rtcp-fb:102 transport-cc
a=rtcp-fb:102 ccm fir
a=rtcp-fb:102 nack
a=rtcp-fb:102 nack pli
a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
a=rtpmap:103 rtx/90000
a=fmtp:103 apt=102
a=rtpmap:104 H264/90000
a=rtcp-fb:104 goog-remb
a=rtcp-fb:104 transport-cc
a=rtcp-fb:104 ccm fir
a=rtcp-fb:104 nack
a=rtcp-fb:104 nack pli
a=fmtp:104 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f
a=rtpmap:105 rtx/90000
a=fmtp:105 apt=104
a=rtpmap:106 H264/90000
a=rtcp-fb:106 goog-remb
a=rtcp-fb:106 transport-cc
a=rtcp-fb:106 ccm fir
a=rtcp-fb:106 nack
a=rtcp-fb:106 nack pli
a=fmtp:106 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:107 rtx/90000
a=fmtp:107 apt=106
a=rtpmap:108 H264/90000
a=rtcp-fb:108 goog-remb
a=rtcp-fb:108 transport-cc
a=rtcp-fb:108 ccm fir
a=rtcp-fb:108 nack
a=rtcp-fb:108 nack pli
a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f
a=rtpmap:109 rtx/90000
a=fmtp:109 apt=108
a=rtpmap:127 H264/90000
a=rtcp-fb:127 goog-remb
a=rtcp-fb:127 transport-cc
a=rtcp-fb:127 ccm fir
a=rtcp-fb:127 nack
a=rtcp-fb:127 nack pli
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=4d001f
a=rtpmap:125 rtx/90000
a=fmtp:125 apt=127
a=rtpmap:39 H264/90000
a=rtcp-fb:39 goog-remb
a=rtcp-fb:39 transport-cc
a=rtcp-fb:39 ccm fir
a=rtcp-fb:39 nack
a=rtcp-fb:39 nack pli
a=fmtp:39 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=4d001f
a=rtpmap:40 rtx/90000
a=fmtp:40 apt=39
a=rtpmap:45 AV1/90000
a=rtcp-fb:45 goog-remb
a=rtcp-fb:45 transport-cc
a=rtcp-fb:45 ccm fir
a=rtcp-fb:45 nack
a=rtcp-fb:45 nack pli
a=fmtp:45 level-idx=5;profile=0;tier=0
a=rtpmap:46 rtx/90000
a=fmtp:46 apt=45
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 profile-id=0
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:100 VP9/90000
a=rtcp-fb:100 goog-remb
a=rtcp-fb:100 transport-cc
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=fmtp:100 profile-id=2
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:112 H264/90000
a=rtcp-fb:112 goog-remb
a=rtcp-fb:112 transport-cc
a=rtcp-fb:112 ccm fir
a=rtcp-fb:112 nack
a=rtcp-fb:112 nack pli
a=fmtp:112 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=64001f
a=rtpmap:113 rtx/90000
a=fmtp:113 apt=112
a=rtpmap:116 red/90000
a=rtpmap:117 rtx/90000
a=fmtp:117 apt=116
a=rtpmap:118 ulpfec/90000
a=ssrc-group:FID 2821221448 1657611027
a=ssrc:2821221448 cname:ZojFAfumNf7nE43g
a=ssrc:2821221448 msid:04bb8378-1f08-4ac1-a6b5-a527a0a56607 16c782df-7138-40b9-8d4c-2c688d77d374
a=ssrc:1657611027 cname:ZojFAfumNf7nE43g
a=ssrc:1657611027 msid:04bb8378-1f08-4ac1-a6b5-a527a0a56607 16c782df-7138-40b9-8d4c-2c688d77d374
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, Access-Control-Allow-Methods
Content-Type: application/sdp
Etag: *
Id: dff99315-deac-4414-a3cb-8fc74e4cdfcc
Link: <stun:turn-ms.votix.com:3478>; rel="ice-server"
Link: <turn:turn-ms.votix.com:3478>; rel="ice-server"; username="2500804448:"; credential="SRun7f1vSTpRb5cjSqJJJ1098UY="; credential-type="password"
Location: http://myserver/stream/whip/58d89c88-3718-46c3-9644-6795053a4204
Server: mediamtx
Date: Sat, 06 Apr 2024 15:29:42 GMT
Transfer-Encoding: chunked

ad5
v=0
o=- 6043780664410569932 1712417382 IN IP4 0.0.0.0
s=-
t=0 0
a=fingerprint:sha-256 72:16:89:38:3A:BA:7E:BB:DC:55:E9:ED:94:54:3B:A0:60:D7:84:34:B7:2B:41:5B:36:C7:31:BD:85:D1:28:D8
a=extmap-allow-mixed
a=group:BUNDLE 0 1
m=audio 9 UDP/TLS/RTP/SAVPF 111 9 0 8
c=IN IP4 0.0.0.0
a=setup:active
a=mid:0
a=ice-ufrag:IArhjwZEEFURPQzC
a=ice-pwd:JXCriWiaHkhijVeaxZLfqpfrzkmUiNXZ
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10;useinbandfec=1
a=rtcp-fb:111 transport-cc 
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=recvonly
a=candidate:3891666212 1 udp 2130706431 192.168.190.20 8189 typ host
a=candidate:3891666212 2 udp 2130706431 192.168.190.20 8189 typ host
a=candidate:233762139 1 udp 2130706431 172.17.0.1 8189 typ host
a=candidate:233762139 2 udp 2130706431 172.17.0.1 8189 typ host
a=candidate:1120243443 1 udp 1694498815 xx.xx.xx.xx 49117 typ srflx raddr 0.0.0.0 rport 49117
a=candidate:1120243443 2 udp 1694498815 xx.xx.xx.xx 49117 typ srflx raddr 0.0.0.0 rport 49117
a=candidate:1120243443 1 udp 1694498815 xx.xx.xx.xx 42566 typ srflx raddr 0.0.0.0 rport 42566
a=candidate:1120243443 2 udp 1694498815 xx.xx.xx.xx 42566 typ srflx raddr 0.0.0.0 rport 42566
a=candidate:3782576977 1 udp 16777215 xx.xx.xx.xx 60356 typ relay raddr 0.0.0.0 rport 55554
a=candidate:3782576977 2 udp 16777215 xx.xx.xx.xx 60356 typ relay raddr 0.0.0.0 rport 55554
a=end-of-candidates
m=video 9 UDP/TLS/RTP/SAVPF 96 102 106 45 98
c=IN IP4 0.0.0.0
a=setup:active
a=mid:1
a=ice-ufrag:IArhjwZEEFURPQzC
a=ice-pwd:JXCriWiaHkhijVeaxZLfqpfrzkmUiNXZ
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb 
a=rtcp-fb:96 transport-cc 
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack 
a=rtcp-fb:96 nack pli
a=rtpmap:102 H264/90000
a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
a=rtcp-fb:102 goog-remb 
a=rtcp-fb:102 transport-cc 
a=rtcp-fb:102 ccm fir
a=rtcp-fb:102 nack 
a=rtcp-fb:102 nack pli
a=rtpmap:106 H264/90000
a=fmtp:106 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtcp-fb:106 goog-remb 
a=rtcp-fb:106 transport-cc 
a=rtcp-fb:106 ccm fir
a=rtcp-fb:106 nack 
a=rtcp-fb:106 nack pli
a=rtpmap:45 AV1/90000
a=fmtp:45 level-idx=5;profile=0;tier=0
a=rtcp-fb:45 goog-remb 
a=rtcp-fb:45 transport-cc 
a=rtcp-fb:45 ccm fir
a=rtcp-fb:45 nack 
a=rtcp-fb:45 nack pli
a=rtpmap:98 VP9/90000
a=fmtp:98 profile-id=0
a=rtcp-fb:98 goog-remb 
a=rtcp-fb:98 transport-cc 
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack 
a=rtcp-fb:98 nack pli
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=recvonly

0

OPTIONS /stream/whip/58d89c88-3718-46c3-9644-6795053a4204 HTTP/1.1
Host: myserver
Connection: keep-alive
Accept: */*
Access-Control-Request-Method: PATCH
Access-Control-Request-Headers: content-type,etag
Origin: http://localhost:1234
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
Sec-Fetch-Mode: cors
Referer: http://localhost:1234/
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 204 No Content
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Authorization, Content-Type, If-Match, Access-Control-Allow-Methods
Access-Control-Allow-Methods: OPTIONS, GET, POST, PATCH, DELETE
Access-Control-Allow-Origin: *
Server: mediamtx
Date: Sat, 06 Apr 2024 15:29:42 GMT
neilyoung commented 7 months ago

A bit more info regarding the failing PATCH:

image
birme commented 7 months ago

Anything on the server side saying why the PATCH failed? Or is it not even receiving it?

neilyoung commented 7 months ago

No, because it's a local browser issue

birme commented 7 months ago

Aha, etag of course. CORS is fun…

neilyoung commented 7 months ago

OK, fixed it now. Hit all the places where MediaMTX messes with CORS headers :)

Works.

birme commented 7 months ago

So etag needs to be in both allow and expose. Looking in the preflight to patch request that one is missing

neilyoung commented 7 months ago

Thanks for your patience

birme commented 7 months ago

No problem! Happy to be able to assist

birme commented 7 months ago

Hope you don’t mind me closing this issue now. Let us know if you find something that should be corrected or if you have any further questions

neilyoung commented 7 months ago

Sure. Sorry, I should have done that. Have a nice weekend.