Unable to talk to docker daemon through sock file
I was trying to use cpp-httplib to communicate with my local docker daemon using sock file. There is a possibility to do that according to this
After testing my connection with curl I was pretty sure that it will be easy to do however problem (IMO) is caused by the fact that docker engine is expecting to receive request on endpoint http://localhost/v1.39/images/json and not /v1.39/images/json. I can not see how to do that using httplib::Client object because its only argument is a path to sock file.
Also curl is working with that socket curl --unix-socket /run/docker.sock /v1.39/images/json
I am attaching a short example comparing httplib and raw socket usage
#if defined(HTTP_LIB)
#include <httplib.h>
#include <iostream>
#include <sys/socket.h>
#else
#include <iostream>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <cstring>
#endif
int main(){
std::string dockerSocket("/run/docker.sock");
std::string endpoint("http://localhost/v1.39/images/json");
#if defined(HTTP_LIB)
httplib::Client cli(dockerSocket);
cli.set_address_family(AF_UNIX);
auto res = cli.Get(endpoint);
if (res && res->status == 200) {
std::cout << "Response from Docker Engine: "<< res->body << std::endl;
} else {
if (res) {
std::cout << "HTTP Error: " << res->status << std::endl;
} else {
std::cout << "Connection failed: " << httplib::to_string(res.error()) << std::endl;
}
}
#else
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
try{
const char* data = "GET http://localhost/v1.39/images/json HTTP/1.1\r\n"
"Host: localhost\r\n"
"Connection: close\r\n\r\n";
if (sockfd < 0) {
throw std::runtime_error("Failed to create socket");
}
sockaddr_un addr{};
addr.sun_family = AF_UNIX;
std::strncpy(addr.sun_path, dockerSocket.c_str(), sizeof(addr.sun_path) - 1);
if (connect(sockfd, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)) < 0) {
throw std::runtime_error("Failed to connect to the socket");
}
if (send(sockfd, data, strlen(data), 0) < 0) {
throw std::runtime_error("Failed to send data");
}
char buffer[4096] = {};
while(true)
{
ssize_t bytesReceived = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
if (bytesReceived <= 0) {
break;
}
buffer[bytesReceived] = '\0';
std::cout << buffer << std::endl;
}
}
catch(const std::exception &ex){
std::cout << "Error: " << ex.what() << std::endl;
}
if (sockfd >= 0){
close(sockfd);
}
#endif
return 0;
}
g++ example.cpp && ./a.out
HTTP/1.1 200 OK
Api-Version: 1.46
Content-Type: application/json
Docker-Experimental: false
Ostype: linux
Server: Docker/27.1.2 (linux)
Date: Tue, 14 Jan 2025 17:08:12 GMT
Connection: close
Transfer-Encoding: chunked
f6b
g++ example.cpp -DHTTP_LIB && ./a.out
HTTP Error: 400
I am using httplib 0.18.3 from vcpkg, but I also tried with trunk version. My GCC is 14.2.1 and Docker 27.1.2
@bialasjaroslaw thanks for the report. What is the request line of the curl request? You can check with curl --unix-socket /run/docker.sock /v1.39/images/json -v.
Also what HTTP status code will be returned with auto res = cli.Get("/v1.39/images/json");?
@yhirose thanks for response. Here are the responses for curl for both with and without localhost
curl --unix-socket /run/docker.sock /v1.39/images/json -v
* URL rejected: No host part in the URL
* closing connection #-1
curl: (3) URL rejected: No host part in the URL
curl --unix-socket /run/docker.sock localhost/v1.39/images/json -v
* Trying /run/docker.sock:0...
* Connected to localhost (/run/docker.sock) port 0
> GET /v1.39/images/json HTTP/1.1
> Host: localhost
> User-Agent: curl/8.9.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Api-Version: 1.46
< Content-Type: application/json
< Docker-Experimental: false
< Ostype: linux
< Server: Docker/27.1.2 (linux)
< Date: Fri, 17 Jan 2025 07:53:32 GMT
< Transfer-Encoding: chunked
<
...
Running c++ app with auto res = cli.Get("/v1.39/images/json");
HTTP Error: 400
I tried various things and I could omit localhost/ for curl as long as it is not starting from /
curl --unix-socket /run/docker.sock v1.39/images/json -v
* Trying /run/docker.sock:0...
* Connected to v1.39 (/run/docker.sock) port 0
> GET /images/json HTTP/1.1
> Host: v1.39
> User-Agent: curl/8.9.1
> Accept: */*
>
...
I can even type whatever I want for host. The only requirement, that seems to be necessary, is to have something hostlike before /images/json(seems that protocol version is also not mandatory)
curl --unix-socket /run/docker.sock whatever/v1.39/images/json -v
* Trying /run/docker.sock:0...
* Connected to whatever (/run/docker.sock) port 0
> GET /v1.39/images/json HTTP/1.1
> Host: whatever
> User-Agent: curl/8.9.1
> Accept: */*
>
...
I tried the same things with raw sockets and it is not working the same way curl does (protocol is required)
# These 2 are not working
"GET localhost/v1.39/images/json HTTP/1.1\r\n"
"GET v1.39/images/json HTTP/1.1\r\n"
# This one still works as expected
"GET http://whatever/images/json HTTP/1.1\r\n"
Result from first two examples
HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
Connection: close
400 Bad Request
Result from last example
HTTP/1.1 200 OK
Api-Version: 1.46
Content-Type: application/json
Docker-Experimental: false
Ostype: linux
Server: Docker/27.1.2 (linux)
Date: Fri, 17 Jan 2025 08:13:35 GMT
Connection: close
Transfer-Encoding: chunked
...
This is so confusing because I thought that there is some kind of host validation on the docker side.
Thanks. How about curl --unix-socket /run/docker.sock http://localhost/v1.39/images/json -v?
Also if you call curl --unix-socket /run/docker.sock whatever/v1.39/images/json -v, did you receive a 200 response?
Also what if the following change in your code?
const char* data = "GET /v1.39/images/json HTTP/1.1\r\n"
"Host: localhost\r\n"
"Connection: close\r\n\r\n";
One more. 😄 How about curl --unix-socket /run/docker.sock /v1.39/images/json -H 'Host: localhost' -v?
@yhirose thank you for response and all of your suggestions. I found a solution, but first I will put an output of all the things you asked for.
curl --unix-socket /run/docker.sock http://localhost/v1.39/images/json -v
* Trying /run/docker.sock:0...
* Connected to localhost (/run/docker.sock) port 0
> GET /v1.39/images/json HTTP/1.1
> Host: localhost
> User-Agent: curl/8.9.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Api-Version: 1.46
< Content-Type: application/json
< Docker-Experimental: false
< Ostype: linux
< Server: Docker/27.1.2 (linux)
< Date: Fri, 17 Jan 2025 18:20:20 GMT
< Transfer-Encoding: chunked
<
curl --unix-socket /run/docker.sock whatever/v1.39/images/json -v
* Trying /run/docker.sock:0...
* Connected to whatever (/run/docker.sock) port 0
> GET /v1.39/images/json HTTP/1.1
> Host: whatever
> User-Agent: curl/8.9.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Api-Version: 1.46
< Content-Type: application/json
< Docker-Experimental: false
< Ostype: linux
< Server: Docker/27.1.2 (linux)
< Date: Fri, 17 Jan 2025 18:21:18 GMT
< Transfer-Encoding: chunked
<
C++ code with header Host
HTTP/1.1 200 OK
Api-Version: 1.46
Content-Type: application/json
Docker-Experimental: false
Ostype: linux
Server: Docker/27.1.2 (linux)
Date: Fri, 17 Jan 2025 18:22:01 GMT
Connection: close
Transfer-Encoding: chunked
curl --unix-socket /run/docker.sock /v1.39/images/json -H 'Host: localhost' -v
* URL rejected: No host part in the URL
* closing connection #-1
curl: (3) URL rejected: No host part in the URL
Solution: Last suggestion of yours give me something to think about, so I tried this:
httplib::Client cli("/run/docker.sock");
cli.set_address_family(AF_UNIX);
httplib::Headers headers{
{"Host", "localhost"},
{"Connection", "close"},
};
auto res = cli.Get("/images/json", headers);
This is it. Host header make it work. It is good enough for me and my personal project. I could provide more information if you need anything. However if this is not something that requires code changes I would be happy to finish out small investigation. Thank you for your help and patience. If there is nothing else to clarify, I believe that thread can be closed.
Fantastic! I'll put 'information' tag, so that others could benefit from the solution. Thanks!