initial commit
This commit is contained in:
commit
19bcef65fa
4 changed files with 172 additions and 0 deletions
24
LICENSE
Normal file
24
LICENSE
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
Copyright (C) 2016 Marius Schellenberger
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* The names of the authors and/or contributors may not be used to
|
||||||
|
endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL ston1th BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
1
README.md
Normal file
1
README.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
# graceful - a simple library for graceful tcp connection handling in Go
|
||||||
53
graceful.go
Normal file
53
graceful.go
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Package graceful provides a graceful tcp handling library
|
||||||
|
package graceful
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const network = "tcp"
|
||||||
|
|
||||||
|
var ErrInterfaceConversion = errors.New("interface conversion failed")
|
||||||
|
|
||||||
|
// Listener is a wrapper for net.TCPListener
|
||||||
|
type Listener struct {
|
||||||
|
*net.TCPListener
|
||||||
|
|
||||||
|
keepAlive time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// New wraps Listener around net.TCPListener
|
||||||
|
func New(laddr string, keepAlive time.Duration) (*Listener, error) {
|
||||||
|
l, err := net.Listen(network, laddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tcpl, ok := l.(*net.TCPListener)
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrInterfaceConversion
|
||||||
|
}
|
||||||
|
return &Listener{
|
||||||
|
TCPListener: tcpl,
|
||||||
|
keepAlive: keepAlive,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept wraps net.TCPListener.AcceptTCP method
|
||||||
|
func (l *Listener) Accept() (net.Conn, error) {
|
||||||
|
conn, err := l.AcceptTCP()
|
||||||
|
if err != nil {
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
if l.keepAlive > 0 {
|
||||||
|
conn.SetKeepAlive(true)
|
||||||
|
conn.SetKeepAlivePeriod(l.keepAlive)
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop stops the listener
|
||||||
|
func (l *Listener) Stop() {
|
||||||
|
l.TCPListener.Close()
|
||||||
|
}
|
||||||
94
tls.go
Normal file
94
tls.go
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
package graceful
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TLSListener is a TLS wrapper for net.TCPListener
|
||||||
|
type TLSListener struct {
|
||||||
|
// protects conns
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
*net.TCPListener
|
||||||
|
|
||||||
|
config *tls.Config
|
||||||
|
conns map[net.Conn]struct{}
|
||||||
|
keepAlive time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSConn is a net.Conn wrapper
|
||||||
|
type TLSConn struct {
|
||||||
|
net.Conn
|
||||||
|
*TLSListener
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close wraps the net.Conn.Close method and removes the conn from the listener map
|
||||||
|
func (c *TLSConn) Close() error {
|
||||||
|
c.RemoveConn(c.Conn)
|
||||||
|
return c.Conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTLS wraps TLSListener around net.TCPListener
|
||||||
|
func NewTLS(laddr string, config *tls.Config, keepAlive time.Duration) (*TLSListener, error) {
|
||||||
|
if config == nil || len(config.Certificates) == 0 {
|
||||||
|
return nil, errors.New("NewTLS: no certificates in configuration")
|
||||||
|
}
|
||||||
|
l, err := net.Listen(network, laddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ltcp, ok := l.(*net.TCPListener)
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrInterfaceConversion
|
||||||
|
}
|
||||||
|
return &TLSListener{
|
||||||
|
TCPListener: ltcp,
|
||||||
|
config: config,
|
||||||
|
conns: make(map[net.Conn]struct{}),
|
||||||
|
keepAlive: keepAlive,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept wraps the net.TCPListener.AcceptTCP method
|
||||||
|
func (l *TLSListener) Accept() (net.Conn, error) {
|
||||||
|
conn, err := l.AcceptTCP()
|
||||||
|
if err != nil {
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
if l.keepAlive > 0 {
|
||||||
|
conn.SetKeepAlive(true)
|
||||||
|
conn.SetKeepAlivePeriod(l.keepAlive)
|
||||||
|
}
|
||||||
|
tlsconn := &TLSConn{tls.Server(conn, l.config), l}
|
||||||
|
l.Lock()
|
||||||
|
defer l.Unlock()
|
||||||
|
l.conns[tlsconn] = struct{}{}
|
||||||
|
return tlsconn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSConnState implements HTTP connection hijacking
|
||||||
|
func (l *TLSListener) TLSConnState(c net.Conn, cs http.ConnState) {
|
||||||
|
if cs == http.StateClosed || cs == http.StateHijacked {
|
||||||
|
l.RemoveConn(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveConn removes a connection from the map
|
||||||
|
func (l *TLSListener) RemoveConn(c net.Conn) {
|
||||||
|
l.Lock()
|
||||||
|
defer l.Unlock()
|
||||||
|
delete(l.conns, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop stops the listener and closes all open connections
|
||||||
|
func (l *TLSListener) Stop() {
|
||||||
|
l.Close()
|
||||||
|
for c := range l.conns {
|
||||||
|
c.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue