neo4j / neo4j-javascript-driver

Neo4j Bolt driver for JavaScript

Home Page:https://neo4j.com/docs/javascript-manual/current/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Bug] `parseDatabaseUrl` removes path from URL

luchillo17 opened this issue · comments

Bug Report

The parseDatabaseUrl function completely ignores the path, these URLs are stripped from path resulting in the wrong web socket request, this happens when we use Ingress to serve the load balancer from a path deeper than the root:

  • neo4j://localhost/neo4j/bolt gets transformed into localhost:7687, the port change kind of makes sense since the driver assumes the default port, but the path is missing.
  • neo4j://localhost:80/neo4j/bolt gets transformed into localhost:80 which breaks because I expected the web socket request to land in ws://localhost:80/neo4j/bolt.

To be more precise what I'm doing is deploying the neo4j helm chart & then using Ingress to put the load balancer service behind a path in the cluster, it is clear in the function the query is never introduced again into the URL:

image

Please note that query is being passed down but the path isn't.

Location of the function: https://github.com/neo4j/neo4j-javascript-driver/blob/5.0/packages/core/src/internal/url-util.ts#L93-L108

My Environment

image

software name version
OS Windows 10 WSL2 Ubuntu
Web browser Chrome 113.0.5672.129
node.js lts/gallium 16.20.0
npm 8.19.4
yarn 1.22.19
Neo4j Browser neo4j/neo4j-browser#1922
Neo4j -

In case anyone is curious about my setup, the reason I had to use Ingress is because of a limitation on Docker Desktop on Windows, that doesn't forward the ports of the cluster to the host, however, another use case is what I mentioned, instead of a neo4j database available at root / we could route it with Ingress and put it under a path like example.com/app1/db.

https://gist.github.com/luchillo17/68c7496b0d5b0b9cd791c236f5924ad2

The driver connection url doesn't support paths.

Since the goal of the driver is keeping parity of functionality between different environments and languages, path in the url is not supported.

Supporting path can be handy for websockets since its api supported it. However, this feature is not supported by regular sockets.

About connection url see:
https://neo4j.com/docs/javascript-manual/current/connect-advanced/
https://neo4j.com/docs/java-manual/current/client-applications/#java-driver-connection-uris

That's a bummer, though I do lack context on the other languages like Java & Python, so basically the closest thing would be to expose the DB with an Ingress that uses a subdomain instead of a path like db.example.com:7467 right?, wonder if I can make it work with nip.io DNS proxy ips...

Yes, you should use subdomains because they will be resolve via dns.

It worked perfectly, thanks.

Nevermind, Browser works fine, but the Driver doesn't, this is what I get from the error when connecting with the driver and Apollo GraphQL
Ingress HTTP port for browser: http://neo4j.[IP].nip.io/browser/
Ingress TCP port for db connection: bolt://bolt.[IP].nip.io:80
For some reason Browser needs to use bolt:// protocol instead of neo4j:// to connect properly, but no matter what I pass to the driver in my app, I get this error:

"Neo4jError: Server responded HTTP. Make sure you are not trying to connect to the http endpoint (HTTP defaults to port 7474 whereas BOLT defaults to port 7687)",
          "    at new Neo4jError (webpack-internal:///(sc_server)/../../node_modules/.pnpm/neo4j-driver-core@5.9.1/node_modules/neo4j-driver-core/lib/error.js:76:16)",
          "    at newError (webpack-internal:///(sc_server)/../../node_modules/.pnpm/neo4j-driver-core@5.9.1/node_modules/neo4j-driver-core/lib/error.js:108:12)",
          "    at parseNegotiatedResponse (webpack-internal:///(sc_server)/../../node_modules/.pnpm/neo4j-driver-bolt-connection@5.9.1/node_modules/neo4j-driver-bolt-connection/lib/bolt/handshake.js:59:48)",
          "    at channel.onmessage (webpack-internal:///(sc_server)/../../node_modules/.pnpm/neo4j-driver-bolt-connection@5.9.1/node_modules/neo4j-driver-bolt-connection/lib/bolt/handshake.js:106:39)",
          "    at Socket.eval (webpack-internal:///(sc_server)/../../node_modules/.pnpm/neo4j-driver-bolt-connection@5.9.1/node_modules/neo4j-driver-bolt-connection/lib/channel/node/node-channel.js:167:26)",
          "    at Socket.emit (node:events:513:28)",
          "    at addChunk (node:internal/streams/readable:324:12)",
          "    at readableAddChunk (node:internal/streams/readable:297:9)",
          "    at Readable.push (node:internal/streams/readable:234:10)",
          "    at TCP.onStreamRead (node:internal/stream_base_commons:190:23)",
          "    at TCP.callbackTrampoline (node:internal/async_hooks:130:17)"

Here's what :debug has to say about my subdomain Ingress URL, not sure why Browser works but NodeJS Driver doesn't:
2023-06-10_00h15_27

This error means the driver is trying to connect to a http server. The NodeJS version of the driver doesn't make use of WebSockets. Neo4j Browser makes use of the javascript driver, but the driver uses websockets on the browsers.

Makes no sense to me why it works ok when using kubectl port-forward svc/neo4j-db-lb-neo4j tcp-bolt http https but if I use Ingress to expose it, then it doesn't work

If the driver can connect properly to the port-forwarded tcp-bolt but not to the same exposed through Ingress, either I'm configuring Ingress wrong or my Ingress implementation (nginx-ingress) can't handle the protocol...

You can try to curl or telnet bolt://bolt.[IP].nip.io:80, they are probably returning the http header.

Hi @luchillo17

I just want to share this draft PR with you as it might be helpful.

We are planning to release a new Helm Chart that provides a thin wrapper over the HAProxy helm chart. This will allow standard Kubernetes Ingress to work well with a Neo4j deployment.
It's not ideal as it means an extra service to manage but it seems to work well so far.