Goal: Use the HttpClient to communicate with a ReST API through a UNIX socket provided by a daemon.
Details: Podman is providing a brand new ReST API (v2.0) and the podman daemon creates a unix socket to which we can connect and start talking with the application using HTTP.
Issue: I've noticed that HttpClient object has a socket property inherited from HttpClientBase[SocketType] but the property is not visible for the outside scope of the module so cannot alter that directly and the newHttpClient also does not provide an argument to override it as well.
Question: How would be the idiomatic way to achieve achieve the goal?
Thoughts so far:
1 - I've tried to sort of override the getSocket function but I don't it worked at all. (to be fair i didn't even knew if it would be possible or not)
proc getSocket * (h: HttpClient): Socket =
var socket = newSocket(AF_UNIX, SOCK_STREAM)
echo("getSocket Override")
return socket
2 - Maybe trying to create a second class called HttpUnixSocketClient inherited from HttpClient or HttpClientBase and try to replace the socket at the initializer. But I'm not so sure if it would work since I'm a bit clueless how Inheritance would work in here.
3 - Sort of re-implementing the http workflows (terrible one. hope this is not the way.)
4 - Open a PR requesting that we either have a setter for the socket property or make it public.
Not looking to be spoon fed so it should enough just to say one of the numbers listed as solution or just say "none of them, try search for this [term] instead". Appreciate your time reading my issue.
Thanks!
In Python i was able to override a class method that handler to socket connection. Bellow is the simplified sample:
from http.client import HTTPConnection # Core Library
class HTTPConnectionUnixSocket(HTTPConnection):
def connect(self):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.settimeout(self.TIMEOUT)
sock.connect(self.uri.path)
self.sock = sock
Later on you would be able to consume this class like this:
ROOT_SOCKET = 'unix://localhost/run/podman/podman.sock'
http = HTTPConnectionSocket(ROOT_SOCKET)
http.request( ... )
http.getresponse()
Option 1 won't work, it's just a duplicate method and I assume the compiler will complain.
Option 2 is workable but requires minor changes to the HttpClient class, because Nim objects don't support dynamic dispatching by default. (In C++ terms, the overriding is non-virtual.) So if you implement a proc with the same name but the first parameter is a subclass, then which proc is called depends on the compile-time class of the first parameter.
You can get dynamic dispatching if you use the keyword method instead of proc. But this has to be done in the base class, i.e. the existing proc getSocket *(h: HttpClient) in the standard library has to be changed to method.
But (after looking at the source code) getSocket isn't the right method to override. The method that creates the socket is newConnection.
This is likely to be more complicated than it looks, though, because the library code is using generics to support both synchronous and async networking, so the actual class is HttpClientBase whose socket property is generic...
I guess I will have to gave up on this one for now. Need more time to get used to the language to tackle this one on my own. But I'm still impressed how fast it was to build and test all the other parts not related to the socket itself.
Appreciate all the replies on the issue. Thanks All.
The solution here to me sounds simple, just implement UNIX socket support in the stdlib by supporting URIs like: unix://localhost/run/podman/podman.sock :)
Happy to give pointers on how to do that if you want to tackle it.