Satish Burnwal
Satish Burnwal

Reputation: 567

golang unix domain socket based http server does not work

I am trying to write a simple Unix domain socket based http server in golang. Here is the code:

package main

import (
    "fmt"
    "log"
    "net/http"
    "net"
    "os"
    "encoding/json"
    "os/signal"
    "syscall"
)

type UnixListener struct {
    ServerMux *http.ServeMux
    SocketPath string
    UID int
    GID int
    SocketFileMode os.FileMode
}

//////////////////////////////////////////////////////////////////////
// Start the HTTP server with UNIX socket.
//////////////////////////////////////////////////////////////////////
func (l *UnixListener) Start() error {
    listener, err := net.Listen("unix", l.SocketPath)
    if err != nil {
        return err
    }
    if err := os.Chown(l.SocketPath, l.UID, l.GID); err != nil {
        return err
    }
    if err := os.Chmod(l.SocketPath, l.SocketFileMode); err != nil {
        return err
    }
    go func(){
        shutdown(listener)
    }()

    svr := http.Server{ 
        Handler: l.ServerMux,
    }

    if err := svr.Serve(listener); err != nil {
        return err
    } else {
        return nil
    }
}


//////////////////////////////////////////////////////////////////////
// Shutdown the HTTP server.
//////////////////////////////////////////////////////////////////////
func shutdown(listener net.Listener) {
    c := make(chan os.Signal, 1)
    signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
    s := <-c
    listener.Close()
    log.Fatalf("[FATAL] Caught a signal. sinal=%s", s)
}


func hello(w http.ResponseWriter, r *http.Request) {
    log.Println("Received request %s", r.RequestURI)
    fmt.Fprintf(w, "Hello World!\n")
    w.Header().Set("ContentType", "application/json")
    w.WriteHeader(http.StatusOK)
    data := map[string]string { "name" : "satish"}
    resp, _ := json.Marshal(data)
    if _, err := w.Write(resp); err != nil {
        fmt.Printf("Error writing response")
    }
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/hello", hello)
    unixListener := UnixListener{
        ServerMux: mux,
        SocketPath: "/temp/test.sock",
        UID: 501,
        GID: 20,
        SocketFileMode: 0644,
    }
    if err := unixListener.Start(); err != nil {
        log.Fatalf("[FATAL] HTTP server error: %s", err)
    }
}

But when I send the request to this UDS based server like below, the handler does not seem to be invoked, giving 404 not found error. What is the right way to implement UDS based http server and how to send the http request to it?

curl -vvv  --unix-socket /temp/test.sock  http:/hello
*   Trying /Users/sburnwal/Projects/ACI/temp/test.sock:0...
* Connected to hello (/temp/test.sock) port 80 (#0)
> GET / HTTP/1.1
> Host: hello
> User-Agent: curl/7.84.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 Not Found
< Content-Type: text/plain; charset=utf-8
< X-Content-Type-Options: nosniff
< Date: Mon, 14 Nov 2022 02:38:11 GMT
< Content-Length: 19
< 
404 page not found
* Connection #0 to host hello left intact

Upvotes: 1

Views: 743

Answers (1)

selbie
selbie

Reputation: 104589

It appears it's interpreting http:/hello in your command line as the host name and using that for the header. And then requesting / as the path

Try replacing http:/hello in your command line with http://hello/hello. I'm not sure if the single slash vs double-slash is significant. It just looks suspicious.

curl -vvv  --unix-socket /temp/test.sock  http://hello/hello

Or change your Go code to have a response handler for "/" instead of just for "/hello"

mux.HandleFunc("/hello", hello)
mux.HandleFunc("/", hello)

Upvotes: 2

Related Questions