티스토리 뷰

Technology/Golang

HTTP Over Unix Socket

캡틴테크 2017. 5. 17. 00:58
반응형

In terms of HTTP server, we used to utilize the server and find easily. But almost of http server is based on 'TCP'. I don't think the TCP is the best option for embedded system and I don't want to abuse port in my system. Therefore I have decided to use Unix Domain Socket for IPC. 

# HTTP

 Using CRUD method which are Create, Read, Update and Delete, we can handle and provide a web service. In addition to that, REST API is much more powerful to use and pretty common in these-day. Golang also provides own library to use http server and there is famous 3rd party library for http server which is 'Gorilla'(http://www.gorillatoolkit.org/). Today, I try to develop http server over UDS and setup routing with gorilla's library.

Here is simple http server with net library in golang.

package main

import (
	"net/http"
)

func main() {
	http.HandleFunc("/hello", func(w http.ResponseWriter, req *http.Request) {
		w.Write([]byte("Hello World"))
	})

	http.ListenAndServe(":5000", nil)
}

net/http library is already included in golang and If you import 'net/http' you can use apis in net/http. HandleFunc and ListenAndServce apis in net/http I used in this code. You can see the running server if you compile the code and run. Plus 'http://localhost:5000 is default address of the web server. HandFunc is kind of router and '/hello' is added before listening the server. 'http://localhost:5000/hello' can be returned 'Hello World' string. Let's see the parameters in http.HandleFunc. There are two parameters, one is path and the other is user function which has two parameters like http.ResponseWriter and http.Request. If you want return with values, write something i http.ResponseWriter like the code.

# Unix Domain Socket

In order to use IPC, share a socket which is for writing and reading some data. Comparing to TCP socket, Unix Socket is much simpler. Here is sample code how to read and write using Unix Socket.

server.go
package main

import (
	"io"
	"log"
	"net"
	"os"
)

func main() {
	file := "/tmp/uds-server.sock"
	
	defer os.Remove(file)
	listener, err := net.Listen("unix", file)
	if err != nil {
		log.Printf("error: %v\n", err)
		return
	}
	for {
		conn, err := listener.Accept()
		if err != nil {
			break
		}
		go func() {
			defer conn.Close()
			data := make([]byte, 0)
			for {
				buf := make([]byte, 512)
				nr, err := conn.Read(buf)
				if err != nil {
					if err != io.EOF {
						log.Printf("error: %v", err)
					}
					break
				}
				buf = buf[:nr]
				log.Printf("receive: %s\n", string(buf))
				data = append(data, buf...)
			}
			log.Printf("data : %s\n", string(data)) 
    	                if err != nil {
    		              log.Printf("error: %v\n", err)
    		              return
    	                }
		}()
	}
}
client.go
package main

import (
	"fmt"
	"log"
	"net"
	"os"
)

func main() {
	if len(os.Args) != 1 {
		return
	}
	file := "/tmp/uds-server.sock"
	message := os.Args[1]
	conn, err := net.Dial("unix",file)
	if err != nil {
		log.Printf("error: %v\n", err)
		return
	}
	defer conn.Close()
	_, err = conn.Write([]byte(message))
	if err != nil {
		log.Printf("error: %v\n", err)
		return
	}
	log.Printf("send: %s\n", message)
	err = conn.(*net.UnixConn).CloseWrite()
	if err != nil {
		log.Printf("error: %v\n", err)
		return
	}
	data := make([]byte, 0)
	for {
		buf := make([]byte, 512)
		nr, err := conn.Read(buf)
		if err != nil {
			break
		}
		buf = buf[:nr]
		log.Printf("receive: %s\n", string(buf))
		data = append(data, buf...)
	}
	fmt.Printf("%s\n", string(data))
}

/tmp/uds-server.sock is the file which can be share each other between processes. Here is the client code how to write and read based on Unix Socket. client.go will access the Unix socket If the process runs with message.

# HTTP Over Unix Socket

Well, here is key point and what I want to explain. How to run http server over unix socket. Let's see the code first.

import (
	"fmt"
	"log"
	"net"
	"os"
)

func main() {
        file := "/tmp/uds-server.sock"
        listener, err := net.Listen("unix", file)
        if err != nil {
		log.Fatalf("Could not listen on %s: %v", listenAddress, err)
		return
	}
        defer listener.Close()
        if err = http.Serve(listener, nil); err != nil {
		log.Fatalf("Could not start HTTP server: %v", err)
	}
}

If you set up the router, you can reach what you want. The code starts http server over unix socket. "unix" protocol is required in net.Listen when you want to use unix socket.

Today we check http, unix socket and http over unix socket. Golang is marvelous how it is simple. Next time, I will come with new informative topic~! 


반응형
댓글