svenvc / zinc

Zinc HTTP Components is an open-source Smalltalk framework to deal with the HTTP networking protocol.
MIT License
96 stars 56 forks source link

Add support for Unix Domain Sockets #120

Closed JurajKubelka closed 1 year ago

JurajKubelka commented 1 year ago

Introduction

The aim of the presented changes is to be able to communicate with HTTP servers over Unix Domain Sockets.

For example to communicate with a Docker Engine, the following code can be executed from a terminal:

curl --unix-socket /var/run/docker.sock http://localhost/v1.43/containers/json

This pull request implements a ZnUnixSocketClient that can be used as follows:

ZnUnixSocketClient new 
    beOneShot;
    unixSocket: '/var/run/docker.sock';
    get: 'http://localhost/v1.43/containers/json'

ZnUnixSocketClient is a subclass of ZnClient and placed in a Zinc-HTTP-UnixSocket package.

Implementation details

svenvc commented 1 year ago

I am merging this: this is clean extension code in a separate package.

I wil now try to run it myself.

svenvc commented 1 year ago

(continuing our email conversation)

Netcat is nc, I think it is standard on macOS and Linux.

I was able to start a server using

$ sudo nc -v -l -U /var/run/test.sock

and connect to it using

$ sudo telnet -u /var/run/test.sock

just type something and hit return.

Here are some links with more info

This is not enough to build a real unit test, but it should allow users to run something.

svenvc commented 1 year ago

Now, when I try the above, I get an error: it seems the method connectTo: on Socket is not in my Pharo 11 image as called from unixSocketOnFile:

Is that something you added and forgot to include ?

svenvc commented 1 year ago

OK, with the missing connectTo: method I can run the code, I can connect with the client to a unix socket (provided I own it) on macOS. The HTTP comes through, but I need to figure out how to answer.

JurajKubelka commented 1 year ago

Thank you! Do you think that we can make the ZnServer to listen to the unix socket? I have looked at the server code, but I was not sure about it.

svenvc commented 1 year ago

I don't know.

But the first problem is that I think something is wrong with the reading part in Pharo.

If you try with the nc/telnet example (BTW, you have to delete the socket to start over), you can send data from telnet to nc and back.

The following would be a correct HTTP response:

HTTP/1.1 200 OK
Server: Zinc HTTP Components 1.0 (Pharo/11.0)
Date: Mon, 04 Sep 2023 14:12:57 GMT
Content-Type: text/plain;charset=utf-8
Content-Length: 2

OK

which goes back to telnet, but never to Pharo.

Oh, you also need a unix socket that you own yourself.

Are you sure reading works ? It seems to be stuck in waiting for data.

JurajKubelka commented 1 year ago

I am not sure it is correct. I have tested it against Docker unix socket and it works (Pharo 10 and GToolkit based on Pharo 10). There is an example of the Docker answer:

Screenshot 2023-09-04 at 11 22 46
JurajKubelka commented 1 year ago

BTW: The Unix Socket implementation is taken from here: https://samadhiweb.com/blog/2013.07.27.unixdomainsockets.html

svenvc commented 1 year ago

Ah OK, that is great.

I guess it should be possible to run nginx locally, serving on a unix domain socket. I would like to see that first.

JurajKubelka commented 1 year ago

It is good idea to use nginx. I will try.

Does it work for you on Pharo 11? I can

Screenshot 2023-09-04 at 11 30 53

It works for me on Pharo 10:

Screenshot 2023-09-04 at 11 34 03
svenvc commented 1 year ago

I am on Pharo 11

I had to use a socket like this /Users/sven/tmp/test.sock (one that is owned by my user)

I am trying to configure nginx under brew BTW

svenvc commented 1 year ago

I got it to work!

Installed nginx via brew and edited /usr/local/etc/nginx/nginx.conf

    server {
        listen       8080;
    listen       unix:/Users/sven/tmp/nginx.sock;
        server_name  localhost;

        location / {
            root   html;
            index  index.html index.htm;
        }
       ...
   }

Now I can do

telnet -u /Users/sven/tmp/nginx.sock
GET /

curl --unix-socket /Users/sven/tmp/nginx.sock http://localhost

and I get HTML back. In Pharo I can then do:

ZnUnixSocketClient new
    unixSocket: '/Users/sven/tmp/nginx.sock';
    get: 'http://localhost/'.

And I get the same HTML.

Now we can start thinking about the server side, maybe simple socket server would better to start with.

I am still not sure how the socket permissions have to be.

svenvc commented 1 year ago

But now that I am home, I also get a NetNameResolver error, where it worked before. So maybe somehow this works only once. Restarting the image does not help.

svenvc commented 1 year ago

OK, I think I found it, NetNameResolver initializeNetwork is missing (see the last comment at the end of https://samadhiweb.com/blog/2013.07.27.unixdomainsockets.html)

JurajKubelka commented 1 year ago

I see. Will you commit the change in ZnNetworkingUtils>>#unixSocketOnFile:?

BTW. Now it works for me in both, Pharo 10 and 11.

svenvc commented 1 year ago

Hi @JurajKubelka

I did a small refactoring (as per the blog post), it feels cleaner to me.

https://github.com/svenvc/zinc/commit/a6f7fa3e91c280673e15b79fc6103b1b2c0a547e

Sven

JurajKubelka commented 1 year ago

Hi @svenvc,

thank you for the refactoring. It looks better now.

Juraj