Jump to content

"/" missing in web socket URL; browser not loading some images


JustEddy

Recommended Posts

JustEddy

When I open http://emby/ (which is running in Docker, sitting behind Traefik) in Firefox, I see this:

Setting require baseUrl to http://emby/web app.js:1:57757
Uncaught (in promise) DOMException: The operation is insecure. appstorage-localstorage.js:1
Begin ConnectionManager constructor connectionmanager.js:1:11379
creating ApiClient singleton app.js:1:7207
credentials initialized with: {"Servers":[{"ManualAddress":"http://emby","ManualAddressOnly":true,"IsLocalServer":true,"AccessToken":"xxx","UserId":"xxx","DateLastAccessed":1628908045570,"LastConnectionMode":2,"Type":"Server","Name":"emby","Id":"xxx","LocalAddress":"http://172.18.0.15:8096"}]} credentials.js:1:1003
ApiClient serverAddress: http://emby apiclient.js:1:8770
ApiClient appName: Emby Web apiclient.js:1:8833
ApiClient appVersion: 4.6.4.0 apiclient.js:1:8884
ApiClient deviceName: Firefox apiclient.js:1:8941
...
Begin connect connectionmanager.js:1:30064
Begin getAvailableServers connectionmanager.js:1:17206
Begin getConnectServers connectionmanager.js:1:17399
Begin connectToServers, with 1 servers connectionmanager.js:1:18783
begin connectToServer connectionmanager.js:1:19342
tryReconnect: http://emby connectionmanager.js:1:20462
tryReconnectToUrl: http://emby connectionmanager.js:1:6673
ConnectionManager requesting url: http://emby/emby/system/info/public connectionmanager.js:1:1530
ConnectionManager response status: 200, url: http://emby/emby/system/info/public connectionmanager.js:1:2458
connectionManager.resolveIfAvailable: http://emby connectionmanager.js:1:21334
connectionManager.onSuccessfulConnection: http://emby connectionmanager.js:1:9079
connectionManager.afterConnectValidated: http://emby connectionmanager.js:1:7166
connectionManager.validateAuthentication: http://emby connectionmanager.js:1:7349
ConnectionManager requesting url: http://emby/emby/System/Info connectionmanager.js:1:1530
ConnectionManager response status: 200, url: http://emby/emby/System/Info connectionmanager.js:1:2458
connectionManager.afterConnectValidated: http://emby connectionmanager.js:1:7166
returning instance from getOrAddApiClient connectionmanager.js:1:15804
Setting server address to http://emby apiclient.js:1:19591
connectionManager.afterConnectValidated result.State: SignedIn connectionmanager.js:1:8721
calling apiClient.ensureWebSocket connectionmanager.js:1:6174
opening web socket with url: ws://embywebsocket?api_key=xxx&deviceId=xxx apiclient.js:1:16902      ⬅⬅⬅⬅
returning instance from getOrAddApiClient connectionmanager.js:1:15804
Firefox can’t establish a connection to the server at ws://embywebsocket/?api_key=xxx&deviceId=xxx. apiclient.js:1:16981
web socket closed apiclient.js:1:17526
nulling out web socket

Notice the marked line: opening web socket with url: ws://embywebsocket?api_key=xxx&deviceId=xxx apiclient.js:1:16902

Any idea why this is happening? (I checked the query logs in my DNS server and can see that it really is trying to look up "embywebserver".)

For reference, it's not just Firefox; Safari behaves the same way:

[Log] opening web socket with url: ws://embywebsocket?api_key=xxx&deviceId=xxx (apiclient.js, line 1)
[Error] WebSocket network error: The operation couldn’t be completed. (kCFErrorDomainCFNetwork error 2.) (x2)
[Log] web socket closed (apiclient.js, line 1)
[Log] nulling out web socket (apiclient.js, line 1)

It looks like the code that generates the web socket URL is not always inserting a "/" when it should be. So my first question: is that a known issue?

 

Unrelated to the above, I'm also seeing a number of missing images in Emby. (It's not always "Thumb" images, it also happens with "Primary" images.)

40870131_ScreenShot2021-08-14at12_40_58PM.thumb.png.d243972cc4da174cd7537963b5eee2bd.png

When I look in the Web Developer Tools and inspect the buttons with missing images, I see this (notice the "Could not load the image" pop-up):

2097745572_ScreenShot2021-08-14at12_45.57@2x.thumb.png.cd991da8c06e2d370fbf2796f32144ba.png

but if I grab the image with curl:

❯ curl -svvR -o Thumb.jpg 'http://emby/Items/1264/Images/Thumb?maxWidth=600&tag=d2xxx8e&quality=90'
* TCP_NODELAY set
* Connected to emby (192.168.8.8) port 80 (#0)
> GET /Items/1264/Images/Thumb?maxWidth=600&tag=d2xxx8e&quality=90 HTTP/1.1
> Host: emby
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Access-Control-Allow-Headers: Accept, Accept-Language, Authorization, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, Content-Type, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, X-Emby-Token, X-Emby-Client, X-Emby-Client-Version, X-Emby-Device-Id, X-Emby-Device-Name, X-Emby-Authorization
< Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS
< Access-Control-Allow-Origin: *
< Age: 58093
< Cache-Control: public, max-age=31536000
< Content-Length: 24843
< Content-Type: image/jpeg
< Date: Sat, 14 Aug 2021 16:58:38 GMT
< Etag: "e9xxxc4"
< Expires: Sun, 14 Aug 2022 16:58:38 GMT
< Last-Modified: Sat, 14 Aug 2021 00:50:26 GMT
< Realtimeinfo.dlna.org: DLNA.ORG_TLAG=*
< Server: UPnP/1.0 DLNADOC/1.50
< Transfermode.dlna.org: Interactive
< Vary: Accept
<
{ [12968 bytes data]
* Connection #0 to host emby left intact
* Closing connection 0

it's fine (and opens successfully in Preview.app):

❯ file Thumb.jpg
Thumb.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 600x337, components 3

In Safari on the same device, the same set of images do not load. (On a different device, it's not always the same set of images that are missing, but there are often commonalities.)

So my second question... any ideas on why some images don't load in the web interface?

Edited by JustEddy
Link to comment
Share on other sites

Hi, the embywebsocket is by design. There is no / missing there. Most likely Traefik is rejecting or redirecting the request incorrectly so you may want to look at how that is configured.

Link to comment
Share on other sites

Regarding images, can you please attach the emby server log from when that happened? Thanks.

Link to comment
Share on other sites

JustEddy
31 minutes ago, Luke said:

Hi, the embywebsocket is by design. There is no / missing there.

Hmmm... that adds to my confusion. There is a difference when I explicitly specify the domain (instead of relying on the search path, like in the log above):

Setting require baseUrl to http://emby.home/web app.js:1:57757
...
Setting server address to http://emby.home apiclient.js:1:19591
connectionManager.afterConnectValidated result.State: SignedIn connectionmanager.js:1:8721
calling apiClient.ensureWebSocket connectionmanager.js:1:6174
opening web socket with url: ws://emby.home/socket?api_key=xxx&deviceId=xxx apiclient.js:1:16902      ⬅⬅⬅⬅
returning instance from getOrAddApiClient

How does that work when the browser tries to query DNS for embywebsocket and it receives NXDOMAIN?

This doesn't work:

❯ dscacheutil -q host -a name embywebsocket
❯
❯ websocat 'ws://embywebsocket/?api_key=xxx&deviceId=xxx'
websocat: WebSocketError: I/O failure
websocat: error running
❯ 

This works:

❯ dscacheutil -q host -a name emby
name: emby.home
ip_address: 192.168.8.8
❯
❯ websocat -v 'ws://emby/socket/?api_key=xxx&deviceId=xxx'
[INFO  websocat::ws_client_peer] Connected to ws

 

Edited by JustEddy
Link to comment
Share on other sites

JustEddy
5 hours ago, Luke said:

Regarding images, can you please attach the emby server log from when that happened? Thanks.

I think I solved this half of my question. While investigating, I noticed that if I changed any of the parameters in the URL (such as maxWidth or quality), the image loaded successfully.

Eventually I did an rm cache/images/resized-images/*/* and now the images are showing up! 🎉

(My theory: I installed Emby for the first time a couple days ago, and yesterday the filesystem I was using for /config on the Docker server filled up. I'm guessing some bogus cache entries got created on the server side when that happened. I had tried clearing the client-side cache in the browsers, but it didn't have any effect. Now I know why...)

Link to comment
Share on other sites

  • 2 weeks later...
JustEddy

My reverse proxy is working fine.

There's a difference in the hostname of the websocket when I go to http://emby/ (and rely on my DNS search path) in my browser vs. me explicitly specifying the FQDN in the browser: http://emby.home/

That's what I'm illustrating in the comment above (the one where I use websocat).

When I go to http://emby/ and rely on my DNS search path, it's trying to establish a websocket to the hostname embywebsocket, which of course returns NXDOMAIN, so the WS connection fails.

opening web socket with url: ws://embywebsocket?api_key=xxx&deviceId=xxx apiclient.js:1:16902

When I go to http://emby.home/ it correctly uses emby as the hostname and the WS connection succeeds.

opening web socket with url: ws://emby.home/socket?api_key=xxx&deviceId=xxx apiclient.js:1:16902
Edited by JustEddy
Link to comment
Share on other sites

pir8radio
On 9/1/2021 at 10:47 AM, JustEddy said:

My reverse proxy is working fine.

There's a difference in the hostname of the websocket when I go to http://emby/ (and rely on my DNS search path) in my browser vs. me explicitly specifying the FQDN in the browser: http://emby.home/

That's what I'm illustrating in the comment above (the one where I use websocat).

When I go to http://emby/ and rely on my DNS search path, it's trying to establish a websocket to the hostname embywebsocket, which of course returns NXDOMAIN, so the WS connection fails.




opening web socket with url: ws://embywebsocket?api_key=xxx&deviceId=xxx apiclient.js:1:16902

When I go to http://emby.home/ it correctly uses emby as the hostname and the WS connection succeeds.




opening web socket with url: ws://emby.home/socket?api_key=xxx&deviceId=xxx apiclient.js:1:16902

is your emby server network settings setup right?    Do you have a value in "external domain"?         what do you mean, you only have this issue when there is no "." in the domain name..  are you talking on a local network?  like PC name?         http://yourpc   is all you are trying to do?  your browser will pass a "Host" header to emby, that is used to send proper responses back..    if you type  http://mypc      and something on the backend is decoding mypc to  mypc.com    that wont make it into the host header...   the proxy and emby will only see a request for mypc.  

 

for example my reverse proxy depends on the host header to serve the correct site to you..   if you go to notallmine.net you will get a response, if you setup a dns entry for poop.com and have it point to notallmine.net   you will probably find that my server rejects your requests now, because there is no service on my server called poop.com which is what it receives in the host header.  

 

Edited by pir8radio
Link to comment
Share on other sites

JustEddy

I'm not sure how else to explain it, so let me show you some packet captures.

  1. I start tcpdump:
    ❯ docker run --tty --rm --net=container:emby tcpdump tcpdump -NA 'port 8096'
  2. In my browser, I navigate to http://emby/ (my DNS search path contains home, so this looks up and works just fine)
     
  3. I see the connection in tcpdump:
    19:15:38.050076 IP traefik.55596 > emby.8096: Flags [P.], seq 1:479, ack 1, win 502, options [nop,nop,TS val 1721236114 ecr 957189224], length 478
    E....2@.@.;y.........,..}..v..r,....Z?.....
    f...9..hGET / HTTP/1.1
    Host: emby
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Safari/605.1.15
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Encoding: gzip, deflate
    Accept-Language: en-us
    Upgrade-Insecure-Requests: 1
    X-Forwarded-For: 192.168.1.199
    X-Forwarded-Host: emby
    X-Forwarded-Port: 80
    X-Forwarded-Proto: http
    X-Forwarded-Server: traefik
    X-Real-Ip: 192.168.1.199
  4. I see the 302 redirect from Emby:
    19:15:38.051612 IP emby.8096 > traefik.55596: Flags [P.], seq 1:136, ack 479, win 506, options [nop,nop,TS val 957189226 ecr 1721236114], length 135
    E... ;@.@..............,..r,}..T....X......
    9..jf...HTTP/1.1 302 Found
    Date: Wed, 08 Sep 2021 19:15:37 GMT
    Server: Kestrel
    Transfer-Encoding: chunked
    Location: web/index.html

     

  5. My browser follows the redirect:
    19:15:38.059279 IP traefik.55596 > emby.8096: Flags [P.], seq 479:971, ack 136, win 501, options [nop,nop,TS val 1721236123 ecr 957189226], length 492
    E.. .4@.@.;i.........,..}..T..r.....ZM.....
    f...9..jGET /web/index.html HTTP/1.1
    Host: emby
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Safari/605.1.15
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Encoding: gzip, deflate
    Accept-Language: en-us
    Upgrade-Insecure-Requests: 1
    X-Forwarded-For: 192.168.1.199
    X-Forwarded-Host: emby
    X-Forwarded-Port: 80
    X-Forwarded-Proto: http
    X-Forwarded-Server: traefik
    X-Real-Ip: 192.168.1.199

     

  6. Emby starts sending the web app:
    19:15:38.080858 IP emby.8096 > traefik.55596: Flags [P.], seq 136:4056, ack 971, win 503, options [nop,nop,TS val 957189256 ecr 1721236123], length 3920
    E... =@.@..............,..r.}..@....g......
    9...f...HTTP/1.1 200 OK
    Date: Wed, 08 Sep 2021 19:15:37 GMT
    Content-Type: text/html; charset=UTF-8
    Server: UPnP/1.0 DLNADOC/1.50
    Content-Length: 3162
    Accept-Ranges: bytes
    Access-Control-Allow-Headers: Accept, Accept-Language, Authorization, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, Content-Type, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, X-Emby-Token, X-Emby-Client, X-Emby-Client-Version, X-Emby-Device-Id, X-Emby-Device-Name, X-Emby-Authorization
    Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS
    Access-Control-Allow-Origin: *
    
    ...<!DOCTYPE html>
    <html data-appversion="4.6.4.0" data-culture="en-US" lang="en" class="preload">
    <head>
    
        <meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
        <link rel="preload" href="modules/fonts/material-icons/LDItaoyNOAY6Uewc665JcIzCKsKc_M9flwmP_1.woff2" as="font" type="font/woff2" crossorigin>
        ...

     

  7. The browser continues to request resources:
    19:15:38.109267 IP traefik.55596 > emby.8096: Flags [P.], seq 971:1494, ack 4056, win 501, options [nop,nop,TS val 1721236173 ecr 957189256], length 523
    E..?.6@.@.;H.........,..}..@........Zl.....
    f...9...GET /web/modules/fonts/material-icons/LDItaoyNOAY6Uewc665JcIzCKsKc_M9flwmP_1.woff2 HTTP/1.1
    Host: emby
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Safari/605.1.15
    Accept: */*
    Accept-Encoding: gzip, deflate
    Accept-Language: en-us
    Origin: http://emby
    Referer: http://emby/web/index.html
    X-Forwarded-For: 192.168.1.199
    X-Forwarded-Host: emby
    X-Forwarded-Port: 80
    X-Forwarded-Proto: http
    X-Forwarded-Server: traefik
    X-Real-Ip: 192.168.1.199

     

  8. One of the things the browser eventually requests (and executes) is /web/bower_components/emby-apiclient/apiclient.js?v=4.6.4.0 HTTP/1.1
     
  9. In apiclient.js there is a getUrl() function that basically does something like this:
    function getUrl(name, params, serverAddress) {
        var url = serverAddress || this._serverAddress;
        var lowered = url.toLowerCase();
        if (!lowered.includes("/emby") || !lowered.includes("/mediabrowser")) {
            url += "/emby";
        }
        if (name.charAt(0) !== "/") {
            url += "/";
        }
        url += name;
        if (params) {
            url += "?".concat(params)
        }
        return url;
    }

     

  10. and there is an openWebSocket() function that does this:
    var url = replaceAll(url = this.getUrl("socket"), "emby/socket", "embywebsocket");
    url = replaceAll(url, "https:", "wss:"),
    url = replaceAll(url, "http:", "ws:"),
    url += "?api_key=".concat(accessToken),
    url += "&deviceId=".concat(this.deviceId()),
    console.log("opening web socket with url: ".concat(url));

     

  11. If we break this down:
    var url = this.getUrl("socket");                                           // url = http://emby/socket
    url = replaceAll(url, "emby/socket", "embywebsocket");                     // url = http://embywebsocket
    url = replaceAll(url, "https:", "wss:");                                   // url = http://embywebsocket
    url = replaceAll(url, "http:", "ws:");                                     // url = ws://embywebsocket
    console.log("opening web socket with url: ".concat("ws://embywebsocket");  // but "embywebsocket" isn't a valid hostname!

 

Hope that is enough to detail to explain what happens when I don't use a fully-qualified name to access Emby.

 

Edited by JustEddy
Link to comment
Share on other sites

Q-Droid

Is "emby" the name of your host? I could be way off base here but are you using what appears to be a reserved literal string in those functions? Maybe not "reserved" but an unexpected value that matches a system URI being replaced?

Link to comment
Share on other sites

JustEddy

Yes, emby is the name I have assigned in DNS to my Emby server. Seems logical, and how I name almost all of my internal services to maintain sanity.

There shouldn't be anything "reserved" about the name "emby". (Also, in case it's not apparent, the code in step 10 is "as it came from the server"; step 11 is just me walking through that code to show what happens.)

I'm not sure why there is code to replace "emby/socket" with "embywebsocket" in openWebSocket(), but that substitution is the source of my problem. Which may be a side-effect of  getUrl() not appending /emby to the URL because the serverAddress does already contain /emby: http://emby so that is skipped...

opening web socket with url: ws://embywebsocket?api_key=xxx&deviceId=xxx apiclient.js:1:16902      ⬅ FAILS
returning instance from getOrAddApiClient connectionmanager.js:1:15804
Firefox can’t establish a connection to the server at ws://embywebsocket/?api_key=xxx&deviceId=xxx. apiclient.js:1:16981
web socket closed apiclient.js:1:17526
nulling out web socket

By way of comparison, "http://emby.home" becomes "ws://emby.home/socket", with the only replacement to change "http:" to "ws:" (or "https:" to "wss:")... and using the FQDN name works fine (but mildly inconvenient, since all my other services are accessible by their unqualified name, thanks to the resolver's search path.)

opening web socket with url: ws://emby.home/socket?api_key=xxx&deviceId=xxx apiclient.js:1:16902      ⬅ WORKS
returning instance from getOrAddApiClient connectionmanager.js:1:15804

I'm definitely no expert in the Emby codebase, but this is about as much digging as I can do to shed some light on where the issue appears to be so the devs can offer better insight (or hopefully a solution).

Edited by JustEddy
Link to comment
Share on other sites

JustEddy

A couple other observations about getUrl's behavior:

serverName getUrl("socket") result
http://emby http://emby/socket
http://emby.home http://emby.home/socket
http://embybug http://embybug/socket
http://embybug.home http://embybug.home/socket
http://bugemby http://bugemby/emby/socket
http://bugemby.home http://bugemby.home/emby/socket
Edited by JustEddy
Link to comment
Share on other sites

pir8radio

so does it work if you call your emby host anything other than emby....            like      http://dude     due to how its coded today, you may just have to call your emby server  "media"   or something     emby01     even though i like the Dude server...

Edited by pir8radio
Link to comment
Share on other sites

Here are more observations of ApiClient's getUrl() and openWebSocket() behavior using "dude" and "embydude" as server names, with and without the FQDN:

serverAddress getUrl("socket") returns openWebSocket() opens
http://dude http://dude/emby/socket ws://dude/embywebsocket
http://dude.home http://dude.home/emby/socket ws://dude.home/embywebsocket
http://embydude http://embydude/socket ws://embydude/socket
http://embydude.home http://embydude.home/socket ws://embydude.home/socket

Notice the difference when the server name starts with "emby"... which prevents "/emby" from getting appended by getUrl().

(and to be clear, step 11 in my post above shows how openWebSocket() generates ws://embywebsocket when the hostname is "emby", which is the issue I'm asking about in my first post...)

Edited by JustEddy
Link to comment
Share on other sites

pir8radio
18 hours ago, JustEddy said:

Here are more observations of ApiClient's getUrl() and openWebSocket() behavior using "dude" and "embydude" as server names, with and without the FQDN:

serverAddress getUrl("socket") returns openWebSocket() opens
http://dude http://dude/emby/socket ws://dude/embywebsocket
http://dude.home http://dude.home/emby/socket ws://dude.home/embywebsocket
http://embydude http://embydude/socket ws://embydude/socket
http://embydude.home http://embydude.home/socket ws://embydude.home/socket

Notice the difference when the server name starts with "emby"... which prevents "/emby" from getting appended by getUrl().

(and to be clear, step 11 in my post above shows how openWebSocket() generates ws://embywebsocket when the hostname is "emby", which is the issue I'm asking about in my first post...)

@Luke  you get what he is saying. sounds like you are doing some kind of text string/regex match might want to make that a little more strict. even though this is a rare case. that is if im reading this correctly.     

 

you can fix this by using nginx as a reverse proxy...  :D     i know not your goal..  just sayin' 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...