Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
HTTP over Unix domain sockets in golang
package main
import (
"context"
"flag"
"fmt"
"io"
"net"
"net/http"
"os"
"strings"
)
func main() {
post := flag.String("d", "", "data to POST")
help := flag.Bool("h", false, "usage help")
flag.Parse()
if *help || len(flag.Args()) != 2 {
fmt.Fprintln(os.Stderr, "usage:", os.Args[0], "[-d data] /path.socket /uri")
flag.PrintDefaults()
os.Exit(0)
}
fmt.Println("Unix HTTP client")
httpc := http.Client{
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", flag.Args()[0])
},
},
}
var response *http.Response
var err error
if len(*post) == 0 {
response, err = httpc.Get("http://unix" + flag.Args()[1])
} else {
response, err = httpc.Post("http://unix"+flag.Args()[1], "application/octet-stream", strings.NewReader(*post))
}
if err != nil {
panic(err)
}
io.Copy(os.Stdout, response.Body)
}
package main
import (
"fmt"
"net"
"net/http"
"os"
)
func main() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "usage:", os.Args[0], "/path.sock [wwwroot]")
return
}
fmt.Println("Unix HTTP server")
root := "."
if len(os.Args) > 2 {
root = os.Args[2]
}
os.Remove(os.Args[1])
server := http.Server{
Handler: http.FileServer(http.Dir(root)),
}
unixListener, err := net.Listen("unix", os.Args[1])
if err != nil {
panic(err)
}
server.Serve(unixListener)
}
@pbalduino

This comment has been minimized.

Copy link

@pbalduino pbalduino commented May 1, 2017

Thank you

@e4jet

This comment has been minimized.

Copy link

@e4jet e4jet commented May 4, 2017

Very nice example!

@yanickxia

This comment has been minimized.

Copy link

@yanickxia yanickxia commented May 5, 2017

Cool, Thanks

@jan4984

This comment has been minimized.

Copy link

@jan4984 jan4984 commented Jul 6, 2017

Good!

@hanneslehmann

This comment has been minimized.

Copy link

@hanneslehmann hanneslehmann commented Nov 13, 2018

Works without any issues, thanks!

@joonas-fi

This comment has been minimized.

Copy link

@joonas-fi joonas-fi commented Dec 21, 2018

Thanks!

To preserve the context, I would recommend:

				DialContext: func(ctx context.Context, _, addr string) (net.Conn, error) {
					dialer := net.Dialer{} // don't know why we need a struct to use DialContext()
					return dialer.DialContext(ctx, "unix", flag.Args()[0])
				},
@jtestard

This comment has been minimized.

Copy link

@jtestard jtestard commented Feb 3, 2019

I am not sure I understand the purpose of the os.Remove(os.Args[1]). It seems that it should delete the socket passed as an argument. However, it doesn't. Also if I remove it, I get a bind: address already in use exception.

@jtestard

This comment has been minimized.

Copy link

@jtestard jtestard commented Feb 3, 2019

It seems that it should delete the socket passed as an argument. However, it doesn't.

Or maybe it does, and then re-creates a new one? I see that after running this code, the permissions on the socket file changed.

@jtestard

This comment has been minimized.

Copy link

@jtestard jtestard commented Feb 3, 2019

Or maybe it does, and then re-creates a new one?

Yes I see that's what's happening.

@facchettos

This comment has been minimized.

Copy link

@facchettos facchettos commented Feb 18, 2019

Or maybe it does, and then re-creates a new one?

Yes I see that's what's happening.

This is because a socket can only be bound once, by recreating a new one, you just make sure that it is a brand new one that will not throw a address already in use exception. Note that if you already have a connection to a previously created socket, this connection will still access the previously created socket. (here with http it is unlikely as connections are short lived, however with classic stream connections this could happen that you have a client that access a socket that is not available anymore to the file system and does not throw an error)

@theferrit32

This comment has been minimized.

Copy link

@theferrit32 theferrit32 commented Mar 11, 2019

This http.Client transport override is very helpful, thanks for posting! The golang standard library should incorporate a more straightforward way of handling HTTP traffic over unix domain sockets.

@wr125

This comment has been minimized.

Copy link

@wr125 wr125 commented Jul 19, 2019

Nice work!

@teknoraver

This comment has been minimized.

Copy link
Owner Author

@teknoraver teknoraver commented Jul 19, 2019

Thanks!

To preserve the context, I would recommend:

				DialContext: func(ctx context.Context, _, addr string) (net.Conn, error) {
					dialer := net.Dialer{} // don't know why we need a struct to use DialContext()
					return dialer.DialContext(ctx, "unix", flag.Args()[0])
				},

Hi,

I didn't do this because it wasn't backward compatible with old Go versions

@wr125

This comment has been minimized.

Copy link

@wr125 wr125 commented Jul 19, 2019

I see!

@AlexanderMatveev

This comment has been minimized.

Copy link

@AlexanderMatveev AlexanderMatveev commented Nov 6, 2020

Thanks!

@DeadlySurgeon

This comment has been minimized.

Copy link

@DeadlySurgeon DeadlySurgeon commented Jun 28, 2021

Thanks!

To preserve the context, I would recommend:

				DialContext: func(ctx context.Context, _, addr string) (net.Conn, error) {
					dialer := net.Dialer{} // don't know why we need a struct to use DialContext()
					return dialer.DialContext(ctx, "unix", flag.Args()[0])
				},

I know this is like, 3 years late, but you need to store it as a variable because DialContext belongs to (*Dialer) not (Dialer). When you assign it a local value, it can be referenced as *Dialer. A way around this, is to do this:

return (&net.Dialer{}).DialContext(ctx, "unix", flags.Args()[0])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment