[Server] Listen and serve on a unix socket
Allow to use a unix socket as a listener. To specify an endpoint type we use an optional configuration field 'net', as there's no way to distinguish a relative socket path from a hostname. Signed-off-by: Anton Tiurin <noxiouz@yandex.ru>
This commit is contained in:
parent
ced8a0378b
commit
ad80cbe1ea
5 changed files with 157 additions and 55 deletions
|
@ -21,6 +21,7 @@ import (
|
|||
_ "github.com/docker/distribution/registry/auth/silly"
|
||||
_ "github.com/docker/distribution/registry/auth/token"
|
||||
"github.com/docker/distribution/registry/handlers"
|
||||
"github.com/docker/distribution/registry/listener"
|
||||
_ "github.com/docker/distribution/registry/storage/driver/azure"
|
||||
_ "github.com/docker/distribution/registry/storage/driver/filesystem"
|
||||
_ "github.com/docker/distribution/registry/storage/driver/inmemory"
|
||||
|
@ -67,14 +68,26 @@ func main() {
|
|||
go debugServer(config.HTTP.Debug.Addr)
|
||||
}
|
||||
|
||||
if config.HTTP.TLS.Certificate == "" {
|
||||
context.GetLogger(app).Infof("listening on %v", config.HTTP.Addr)
|
||||
if err := http.ListenAndServe(config.HTTP.Addr, handler); err != nil {
|
||||
server := &http.Server{
|
||||
Handler: handler,
|
||||
}
|
||||
|
||||
ln, err := listener.NewListener(config.HTTP.Net, config.HTTP.Addr)
|
||||
if err != nil {
|
||||
context.GetLogger(app).Fatalln(err)
|
||||
}
|
||||
} else {
|
||||
defer ln.Close()
|
||||
|
||||
if config.HTTP.TLS.Certificate != "" {
|
||||
tlsConf := &tls.Config{
|
||||
ClientAuth: tls.NoClientCert,
|
||||
NextProtos: []string{"http/1.1"},
|
||||
Certificates: make([]tls.Certificate, 1),
|
||||
}
|
||||
|
||||
tlsConf.Certificates[0], err = tls.LoadX509KeyPair(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key)
|
||||
if err != nil {
|
||||
context.GetLogger(app).Fatalln(err)
|
||||
}
|
||||
|
||||
if len(config.HTTP.TLS.ClientCAs) != 0 {
|
||||
|
@ -99,18 +112,16 @@ func main() {
|
|||
tlsConf.ClientCAs = pool
|
||||
}
|
||||
|
||||
context.GetLogger(app).Infof("listening on %v, tls", config.HTTP.Addr)
|
||||
server := &http.Server{
|
||||
Addr: config.HTTP.Addr,
|
||||
Handler: handler,
|
||||
TLSConfig: tlsConf,
|
||||
ln = tls.NewListener(ln, tlsConf)
|
||||
context.GetLogger(app).Infof("listening on %v, tls", ln.Addr())
|
||||
} else {
|
||||
context.GetLogger(app).Infof("listening on %v", ln.Addr())
|
||||
}
|
||||
|
||||
if err := server.ListenAndServeTLS(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key); err != nil {
|
||||
if err := server.Serve(ln); err != nil {
|
||||
context.GetLogger(app).Fatalln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintln(os.Stderr, "usage:", os.Args[0], "<config>")
|
||||
|
|
|
@ -54,6 +54,9 @@ type Configuration struct {
|
|||
// Addr specifies the bind address for the registry instance.
|
||||
Addr string `yaml:"addr,omitempty"`
|
||||
|
||||
// Net specifies the net portion of the bind address. A default empty value means tcp.
|
||||
Net string `yaml:"net,omitempty"`
|
||||
|
||||
Prefix string `yaml:"prefix,omitempty"`
|
||||
|
||||
// Secret specifies the secret key which HMAC tokens are created with.
|
||||
|
|
|
@ -61,6 +61,7 @@ var configStruct = Configuration{
|
|||
},
|
||||
HTTP: struct {
|
||||
Addr string `yaml:"addr,omitempty"`
|
||||
Net string `yaml:"net,omitempty"`
|
||||
Prefix string `yaml:"prefix,omitempty"`
|
||||
Secret string `yaml:"secret,omitempty"`
|
||||
TLS struct {
|
||||
|
|
|
@ -810,6 +810,7 @@ configuration may contain both.
|
|||
```yaml
|
||||
http:
|
||||
addr: localhost:5000
|
||||
net: tcp
|
||||
prefix: /my/nested/registry/
|
||||
secret: asecretforlocaldevelopment
|
||||
tls:
|
||||
|
@ -838,7 +839,20 @@ The `http` option details the configuration for the HTTP server that hosts the r
|
|||
yes
|
||||
</td>
|
||||
<td>
|
||||
The <code>HOST:PORT</code> for which the server should accept connections.
|
||||
The address for which the server should accept connections. The form depends on a network type (see <code>net</code> option):
|
||||
<code>HOST:PORT</code> for tcp and <code>FILE</code> for a unix socket.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>net</code>
|
||||
</td>
|
||||
<td>
|
||||
no
|
||||
</td>
|
||||
<td>
|
||||
The network which is used to create a listening socket. Known networks are <code>unix</code> and <code>tcp</code>.
|
||||
The default empty value means tcp.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -1293,4 +1307,3 @@ middleware:
|
|||
>**Note**: Cloudfront keys exist separately to other AWS keys. See
|
||||
>[the documentation on AWS credentials](http://docs.aws.amazon.com/AWSSecurityCredentials/1.0/AboutAWSCredentials.html#KeyPairs)
|
||||
>for more information.
|
||||
|
||||
|
|
74
registry/listener/listener.go
Normal file
74
registry/listener/listener.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
package listener
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
|
||||
// connections. It's used by ListenAndServe and ListenAndServeTLS so
|
||||
// dead TCP connections (e.g. closing laptop mid-download) eventually
|
||||
// go away.
|
||||
// it is a plain copy-paste from net/http/server.go
|
||||
type tcpKeepAliveListener struct {
|
||||
*net.TCPListener
|
||||
}
|
||||
|
||||
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
|
||||
tc, err := ln.AcceptTCP()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tc.SetKeepAlive(true)
|
||||
tc.SetKeepAlivePeriod(3 * time.Minute)
|
||||
return tc, nil
|
||||
}
|
||||
|
||||
// NewListener announces on laddr and net. Accepted values of the net are
|
||||
// 'unix' and 'tcp'
|
||||
func NewListener(net, laddr string) (net.Listener, error) {
|
||||
switch net {
|
||||
case "unix":
|
||||
return newUnixListener(laddr)
|
||||
case "tcp", "": // an empty net means tcp
|
||||
return newTCPListener(laddr)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown address type %s", net)
|
||||
}
|
||||
}
|
||||
|
||||
func newUnixListener(laddr string) (net.Listener, error) {
|
||||
fi, err := os.Stat(laddr)
|
||||
if err == nil {
|
||||
// the file exists.
|
||||
// try to remove it if it's a socket
|
||||
if !isSocket(fi.Mode()) {
|
||||
return nil, fmt.Errorf("file %s exists and is not a socket", laddr)
|
||||
}
|
||||
|
||||
if err := os.Remove(laddr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if !os.IsNotExist(err) {
|
||||
// we can't do stat on the file.
|
||||
// it means we can not remove it
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return net.Listen("unix", laddr)
|
||||
}
|
||||
|
||||
func isSocket(m os.FileMode) bool {
|
||||
return m&os.ModeSocket != 0
|
||||
}
|
||||
|
||||
func newTCPListener(laddr string) (net.Listener, error) {
|
||||
ln, err := net.Listen("tcp", laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tcpKeepAliveListener{ln.(*net.TCPListener)}, nil
|
||||
}
|
Loading…
Reference in a new issue