mirror of https://github.com/ginuerzh/gost
233 changed files with 27691 additions and 8423 deletions
@ -0,0 +1,109 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"crypto/tls" |
|||
"flag" |
|||
"log" |
|||
|
|||
"github.com/ginuerzh/gost/gost" |
|||
) |
|||
|
|||
var ( |
|||
laddr, faddr string |
|||
quiet bool |
|||
) |
|||
|
|||
func init() { |
|||
log.SetFlags(log.LstdFlags | log.Lshortfile) |
|||
|
|||
flag.StringVar(&laddr, "L", ":18080", "listen address") |
|||
flag.StringVar(&faddr, "F", ":6121", "forward address") |
|||
flag.BoolVar(&quiet, "q", false, "quiet mode") |
|||
flag.BoolVar(&gost.Debug, "d", false, "debug mode") |
|||
flag.Parse() |
|||
|
|||
if quiet { |
|||
gost.SetLogger(&gost.NopLogger{}) |
|||
} |
|||
} |
|||
|
|||
func main() { |
|||
chain := gost.NewChain( |
|||
gost.Node{ |
|||
Protocol: "socks5", |
|||
Transport: "quic", |
|||
Addr: faddr, |
|||
Client: gost.NewClient( |
|||
gost.SOCKS5Connector(nil), |
|||
gost.QUICTransporter(nil), |
|||
), |
|||
}, |
|||
) |
|||
|
|||
s := &gost.Server{} |
|||
s.Handle(gost.SOCKS5Handler( |
|||
gost.ChainHandlerOption(chain), |
|||
gost.TLSConfigHandlerOption(tlsConfig()), |
|||
)) |
|||
ln, err := gost.TCPListener(laddr) |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
log.Fatal(s.Serve(ln)) |
|||
} |
|||
|
|||
var ( |
|||
rawCert = []byte(`-----BEGIN CERTIFICATE----- |
|||
MIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw |
|||
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5 |
|||
MDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw |
|||
ggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N |
|||
0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw |
|||
hLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4 |
|||
8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv |
|||
482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR |
|||
LIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF |
|||
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC |
|||
CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN |
|||
l/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS |
|||
cBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w |
|||
emcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj |
|||
b3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57 |
|||
lNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg== |
|||
-----END CERTIFICATE-----`) |
|||
rawKey = []byte(`-----BEGIN RSA PRIVATE KEY----- |
|||
MIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N |
|||
ZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo |
|||
N39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv |
|||
GYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh |
|||
Jnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg |
|||
IQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n |
|||
IShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H |
|||
r+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe |
|||
yE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru |
|||
kcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk |
|||
TS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU
|
|||
k8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o |
|||
/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK |
|||
HgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg |
|||
HcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY |
|||
CFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d |
|||
JI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr |
|||
pJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt |
|||
/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD |
|||
xJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL |
|||
vx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX |
|||
1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt |
|||
7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4 |
|||
fqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw |
|||
cfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI= |
|||
-----END RSA PRIVATE KEY-----`) |
|||
) |
|||
|
|||
func tlsConfig() *tls.Config { |
|||
cert, err := tls.X509KeyPair(rawCert, rawKey) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
return &tls.Config{Certificates: []tls.Certificate{cert}} |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"crypto/tls" |
|||
"flag" |
|||
"log" |
|||
|
|||
"github.com/ginuerzh/gost/gost" |
|||
) |
|||
|
|||
var ( |
|||
laddr string |
|||
quiet bool |
|||
) |
|||
|
|||
func init() { |
|||
log.SetFlags(log.LstdFlags | log.Lshortfile) |
|||
|
|||
flag.StringVar(&laddr, "L", ":6121", "listen address") |
|||
flag.BoolVar(&quiet, "q", false, "quiet mode") |
|||
flag.BoolVar(&gost.Debug, "d", false, "debug mode") |
|||
|
|||
flag.Parse() |
|||
|
|||
if quiet { |
|||
gost.SetLogger(&gost.NopLogger{}) |
|||
} |
|||
} |
|||
|
|||
func main() { |
|||
quicServer() |
|||
} |
|||
|
|||
func quicServer() { |
|||
s := &gost.Server{} |
|||
s.Handle( |
|||
gost.SOCKS5Handler(gost.TLSConfigHandlerOption(tlsConfig())), |
|||
) |
|||
|
|||
ln, err := gost.QUICListener(laddr, &gost.QUICConfig{TLSConfig: tlsConfig()}) |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
log.Println("server listen on", laddr) |
|||
log.Fatal(s.Serve(ln)) |
|||
} |
|||
|
|||
var ( |
|||
rawCert = []byte(`-----BEGIN CERTIFICATE----- |
|||
MIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw |
|||
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5 |
|||
MDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw |
|||
ggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N |
|||
0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw |
|||
hLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4 |
|||
8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv |
|||
482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR |
|||
LIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF |
|||
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC |
|||
CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN |
|||
l/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS |
|||
cBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w |
|||
emcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj |
|||
b3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57 |
|||
lNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg== |
|||
-----END CERTIFICATE-----`) |
|||
rawKey = []byte(`-----BEGIN RSA PRIVATE KEY----- |
|||
MIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N |
|||
ZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo |
|||
N39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv |
|||
GYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh |
|||
Jnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg |
|||
IQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n |
|||
IShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H |
|||
r+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe |
|||
yE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru |
|||
kcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk |
|||
TS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU
|
|||
k8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o |
|||
/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK |
|||
HgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg |
|||
HcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY |
|||
CFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d |
|||
JI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr |
|||
pJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt |
|||
/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD |
|||
xJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL |
|||
vx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX |
|||
1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt |
|||
7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4 |
|||
fqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw |
|||
cfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI= |
|||
-----END RSA PRIVATE KEY-----`) |
|||
) |
|||
|
|||
func tlsConfig() *tls.Config { |
|||
cert, err := tls.X509KeyPair(rawCert, rawKey) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
return &tls.Config{Certificates: []tls.Certificate{cert}} |
|||
} |
|||
@ -0,0 +1,234 @@ |
|||
package gost |
|||
|
|||
import ( |
|||
"crypto/tls" |
|||
"errors" |
|||
"net" |
|||
"sync" |
|||
"time" |
|||
|
|||
"github.com/go-log/log" |
|||
quic "github.com/lucas-clemente/quic-go" |
|||
) |
|||
|
|||
type quicSession struct { |
|||
conn net.Conn |
|||
session quic.Session |
|||
} |
|||
|
|||
func (session *quicSession) GetConn() (*quicConn, error) { |
|||
stream, err := session.session.OpenStream() |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return &quicConn{ |
|||
Stream: stream, |
|||
laddr: session.session.LocalAddr(), |
|||
raddr: session.session.RemoteAddr(), |
|||
}, nil |
|||
} |
|||
|
|||
func (session *quicSession) Close() error { |
|||
return session.session.Close(nil) |
|||
} |
|||
|
|||
type quicTransporter struct { |
|||
config *QUICConfig |
|||
sessionMutex sync.Mutex |
|||
sessions map[string]*quicSession |
|||
} |
|||
|
|||
// QUICTransporter creates a Transporter that is used by QUIC proxy client.
|
|||
func QUICTransporter(config *QUICConfig) Transporter { |
|||
if config == nil { |
|||
config = &QUICConfig{} |
|||
} |
|||
return &quicTransporter{ |
|||
config: config, |
|||
sessions: make(map[string]*quicSession), |
|||
} |
|||
} |
|||
|
|||
func (tr *quicTransporter) Dial(addr string, options ...DialOption) (conn net.Conn, err error) { |
|||
tr.sessionMutex.Lock() |
|||
defer tr.sessionMutex.Unlock() |
|||
|
|||
session, ok := tr.sessions[addr] |
|||
if !ok { |
|||
conn, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) |
|||
if err != nil { |
|||
return |
|||
} |
|||
session = &quicSession{conn: conn} |
|||
tr.sessions[addr] = session |
|||
} |
|||
return session.conn, nil |
|||
} |
|||
|
|||
func (tr *quicTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) { |
|||
opts := &HandshakeOptions{} |
|||
for _, option := range options { |
|||
option(opts) |
|||
} |
|||
config := tr.config |
|||
if opts.QUICConfig != nil { |
|||
config = opts.QUICConfig |
|||
} |
|||
if config.TLSConfig == nil { |
|||
config.TLSConfig = &tls.Config{InsecureSkipVerify: true} |
|||
} |
|||
|
|||
tr.sessionMutex.Lock() |
|||
defer tr.sessionMutex.Unlock() |
|||
|
|||
session, ok := tr.sessions[opts.Addr] |
|||
if session != nil && session.conn != conn { |
|||
conn.Close() |
|||
return nil, errors.New("quic: unrecognized connection") |
|||
} |
|||
if !ok || session.session == nil { |
|||
s, err := tr.initSession(opts.Addr, conn, config) |
|||
if err != nil { |
|||
conn.Close() |
|||
delete(tr.sessions, opts.Addr) |
|||
return nil, err |
|||
} |
|||
session = s |
|||
tr.sessions[opts.Addr] = session |
|||
} |
|||
cc, err := session.GetConn() |
|||
if err != nil { |
|||
session.Close() |
|||
delete(tr.sessions, opts.Addr) |
|||
return nil, err |
|||
} |
|||
|
|||
return cc, nil |
|||
} |
|||
|
|||
func (tr *quicTransporter) initSession(addr string, conn net.Conn, config *QUICConfig) (*quicSession, error) { |
|||
udpConn, ok := conn.(*net.UDPConn) |
|||
if !ok { |
|||
return nil, errors.New("quic: wrong connection type") |
|||
} |
|||
udpAddr, err := net.ResolveUDPAddr("udp", addr) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
quicConfig := &quic.Config{ |
|||
HandshakeTimeout: config.Timeout, |
|||
KeepAlive: config.KeepAlive, |
|||
} |
|||
session, err := quic.Dial(udpConn, udpAddr, addr, config.TLSConfig, quicConfig) |
|||
if err != nil { |
|||
log.Log("quic dial", err) |
|||
return nil, err |
|||
} |
|||
return &quicSession{conn: conn, session: session}, nil |
|||
} |
|||
|
|||
func (tr *quicTransporter) Multiplex() bool { |
|||
return true |
|||
} |
|||
|
|||
type QUICConfig struct { |
|||
TLSConfig *tls.Config |
|||
Timeout time.Duration |
|||
KeepAlive bool |
|||
} |
|||
|
|||
type quicListener struct { |
|||
ln quic.Listener |
|||
connChan chan net.Conn |
|||
errChan chan error |
|||
} |
|||
|
|||
// QUICListener creates a Listener for QUIC proxy server.
|
|||
func QUICListener(addr string, config *QUICConfig) (Listener, error) { |
|||
if config == nil { |
|||
config = &QUICConfig{} |
|||
} |
|||
quicConfig := &quic.Config{ |
|||
HandshakeTimeout: config.Timeout, |
|||
KeepAlive: config.KeepAlive, |
|||
} |
|||
|
|||
ln, err := quic.ListenAddr(addr, config.TLSConfig, quicConfig) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
l := &quicListener{ |
|||
ln: ln, |
|||
connChan: make(chan net.Conn, 1024), |
|||
errChan: make(chan error, 1), |
|||
} |
|||
go l.listenLoop() |
|||
|
|||
return l, nil |
|||
} |
|||
|
|||
func (l *quicListener) listenLoop() { |
|||
for { |
|||
session, err := l.ln.Accept() |
|||
if err != nil { |
|||
log.Log("[quic] accept:", err) |
|||
l.errChan <- err |
|||
close(l.errChan) |
|||
return |
|||
} |
|||
go l.sessionLoop(session) |
|||
} |
|||
} |
|||
|
|||
func (l *quicListener) sessionLoop(session quic.Session) { |
|||
log.Logf("[quic] %s <-> %s", session.RemoteAddr(), session.LocalAddr()) |
|||
defer log.Logf("[quic] %s >-< %s", session.RemoteAddr(), session.LocalAddr()) |
|||
|
|||
for { |
|||
stream, err := session.AcceptStream() |
|||
if err != nil { |
|||
log.Log("[quic] accept stream:", err) |
|||
return |
|||
} |
|||
select { |
|||
case l.connChan <- &quicConn{Stream: stream, laddr: session.LocalAddr(), raddr: session.RemoteAddr()}: |
|||
default: |
|||
log.Logf("[quic] %s - %s: connection queue is full", session.RemoteAddr(), session.LocalAddr()) |
|||
} |
|||
} |
|||
} |
|||
|
|||
func (l *quicListener) Accept() (conn net.Conn, err error) { |
|||
var ok bool |
|||
select { |
|||
case conn = <-l.connChan: |
|||
case err, ok = <-l.errChan: |
|||
if !ok { |
|||
err = errors.New("accpet on closed listener") |
|||
} |
|||
} |
|||
return |
|||
} |
|||
|
|||
func (l *quicListener) Addr() net.Addr { |
|||
return l.ln.Addr() |
|||
} |
|||
|
|||
func (l *quicListener) Close() error { |
|||
return l.ln.Close() |
|||
} |
|||
|
|||
type quicConn struct { |
|||
quic.Stream |
|||
laddr net.Addr |
|||
raddr net.Addr |
|||
} |
|||
|
|||
func (c *quicConn) LocalAddr() net.Addr { |
|||
return c.laddr |
|||
} |
|||
|
|||
func (c *quicConn) RemoteAddr() net.Addr { |
|||
return c.raddr |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
ISC License |
|||
|
|||
Copyright (c) 2012-2013 Dave Collins <[email protected]> |
|||
|
|||
Permission to use, copy, modify, and distribute this software for any |
|||
purpose with or without fee is hereby granted, provided that the above |
|||
copyright notice and this permission notice appear in all copies. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
@ -1,152 +0,0 @@ |
|||
// Copyright (c) 2015 Dave Collins <[email protected]>
|
|||
//
|
|||
// Permission to use, copy, modify, and distribute this software for any
|
|||
// purpose with or without fee is hereby granted, provided that the above
|
|||
// copyright notice and this permission notice appear in all copies.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
|
|||
// NOTE: Due to the following build constraints, this file will only be compiled
|
|||
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
|||
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
|||
// tag is deprecated and thus should not be used.
|
|||
// +build !js,!appengine,!safe,!disableunsafe
|
|||
|
|||
package spew |
|||
|
|||
import ( |
|||
"reflect" |
|||
"unsafe" |
|||
) |
|||
|
|||
const ( |
|||
// UnsafeDisabled is a build-time constant which specifies whether or
|
|||
// not access to the unsafe package is available.
|
|||
UnsafeDisabled = false |
|||
|
|||
// ptrSize is the size of a pointer on the current arch.
|
|||
ptrSize = unsafe.Sizeof((*byte)(nil)) |
|||
) |
|||
|
|||
var ( |
|||
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
|
|||
// internal reflect.Value fields. These values are valid before golang
|
|||
// commit ecccf07e7f9d which changed the format. The are also valid
|
|||
// after commit 82f48826c6c7 which changed the format again to mirror
|
|||
// the original format. Code in the init function updates these offsets
|
|||
// as necessary.
|
|||
offsetPtr = uintptr(ptrSize) |
|||
offsetScalar = uintptr(0) |
|||
offsetFlag = uintptr(ptrSize * 2) |
|||
|
|||
// flagKindWidth and flagKindShift indicate various bits that the
|
|||
// reflect package uses internally to track kind information.
|
|||
//
|
|||
// flagRO indicates whether or not the value field of a reflect.Value is
|
|||
// read-only.
|
|||
//
|
|||
// flagIndir indicates whether the value field of a reflect.Value is
|
|||
// the actual data or a pointer to the data.
|
|||
//
|
|||
// These values are valid before golang commit 90a7c3c86944 which
|
|||
// changed their positions. Code in the init function updates these
|
|||
// flags as necessary.
|
|||
flagKindWidth = uintptr(5) |
|||
flagKindShift = uintptr(flagKindWidth - 1) |
|||
flagRO = uintptr(1 << 0) |
|||
flagIndir = uintptr(1 << 1) |
|||
) |
|||
|
|||
func init() { |
|||
// Older versions of reflect.Value stored small integers directly in the
|
|||
// ptr field (which is named val in the older versions). Versions
|
|||
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
|
|||
// scalar for this purpose which unfortunately came before the flag
|
|||
// field, so the offset of the flag field is different for those
|
|||
// versions.
|
|||
//
|
|||
// This code constructs a new reflect.Value from a known small integer
|
|||
// and checks if the size of the reflect.Value struct indicates it has
|
|||
// the scalar field. When it does, the offsets are updated accordingly.
|
|||
vv := reflect.ValueOf(0xf00) |
|||
if unsafe.Sizeof(vv) == (ptrSize * 4) { |
|||
offsetScalar = ptrSize * 2 |
|||
offsetFlag = ptrSize * 3 |
|||
} |
|||
|
|||
// Commit 90a7c3c86944 changed the flag positions such that the low
|
|||
// order bits are the kind. This code extracts the kind from the flags
|
|||
// field and ensures it's the correct type. When it's not, the flag
|
|||
// order has been changed to the newer format, so the flags are updated
|
|||
// accordingly.
|
|||
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag) |
|||
upfv := *(*uintptr)(upf) |
|||
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift) |
|||
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) { |
|||
flagKindShift = 0 |
|||
flagRO = 1 << 5 |
|||
flagIndir = 1 << 6 |
|||
|
|||
// Commit adf9b30e5594 modified the flags to separate the
|
|||
// flagRO flag into two bits which specifies whether or not the
|
|||
// field is embedded. This causes flagIndir to move over a bit
|
|||
// and means that flagRO is the combination of either of the
|
|||
// original flagRO bit and the new bit.
|
|||
//
|
|||
// This code detects the change by extracting what used to be
|
|||
// the indirect bit to ensure it's set. When it's not, the flag
|
|||
// order has been changed to the newer format, so the flags are
|
|||
// updated accordingly.
|
|||
if upfv&flagIndir == 0 { |
|||
flagRO = 3 << 5 |
|||
flagIndir = 1 << 7 |
|||
} |
|||
} |
|||
} |
|||
|
|||
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
|
|||
// the typical safety restrictions preventing access to unaddressable and
|
|||
// unexported data. It works by digging the raw pointer to the underlying
|
|||
// value out of the protected value and generating a new unprotected (unsafe)
|
|||
// reflect.Value to it.
|
|||
//
|
|||
// This allows us to check for implementations of the Stringer and error
|
|||
// interfaces to be used for pretty printing ordinarily unaddressable and
|
|||
// inaccessible values such as unexported struct fields.
|
|||
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) { |
|||
indirects := 1 |
|||
vt := v.Type() |
|||
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr) |
|||
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag)) |
|||
if rvf&flagIndir != 0 { |
|||
vt = reflect.PtrTo(v.Type()) |
|||
indirects++ |
|||
} else if offsetScalar != 0 { |
|||
// The value is in the scalar field when it's not one of the
|
|||
// reference types.
|
|||
switch vt.Kind() { |
|||
case reflect.Uintptr: |
|||
case reflect.Chan: |
|||
case reflect.Func: |
|||
case reflect.Map: |
|||
case reflect.Ptr: |
|||
case reflect.UnsafePointer: |
|||
default: |
|||
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + |
|||
offsetScalar) |
|||
} |
|||
} |
|||
|
|||
pv := reflect.NewAt(vt, upv) |
|||
rv = pv |
|||
for i := 0; i < indirects; i++ { |
|||
rv = rv.Elem() |
|||
} |
|||
return rv |
|||
} |
|||
@ -1,38 +0,0 @@ |
|||
// Copyright (c) 2015 Dave Collins <[email protected]>
|
|||
//
|
|||
// Permission to use, copy, modify, and distribute this software for any
|
|||
// purpose with or without fee is hereby granted, provided that the above
|
|||
// copyright notice and this permission notice appear in all copies.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
|
|||
// NOTE: Due to the following build constraints, this file will only be compiled
|
|||
// when the code is running on Google App Engine, compiled by GopherJS, or
|
|||
// "-tags safe" is added to the go build command line. The "disableunsafe"
|
|||
// tag is deprecated and thus should not be used.
|
|||
// +build js appengine safe disableunsafe
|
|||
|
|||
package spew |
|||
|
|||
import "reflect" |
|||
|
|||
const ( |
|||
// UnsafeDisabled is a build-time constant which specifies whether or
|
|||
// not access to the unsafe package is available.
|
|||
UnsafeDisabled = true |
|||
) |
|||
|
|||
// unsafeReflectValue typically converts the passed reflect.Value into a one
|
|||
// that bypasses the typical safety restrictions preventing access to
|
|||
// unaddressable and unexported data. However, doing this relies on access to
|
|||
// the unsafe package. This is a stub version which simply returns the passed
|
|||
// reflect.Value when the unsafe package is not available.
|
|||
func unsafeReflectValue(v reflect.Value) reflect.Value { |
|||
return v |
|||
} |
|||
@ -1,341 +0,0 @@ |
|||
/* |
|||
* Copyright (c) 2013 Dave Collins <dave@davec.name> |
|||
* |
|||
* Permission to use, copy, modify, and distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
package spew |
|||
|
|||
import ( |
|||
"bytes" |
|||
"fmt" |
|||
"io" |
|||
"reflect" |
|||
"sort" |
|||
"strconv" |
|||
) |
|||
|
|||
// Some constants in the form of bytes to avoid string overhead. This mirrors
|
|||
// the technique used in the fmt package.
|
|||
var ( |
|||
panicBytes = []byte("(PANIC=") |
|||
plusBytes = []byte("+") |
|||
iBytes = []byte("i") |
|||
trueBytes = []byte("true") |
|||
falseBytes = []byte("false") |
|||
interfaceBytes = []byte("(interface {})") |
|||
commaNewlineBytes = []byte(",\n") |
|||
newlineBytes = []byte("\n") |
|||
openBraceBytes = []byte("{") |
|||
openBraceNewlineBytes = []byte("{\n") |
|||
closeBraceBytes = []byte("}") |
|||
asteriskBytes = []byte("*") |
|||
colonBytes = []byte(":") |
|||
colonSpaceBytes = []byte(": ") |
|||
openParenBytes = []byte("(") |
|||
closeParenBytes = []byte(")") |
|||
spaceBytes = []byte(" ") |
|||
pointerChainBytes = []byte("->") |
|||
nilAngleBytes = []byte("<nil>") |
|||
maxNewlineBytes = []byte("<max depth reached>\n") |
|||
maxShortBytes = []byte("<max>") |
|||
circularBytes = []byte("<already shown>") |
|||
circularShortBytes = []byte("<shown>") |
|||
invalidAngleBytes = []byte("<invalid>") |
|||
openBracketBytes = []byte("[") |
|||
closeBracketBytes = []byte("]") |
|||
percentBytes = []byte("%") |
|||
precisionBytes = []byte(".") |
|||
openAngleBytes = []byte("<") |
|||
closeAngleBytes = []byte(">") |
|||
openMapBytes = []byte("map[") |
|||
closeMapBytes = []byte("]") |
|||
lenEqualsBytes = []byte("len=") |
|||
capEqualsBytes = []byte("cap=") |
|||
) |
|||
|
|||
// hexDigits is used to map a decimal value to a hex digit.
|
|||
var hexDigits = "0123456789abcdef" |
|||
|
|||
// catchPanic handles any panics that might occur during the handleMethods
|
|||
// calls.
|
|||
func catchPanic(w io.Writer, v reflect.Value) { |
|||
if err := recover(); err != nil { |
|||
w.Write(panicBytes) |
|||
fmt.Fprintf(w, "%v", err) |
|||
w.Write(closeParenBytes) |
|||
} |
|||
} |
|||
|
|||
// handleMethods attempts to call the Error and String methods on the underlying
|
|||
// type the passed reflect.Value represents and outputes the result to Writer w.
|
|||
//
|
|||
// It handles panics in any called methods by catching and displaying the error
|
|||
// as the formatted value.
|
|||
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) { |
|||
// We need an interface to check if the type implements the error or
|
|||
// Stringer interface. However, the reflect package won't give us an
|
|||
// interface on certain things like unexported struct fields in order
|
|||
// to enforce visibility rules. We use unsafe, when it's available,
|
|||
// to bypass these restrictions since this package does not mutate the
|
|||
// values.
|
|||
if !v.CanInterface() { |
|||
if UnsafeDisabled { |
|||
return false |
|||
} |
|||
|
|||
v = unsafeReflectValue(v) |
|||
} |
|||
|
|||
// Choose whether or not to do error and Stringer interface lookups against
|
|||
// the base type or a pointer to the base type depending on settings.
|
|||
// Technically calling one of these methods with a pointer receiver can
|
|||
// mutate the value, however, types which choose to satisify an error or
|
|||
// Stringer interface with a pointer receiver should not be mutating their
|
|||
// state inside these interface methods.
|
|||
if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() { |
|||
v = unsafeReflectValue(v) |
|||
} |
|||
if v.CanAddr() { |
|||
v = v.Addr() |
|||
} |
|||
|
|||
// Is it an error or Stringer?
|
|||
switch iface := v.Interface().(type) { |
|||
case error: |
|||
defer catchPanic(w, v) |
|||
if cs.ContinueOnMethod { |
|||
w.Write(openParenBytes) |
|||
w.Write([]byte(iface.Error())) |
|||
w.Write(closeParenBytes) |
|||
w.Write(spaceBytes) |
|||
return false |
|||
} |
|||
|
|||
w.Write([]byte(iface.Error())) |
|||
return true |
|||
|
|||
case fmt.Stringer: |
|||
defer catchPanic(w, v) |
|||
if cs.ContinueOnMethod { |
|||
w.Write(openParenBytes) |
|||
w.Write([]byte(iface.String())) |
|||
w.Write(closeParenBytes) |
|||
w.Write(spaceBytes) |
|||
return false |
|||
} |
|||
w.Write([]byte(iface.String())) |
|||
return true |
|||
} |
|||
return false |
|||
} |
|||
|
|||
// printBool outputs a boolean value as true or false to Writer w.
|
|||
func printBool(w io.Writer, val bool) { |
|||
if val { |
|||
w.Write(trueBytes) |
|||
} else { |
|||
w.Write(falseBytes) |
|||
} |
|||
} |
|||
|
|||
// printInt outputs a signed integer value to Writer w.
|
|||
func printInt(w io.Writer, val int64, base int) { |
|||
w.Write([]byte(strconv.FormatInt(val, base))) |
|||
} |
|||
|
|||
// printUint outputs an unsigned integer value to Writer w.
|
|||
func printUint(w io.Writer, val uint64, base int) { |
|||
w.Write([]byte(strconv.FormatUint(val, base))) |
|||
} |
|||
|
|||
// printFloat outputs a floating point value using the specified precision,
|
|||
// which is expected to be 32 or 64bit, to Writer w.
|
|||
func printFloat(w io.Writer, val float64, precision int) { |
|||
w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision))) |
|||
} |
|||
|
|||
// printComplex outputs a complex value using the specified float precision
|
|||
// for the real and imaginary parts to Writer w.
|
|||
func printComplex(w io.Writer, c complex128, floatPrecision int) { |
|||
r := real(c) |
|||
w.Write(openParenBytes) |
|||
w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision))) |
|||
i := imag(c) |
|||
if i >= 0 { |
|||
w.Write(plusBytes) |
|||
} |
|||
w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision))) |
|||
w.Write(iBytes) |
|||
w.Write(closeParenBytes) |
|||
} |
|||
|
|||
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
|
|||
// prefix to Writer w.
|
|||
func printHexPtr(w io.Writer, p uintptr) { |
|||
// Null pointer.
|
|||
num := uint64(p) |
|||
if num == 0 { |
|||
w.Write(nilAngleBytes) |
|||
return |
|||
} |
|||
|
|||
// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
|
|||
buf := make([]byte, 18) |
|||
|
|||
// It's simpler to construct the hex string right to left.
|
|||
base := uint64(16) |
|||
i := len(buf) - 1 |
|||
for num >= base { |
|||
buf[i] = hexDigits[num%base] |
|||
num /= base |
|||
i-- |
|||
} |
|||
buf[i] = hexDigits[num] |
|||
|
|||
// Add '0x' prefix.
|
|||
i-- |
|||
buf[i] = 'x' |
|||
i-- |
|||
buf[i] = '0' |
|||
|
|||
// Strip unused leading bytes.
|
|||
buf = buf[i:] |
|||
w.Write(buf) |
|||
} |
|||
|
|||
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
|
|||
// elements to be sorted.
|
|||
type valuesSorter struct { |
|||
values []reflect.Value |
|||
strings []string // either nil or same len and values
|
|||
cs *ConfigState |
|||
} |
|||
|
|||
// newValuesSorter initializes a valuesSorter instance, which holds a set of
|
|||
// surrogate keys on which the data should be sorted. It uses flags in
|
|||
// ConfigState to decide if and how to populate those surrogate keys.
|
|||
func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface { |
|||
vs := &valuesSorter{values: values, cs: cs} |
|||
if canSortSimply(vs.values[0].Kind()) { |
|||
return vs |
|||
} |
|||
if !cs.DisableMethods { |
|||
vs.strings = make([]string, len(values)) |
|||
for i := range vs.values { |
|||
b := bytes.Buffer{} |
|||
if !handleMethods(cs, &b, vs.values[i]) { |
|||
vs.strings = nil |
|||
break |
|||
} |
|||
vs.strings[i] = b.String() |
|||
} |
|||
} |
|||
if vs.strings == nil && cs.SpewKeys { |
|||
vs.strings = make([]string, len(values)) |
|||
for i := range vs.values { |
|||
vs.strings[i] = Sprintf("%#v", vs.values[i].Interface()) |
|||
} |
|||
} |
|||
return vs |
|||
} |
|||
|
|||
// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
|
|||
// directly, or whether it should be considered for sorting by surrogate keys
|
|||
// (if the ConfigState allows it).
|
|||
func canSortSimply(kind reflect.Kind) bool { |
|||
// This switch parallels valueSortLess, except for the default case.
|
|||
switch kind { |
|||
case reflect.Bool: |
|||
return true |
|||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: |
|||
return true |
|||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: |
|||
return true |
|||
case reflect.Float32, reflect.Float64: |
|||
return true |
|||
case reflect.String: |
|||
return true |
|||
case reflect.Uintptr: |
|||
return true |
|||
case reflect.Array: |
|||
return true |
|||
} |
|||
return false |
|||
} |
|||
|
|||
// Len returns the number of values in the slice. It is part of the
|
|||
// sort.Interface implementation.
|
|||
func (s *valuesSorter) Len() int { |
|||
return len(s.values) |
|||
} |
|||
|
|||
// Swap swaps the values at the passed indices. It is part of the
|
|||
// sort.Interface implementation.
|
|||
func (s *valuesSorter) Swap(i, j int) { |
|||
s.values[i], s.values[j] = s.values[j], s.values[i] |
|||
if s.strings != nil { |
|||
s.strings[i], s.strings[j] = s.strings[j], s.strings[i] |
|||
} |
|||
} |
|||
|
|||
// valueSortLess returns whether the first value should sort before the second
|
|||
// value. It is used by valueSorter.Less as part of the sort.Interface
|
|||
// implementation.
|
|||
func valueSortLess(a, b reflect.Value) bool { |
|||
switch a.Kind() { |
|||
case reflect.Bool: |
|||
return !a.Bool() && b.Bool() |
|||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: |
|||
return a.Int() < b.Int() |
|||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: |
|||
return a.Uint() < b.Uint() |
|||
case reflect.Float32, reflect.Float64: |
|||
return a.Float() < b.Float() |
|||
case reflect.String: |
|||
return a.String() < b.String() |
|||
case reflect.Uintptr: |
|||
return a.Uint() < b.Uint() |
|||
case reflect.Array: |
|||
// Compare the contents of both arrays.
|
|||
l := a.Len() |
|||
for i := 0; i < l; i++ { |
|||
av := a.Index(i) |
|||
bv := b.Index(i) |
|||
if av.Interface() == bv.Interface() { |
|||
continue |
|||
} |
|||
return valueSortLess(av, bv) |
|||
} |
|||
} |
|||
return a.String() < b.String() |
|||
} |
|||
|
|||
// Less returns whether the value at index i should sort before the
|
|||
// value at index j. It is part of the sort.Interface implementation.
|
|||
func (s *valuesSorter) Less(i, j int) bool { |
|||
if s.strings == nil { |
|||
return valueSortLess(s.values[i], s.values[j]) |
|||
} |
|||
return s.strings[i] < s.strings[j] |
|||
} |
|||
|
|||
// sortValues is a sort function that handles both native types and any type that
|
|||
// can be converted to error or Stringer. Other inputs are sorted according to
|
|||
// their Value.String() value to ensure display stability.
|
|||
func sortValues(values []reflect.Value, cs *ConfigState) { |
|||
if len(values) == 0 { |
|||
return |
|||
} |
|||
sort.Sort(newValuesSorter(values, cs)) |
|||
} |
|||
@ -1,306 +0,0 @@ |
|||
/* |
|||
* Copyright (c) 2013 Dave Collins <dave@davec.name> |
|||
* |
|||
* Permission to use, copy, modify, and distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
package spew |
|||
|
|||
import ( |
|||
"bytes" |
|||
"fmt" |
|||
"io" |
|||
"os" |
|||
) |
|||
|
|||
// ConfigState houses the configuration options used by spew to format and
|
|||
// display values. There is a global instance, Config, that is used to control
|
|||
// all top-level Formatter and Dump functionality. Each ConfigState instance
|
|||
// provides methods equivalent to the top-level functions.
|
|||
//
|
|||
// The zero value for ConfigState provides no indentation. You would typically
|
|||
// want to set it to a space or a tab.
|
|||
//
|
|||
// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
|
|||
// with default settings. See the documentation of NewDefaultConfig for default
|
|||
// values.
|
|||
type ConfigState struct { |
|||
// Indent specifies the string to use for each indentation level. The
|
|||
// global config instance that all top-level functions use set this to a
|
|||
// single space by default. If you would like more indentation, you might
|
|||
// set this to a tab with "\t" or perhaps two spaces with " ".
|
|||
Indent string |
|||
|
|||
// MaxDepth controls the maximum number of levels to descend into nested
|
|||
// data structures. The default, 0, means there is no limit.
|
|||
//
|
|||
// NOTE: Circular data structures are properly detected, so it is not
|
|||
// necessary to set this value unless you specifically want to limit deeply
|
|||
// nested data structures.
|
|||
MaxDepth int |
|||
|
|||
// DisableMethods specifies whether or not error and Stringer interfaces are
|
|||
// invoked for types that implement them.
|
|||
DisableMethods bool |
|||
|
|||
// DisablePointerMethods specifies whether or not to check for and invoke
|
|||
// error and Stringer interfaces on types which only accept a pointer
|
|||
// receiver when the current type is not a pointer.
|
|||
//
|
|||
// NOTE: This might be an unsafe action since calling one of these methods
|
|||
// with a pointer receiver could technically mutate the value, however,
|
|||
// in practice, types which choose to satisify an error or Stringer
|
|||
// interface with a pointer receiver should not be mutating their state
|
|||
// inside these interface methods. As a result, this option relies on
|
|||
// access to the unsafe package, so it will not have any effect when
|
|||
// running in environments without access to the unsafe package such as
|
|||
// Google App Engine or with the "safe" build tag specified.
|
|||
DisablePointerMethods bool |
|||
|
|||
// DisablePointerAddresses specifies whether to disable the printing of
|
|||
// pointer addresses. This is useful when diffing data structures in tests.
|
|||
DisablePointerAddresses bool |
|||
|
|||
// DisableCapacities specifies whether to disable the printing of capacities
|
|||
// for arrays, slices, maps and channels. This is useful when diffing
|
|||
// data structures in tests.
|
|||
DisableCapacities bool |
|||
|
|||
// ContinueOnMethod specifies whether or not recursion should continue once
|
|||
// a custom error or Stringer interface is invoked. The default, false,
|
|||
// means it will print the results of invoking the custom error or Stringer
|
|||
// interface and return immediately instead of continuing to recurse into
|
|||
// the internals of the data type.
|
|||
//
|
|||
// NOTE: This flag does not have any effect if method invocation is disabled
|
|||
// via the DisableMethods or DisablePointerMethods options.
|
|||
ContinueOnMethod bool |
|||
|
|||
// SortKeys specifies map keys should be sorted before being printed. Use
|
|||
// this to have a more deterministic, diffable output. Note that only
|
|||
// native types (bool, int, uint, floats, uintptr and string) and types
|
|||
// that support the error or Stringer interfaces (if methods are
|
|||
// enabled) are supported, with other types sorted according to the
|
|||
// reflect.Value.String() output which guarantees display stability.
|
|||
SortKeys bool |
|||
|
|||
// SpewKeys specifies that, as a last resort attempt, map keys should
|
|||
// be spewed to strings and sorted by those strings. This is only
|
|||
// considered if SortKeys is true.
|
|||
SpewKeys bool |
|||
} |
|||
|
|||
// Config is the active configuration of the top-level functions.
|
|||
// The configuration can be changed by modifying the contents of spew.Config.
|
|||
var Config = ConfigState{Indent: " "} |
|||
|
|||
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
|||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
|||
// the formatted string as a value that satisfies error. See NewFormatter
|
|||
// for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b))
|
|||
func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) { |
|||
return fmt.Errorf(format, c.convertArgs(a)...) |
|||
} |
|||
|
|||
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
|||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
|||
// the number of bytes written and any write error encountered. See
|
|||
// NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b))
|
|||
func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) { |
|||
return fmt.Fprint(w, c.convertArgs(a)...) |
|||
} |
|||
|
|||
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
|||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
|||
// the number of bytes written and any write error encountered. See
|
|||
// NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b))
|
|||
func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { |
|||
return fmt.Fprintf(w, format, c.convertArgs(a)...) |
|||
} |
|||
|
|||
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
|||
// passed with a Formatter interface returned by c.NewFormatter. See
|
|||
// NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
|
|||
func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { |
|||
return fmt.Fprintln(w, c.convertArgs(a)...) |
|||
} |
|||
|
|||
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
|||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
|||
// the number of bytes written and any write error encountered. See
|
|||
// NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Print(c.NewFormatter(a), c.NewFormatter(b))
|
|||
func (c *ConfigState) Print(a ...interface{}) (n int, err error) { |
|||
return fmt.Print(c.convertArgs(a)...) |
|||
} |
|||
|
|||
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
|||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
|||
// the number of bytes written and any write error encountered. See
|
|||
// NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b))
|
|||
func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) { |
|||
return fmt.Printf(format, c.convertArgs(a)...) |
|||
} |
|||
|
|||
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
|||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
|||
// the number of bytes written and any write error encountered. See
|
|||
// NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Println(c.NewFormatter(a), c.NewFormatter(b))
|
|||
func (c *ConfigState) Println(a ...interface{}) (n int, err error) { |
|||
return fmt.Println(c.convertArgs(a)...) |
|||
} |
|||
|
|||
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
|
|||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
|||
// the resulting string. See NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
|
|||
func (c *ConfigState) Sprint(a ...interface{}) string { |
|||
return fmt.Sprint(c.convertArgs(a)...) |
|||
} |
|||
|
|||
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
|
|||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
|||
// the resulting string. See NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
|
|||
func (c *ConfigState) Sprintf(format string, a ...interface{}) string { |
|||
return fmt.Sprintf(format, c.convertArgs(a)...) |
|||
} |
|||
|
|||
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
|
|||
// were passed with a Formatter interface returned by c.NewFormatter. It
|
|||
// returns the resulting string. See NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
|
|||
func (c *ConfigState) Sprintln(a ...interface{}) string { |
|||
return fmt.Sprintln(c.convertArgs(a)...) |
|||
} |
|||
|
|||
/* |
|||
NewFormatter returns a custom formatter that satisfies the fmt.Formatter |
|||
interface. As a result, it integrates cleanly with standard fmt package |
|||
printing functions. The formatter is useful for inline printing of smaller data |
|||
types similar to the standard %v format specifier. |
|||
|
|||
The custom formatter only responds to the %v (most compact), %+v (adds pointer |
|||
addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb |
|||
combinations. Any other verbs such as %x and %q will be sent to the the |
|||
standard fmt package for formatting. In addition, the custom formatter ignores |
|||
the width and precision arguments (however they will still work on the format |
|||
specifiers not handled by the custom formatter). |
|||
|
|||
Typically this function shouldn't be called directly. It is much easier to make |
|||
use of the custom formatter by calling one of the convenience functions such as |
|||
c.Printf, c.Println, or c.Printf. |
|||
*/ |
|||
func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter { |
|||
return newFormatter(c, v) |
|||
} |
|||
|
|||
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
|||
// exactly the same as Dump.
|
|||
func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) { |
|||
fdump(c, w, a...) |
|||
} |
|||
|
|||
/* |
|||
Dump displays the passed parameters to standard out with newlines, customizable |
|||
indentation, and additional debug information such as complete types and all |
|||
pointer addresses used to indirect to the final value. It provides the |
|||
following features over the built-in printing facilities provided by the fmt |
|||
package: |
|||
|
|||
* Pointers are dereferenced and followed |
|||
* Circular data structures are detected and handled properly |
|||
* Custom Stringer/error interfaces are optionally invoked, including |
|||
on unexported types |
|||
* Custom types which only implement the Stringer/error interfaces via |
|||
a pointer receiver are optionally invoked when passing non-pointer |
|||
variables |
|||
* Byte arrays and slices are dumped like the hexdump -C command which |
|||
includes offsets, byte values in hex, and ASCII output |
|||
|
|||
The configuration options are controlled by modifying the public members |
|||
of c. See ConfigState for options documentation. |
|||
|
|||
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to |
|||
get the formatted result as a string. |
|||
*/ |
|||
func (c *ConfigState) Dump(a ...interface{}) { |
|||
fdump(c, os.Stdout, a...) |
|||
} |
|||
|
|||
// Sdump returns a string with the passed arguments formatted exactly the same
|
|||
// as Dump.
|
|||
func (c *ConfigState) Sdump(a ...interface{}) string { |
|||
var buf bytes.Buffer |
|||
fdump(c, &buf, a...) |
|||
return buf.String() |
|||
} |
|||
|
|||
// convertArgs accepts a slice of arguments and returns a slice of the same
|
|||
// length with each argument converted to a spew Formatter interface using
|
|||
// the ConfigState associated with s.
|
|||
func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) { |
|||
formatters = make([]interface{}, len(args)) |
|||
for index, arg := range args { |
|||
formatters[index] = newFormatter(c, arg) |
|||
} |
|||
return formatters |
|||
} |
|||
|
|||
// NewDefaultConfig returns a ConfigState with the following default settings.
|
|||
//
|
|||
// Indent: " "
|
|||
// MaxDepth: 0
|
|||
// DisableMethods: false
|
|||
// DisablePointerMethods: false
|
|||
// ContinueOnMethod: false
|
|||
// SortKeys: false
|
|||
func NewDefaultConfig() *ConfigState { |
|||
return &ConfigState{Indent: " "} |
|||
} |
|||
@ -1,202 +0,0 @@ |
|||
/* |
|||
* Copyright (c) 2013 Dave Collins <dave@davec.name> |
|||
* |
|||
* Permission to use, copy, modify, and distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
/* |
|||
Package spew implements a deep pretty printer for Go data structures to aid in |
|||
debugging. |
|||
|
|||
A quick overview of the additional features spew provides over the built-in |
|||
printing facilities for Go data types are as follows: |
|||
|
|||
* Pointers are dereferenced and followed |
|||
* Circular data structures are detected and handled properly |
|||
* Custom Stringer/error interfaces are optionally invoked, including |
|||
on unexported types |
|||
* Custom types which only implement the Stringer/error interfaces via |
|||
a pointer receiver are optionally invoked when passing non-pointer |
|||
variables |
|||
* Byte arrays and slices are dumped like the hexdump -C command which |
|||
includes offsets, byte values in hex, and ASCII output (only when using |
|||
Dump style) |
|||
|
|||
There are two different approaches spew allows for dumping Go data structures: |
|||
|
|||
* Dump style which prints with newlines, customizable indentation, |
|||
and additional debug information such as types and all pointer addresses |
|||
used to indirect to the final value |
|||
* A custom Formatter interface that integrates cleanly with the standard fmt |
|||
package and replaces %v, %+v, %#v, and %#+v to provide inline printing |
|||
similar to the default %v while providing the additional functionality |
|||
outlined above and passing unsupported format verbs such as %x and %q |
|||
along to fmt |
|||
|
|||
Quick Start |
|||
|
|||
This section demonstrates how to quickly get started with spew. See the |
|||
sections below for further details on formatting and configuration options. |
|||
|
|||
To dump a variable with full newlines, indentation, type, and pointer |
|||
information use Dump, Fdump, or Sdump: |
|||
spew.Dump(myVar1, myVar2, ...) |
|||
spew.Fdump(someWriter, myVar1, myVar2, ...) |
|||
str := spew.Sdump(myVar1, myVar2, ...) |
|||
|
|||
Alternatively, if you would prefer to use format strings with a compacted inline |
|||
printing style, use the convenience wrappers Printf, Fprintf, etc with |
|||
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or |
|||
%#+v (adds types and pointer addresses): |
|||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) |
|||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) |
|||
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) |
|||
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) |
|||
|
|||
Configuration Options |
|||
|
|||
Configuration of spew is handled by fields in the ConfigState type. For |
|||
convenience, all of the top-level functions use a global state available |
|||
via the spew.Config global. |
|||
|
|||
It is also possible to create a ConfigState instance that provides methods |
|||
equivalent to the top-level functions. This allows concurrent configuration |
|||
options. See the ConfigState documentation for more details. |
|||
|
|||
The following configuration options are available: |
|||
* Indent |
|||
String to use for each indentation level for Dump functions. |
|||
It is a single space by default. A popular alternative is "\t". |
|||
|
|||
* MaxDepth |
|||
Maximum number of levels to descend into nested data structures. |
|||
There is no limit by default. |
|||
|
|||
* DisableMethods |
|||
Disables invocation of error and Stringer interface methods. |
|||
Method invocation is enabled by default. |
|||
|
|||
* DisablePointerMethods |
|||
Disables invocation of error and Stringer interface methods on types |
|||
which only accept pointer receivers from non-pointer variables. |
|||
Pointer method invocation is enabled by default. |
|||
|
|||
* ContinueOnMethod |
|||
Enables recursion into types after invoking error and Stringer interface |
|||
methods. Recursion after method invocation is disabled by default. |
|||
|
|||
* SortKeys |
|||
Specifies map keys should be sorted before being printed. Use |
|||
this to have a more deterministic, diffable output. Note that |
|||
only native types (bool, int, uint, floats, uintptr and string) |
|||
and types which implement error or Stringer interfaces are |
|||
supported with other types sorted according to the |
|||
reflect.Value.String() output which guarantees display |
|||
stability. Natural map order is used by default. |
|||
|
|||
* SpewKeys |
|||
Specifies that, as a last resort attempt, map keys should be |
|||
spewed to strings and sorted by those strings. This is only |
|||
considered if SortKeys is true. |
|||
|
|||
Dump Usage |
|||
|
|||
Simply call spew.Dump with a list of variables you want to dump: |
|||
|
|||
spew.Dump(myVar1, myVar2, ...) |
|||
|
|||
You may also call spew.Fdump if you would prefer to output to an arbitrary |
|||
io.Writer. For example, to dump to standard error: |
|||
|
|||
spew.Fdump(os.Stderr, myVar1, myVar2, ...) |
|||
|
|||
A third option is to call spew.Sdump to get the formatted output as a string: |
|||
|
|||
str := spew.Sdump(myVar1, myVar2, ...) |
|||
|
|||
Sample Dump Output |
|||
|
|||
See the Dump example for details on the setup of the types and variables being |
|||
shown here. |
|||
|
|||
(main.Foo) { |
|||
unexportedField: (*main.Bar)(0xf84002e210)({ |
|||
flag: (main.Flag) flagTwo, |
|||
data: (uintptr) <nil> |
|||
}), |
|||
ExportedField: (map[interface {}]interface {}) (len=1) { |
|||
(string) (len=3) "one": (bool) true |
|||
} |
|||
} |
|||
|
|||
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C |
|||
command as shown. |
|||
([]uint8) (len=32 cap=32) { |
|||
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | |
|||
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| |
|||
00000020 31 32 |12| |
|||
} |
|||
|
|||
Custom Formatter |
|||
|
|||
Spew provides a custom formatter that implements the fmt.Formatter interface |
|||
so that it integrates cleanly with standard fmt package printing functions. The |
|||
formatter is useful for inline printing of smaller data types similar to the |
|||
standard %v format specifier. |
|||
|
|||
The custom formatter only responds to the %v (most compact), %+v (adds pointer |
|||
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb |
|||
combinations. Any other verbs such as %x and %q will be sent to the the |
|||
standard fmt package for formatting. In addition, the custom formatter ignores |
|||
the width and precision arguments (however they will still work on the format |
|||
specifiers not handled by the custom formatter). |
|||
|
|||
Custom Formatter Usage |
|||
|
|||
The simplest way to make use of the spew custom formatter is to call one of the |
|||
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The |
|||
functions have syntax you are most likely already familiar with: |
|||
|
|||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) |
|||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) |
|||
spew.Println(myVar, myVar2) |
|||
spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) |
|||
spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) |
|||
|
|||
See the Index for the full list convenience functions. |
|||
|
|||
Sample Formatter Output |
|||
|
|||
Double pointer to a uint8: |
|||
%v: <**>5 |
|||
%+v: <**>(0xf8400420d0->0xf8400420c8)5 |
|||
%#v: (**uint8)5 |
|||
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 |
|||
|
|||
Pointer to circular struct with a uint8 field and a pointer to itself: |
|||
%v: <*>{1 <*><shown>} |
|||
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>} |
|||
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>} |
|||
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>} |
|||
|
|||
See the Printf example for details on the setup of variables being shown |
|||
here. |
|||
|
|||
Errors |
|||
|
|||
Since it is possible for custom Stringer/error interfaces to panic, spew |
|||
detects them and handles them internally by printing the panic information |
|||
inline with the output. Since spew is intended to provide deep pretty printing |
|||
capabilities on structures, it intentionally does not return any errors. |
|||
*/ |
|||
package spew |
|||
@ -1,509 +0,0 @@ |
|||
/* |
|||
* Copyright (c) 2013 Dave Collins <dave@davec.name> |
|||
* |
|||
* Permission to use, copy, modify, and distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
package spew |
|||
|
|||
import ( |
|||
"bytes" |
|||
"encoding/hex" |
|||
"fmt" |
|||
"io" |
|||
"os" |
|||
"reflect" |
|||
"regexp" |
|||
"strconv" |
|||
"strings" |
|||
) |
|||
|
|||
var ( |
|||
// uint8Type is a reflect.Type representing a uint8. It is used to
|
|||
// convert cgo types to uint8 slices for hexdumping.
|
|||
uint8Type = reflect.TypeOf(uint8(0)) |
|||
|
|||
// cCharRE is a regular expression that matches a cgo char.
|
|||
// It is used to detect character arrays to hexdump them.
|
|||
cCharRE = regexp.MustCompile("^.*\\._Ctype_char$") |
|||
|
|||
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
|
|||
// char. It is used to detect unsigned character arrays to hexdump
|
|||
// them.
|
|||
cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$") |
|||
|
|||
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
|
|||
// It is used to detect uint8_t arrays to hexdump them.
|
|||
cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$") |
|||
) |
|||
|
|||
// dumpState contains information about the state of a dump operation.
|
|||
type dumpState struct { |
|||
w io.Writer |
|||
depth int |
|||
pointers map[uintptr]int |
|||
ignoreNextType bool |
|||
ignoreNextIndent bool |
|||
cs *ConfigState |
|||
} |
|||
|
|||
// indent performs indentation according to the depth level and cs.Indent
|
|||
// option.
|
|||
func (d *dumpState) indent() { |
|||
if d.ignoreNextIndent { |
|||
d.ignoreNextIndent = false |
|||
return |
|||
} |
|||
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth)) |
|||
} |
|||
|
|||
// unpackValue returns values inside of non-nil interfaces when possible.
|
|||
// This is useful for data types like structs, arrays, slices, and maps which
|
|||
// can contain varying types packed inside an interface.
|
|||
func (d *dumpState) unpackValue(v reflect.Value) reflect.Value { |
|||
if v.Kind() == reflect.Interface && !v.IsNil() { |
|||
v = v.Elem() |
|||
} |
|||
return v |
|||
} |
|||
|
|||
// dumpPtr handles formatting of pointers by indirecting them as necessary.
|
|||
func (d *dumpState) dumpPtr(v reflect.Value) { |
|||
// Remove pointers at or below the current depth from map used to detect
|
|||
// circular refs.
|
|||
for k, depth := range d.pointers { |
|||
if depth >= d.depth { |
|||
delete(d.pointers, k) |
|||
} |
|||
} |
|||
|
|||
// Keep list of all dereferenced pointers to show later.
|
|||
pointerChain := make([]uintptr, 0) |
|||
|
|||
// Figure out how many levels of indirection there are by dereferencing
|
|||
// pointers and unpacking interfaces down the chain while detecting circular
|
|||
// references.
|
|||
nilFound := false |
|||
cycleFound := false |
|||
indirects := 0 |
|||
ve := v |
|||
for ve.Kind() == reflect.Ptr { |
|||
if ve.IsNil() { |
|||
nilFound = true |
|||
break |
|||
} |
|||
indirects++ |
|||
addr := ve.Pointer() |
|||
pointerChain = append(pointerChain, addr) |
|||
if pd, ok := d.pointers[addr]; ok && pd < d.depth { |
|||
cycleFound = true |
|||
indirects-- |
|||
break |
|||
} |
|||
d.pointers[addr] = d.depth |
|||
|
|||
ve = ve.Elem() |
|||
if ve.Kind() == reflect.Interface { |
|||
if ve.IsNil() { |
|||
nilFound = true |
|||
break |
|||
} |
|||
ve = ve.Elem() |
|||
} |
|||
} |
|||
|
|||
// Display type information.
|
|||
d.w.Write(openParenBytes) |
|||
d.w.Write(bytes.Repeat(asteriskBytes, indirects)) |
|||
d.w.Write([]byte(ve.Type().String())) |
|||
d.w.Write(closeParenBytes) |
|||
|
|||
// Display pointer information.
|
|||
if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 { |
|||
d.w.Write(openParenBytes) |
|||
for i, addr := range pointerChain { |
|||
if i > 0 { |
|||
d.w.Write(pointerChainBytes) |
|||
} |
|||
printHexPtr(d.w, addr) |
|||
} |
|||
d.w.Write(closeParenBytes) |
|||
} |
|||
|
|||
// Display dereferenced value.
|
|||
d.w.Write(openParenBytes) |
|||
switch { |
|||
case nilFound == true: |
|||
d.w.Write(nilAngleBytes) |
|||
|
|||
case cycleFound == true: |
|||
d.w.Write(circularBytes) |
|||
|
|||
default: |
|||
d.ignoreNextType = true |
|||
d.dump(ve) |
|||
} |
|||
d.w.Write(closeParenBytes) |
|||
} |
|||
|
|||
// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
|
|||
// reflection) arrays and slices are dumped in hexdump -C fashion.
|
|||
func (d *dumpState) dumpSlice(v reflect.Value) { |
|||
// Determine whether this type should be hex dumped or not. Also,
|
|||
// for types which should be hexdumped, try to use the underlying data
|
|||
// first, then fall back to trying to convert them to a uint8 slice.
|
|||
var buf []uint8 |
|||
doConvert := false |
|||
doHexDump := false |
|||
numEntries := v.Len() |
|||
if numEntries > 0 { |
|||
vt := v.Index(0).Type() |
|||
vts := vt.String() |
|||
switch { |
|||
// C types that need to be converted.
|
|||
case cCharRE.MatchString(vts): |
|||
fallthrough |
|||
case cUnsignedCharRE.MatchString(vts): |
|||
fallthrough |
|||
case cUint8tCharRE.MatchString(vts): |
|||
doConvert = true |
|||
|
|||
// Try to use existing uint8 slices and fall back to converting
|
|||
// and copying if that fails.
|
|||
case vt.Kind() == reflect.Uint8: |
|||
// We need an addressable interface to convert the type
|
|||
// to a byte slice. However, the reflect package won't
|
|||
// give us an interface on certain things like
|
|||
// unexported struct fields in order to enforce
|
|||
// visibility rules. We use unsafe, when available, to
|
|||
// bypass these restrictions since this package does not
|
|||
// mutate the values.
|
|||
vs := v |
|||
if !vs.CanInterface() || !vs.CanAddr() { |
|||
vs = unsafeReflectValue(vs) |
|||
} |
|||
if !UnsafeDisabled { |
|||
vs = vs.Slice(0, numEntries) |
|||
|
|||
// Use the existing uint8 slice if it can be
|
|||
// type asserted.
|
|||
iface := vs.Interface() |
|||
if slice, ok := iface.([]uint8); ok { |
|||
buf = slice |
|||
doHexDump = true |
|||
break |
|||
} |
|||
} |
|||
|
|||
// The underlying data needs to be converted if it can't
|
|||
// be type asserted to a uint8 slice.
|
|||
doConvert = true |
|||
} |
|||
|
|||
// Copy and convert the underlying type if needed.
|
|||
if doConvert && vt.ConvertibleTo(uint8Type) { |
|||
// Convert and copy each element into a uint8 byte
|
|||
// slice.
|
|||
buf = make([]uint8, numEntries) |
|||
for i := 0; i < numEntries; i++ { |
|||
vv := v.Index(i) |
|||
buf[i] = uint8(vv.Convert(uint8Type).Uint()) |
|||
} |
|||
doHexDump = true |
|||
} |
|||
} |
|||
|
|||
// Hexdump the entire slice as needed.
|
|||
if doHexDump { |
|||
indent := strings.Repeat(d.cs.Indent, d.depth) |
|||
str := indent + hex.Dump(buf) |
|||
str = strings.Replace(str, "\n", "\n"+indent, -1) |
|||
str = strings.TrimRight(str, d.cs.Indent) |
|||
d.w.Write([]byte(str)) |
|||
return |
|||
} |
|||
|
|||
// Recursively call dump for each item.
|
|||
for i := 0; i < numEntries; i++ { |
|||
d.dump(d.unpackValue(v.Index(i))) |
|||
if i < (numEntries - 1) { |
|||
d.w.Write(commaNewlineBytes) |
|||
} else { |
|||
d.w.Write(newlineBytes) |
|||
} |
|||
} |
|||
} |
|||
|
|||
// dump is the main workhorse for dumping a value. It uses the passed reflect
|
|||
// value to figure out what kind of object we are dealing with and formats it
|
|||
// appropriately. It is a recursive function, however circular data structures
|
|||
// are detected and handled properly.
|
|||
func (d *dumpState) dump(v reflect.Value) { |
|||
// Handle invalid reflect values immediately.
|
|||
kind := v.Kind() |
|||
if kind == reflect.Invalid { |
|||
d.w.Write(invalidAngleBytes) |
|||
return |
|||
} |
|||
|
|||
// Handle pointers specially.
|
|||
if kind == reflect.Ptr { |
|||
d.indent() |
|||
d.dumpPtr(v) |
|||
return |
|||
} |
|||
|
|||
// Print type information unless already handled elsewhere.
|
|||
if !d.ignoreNextType { |
|||
d.indent() |
|||
d.w.Write(openParenBytes) |
|||
d.w.Write([]byte(v.Type().String())) |
|||
d.w.Write(closeParenBytes) |
|||
d.w.Write(spaceBytes) |
|||
} |
|||
d.ignoreNextType = false |
|||
|
|||
// Display length and capacity if the built-in len and cap functions
|
|||
// work with the value's kind and the len/cap itself is non-zero.
|
|||
valueLen, valueCap := 0, 0 |
|||
switch v.Kind() { |
|||
case reflect.Array, reflect.Slice, reflect.Chan: |
|||
valueLen, valueCap = v.Len(), v.Cap() |
|||
case reflect.Map, reflect.String: |
|||
valueLen = v.Len() |
|||
} |
|||
if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 { |
|||
d.w.Write(openParenBytes) |
|||
if valueLen != 0 { |
|||
d.w.Write(lenEqualsBytes) |
|||
printInt(d.w, int64(valueLen), 10) |
|||
} |
|||
if !d.cs.DisableCapacities && valueCap != 0 { |
|||
if valueLen != 0 { |
|||
d.w.Write(spaceBytes) |
|||
} |
|||
d.w.Write(capEqualsBytes) |
|||
printInt(d.w, int64(valueCap), 10) |
|||
} |
|||
d.w.Write(closeParenBytes) |
|||
d.w.Write(spaceBytes) |
|||
} |
|||
|
|||
// Call Stringer/error interfaces if they exist and the handle methods flag
|
|||
// is enabled
|
|||
if !d.cs.DisableMethods { |
|||
if (kind != reflect.Invalid) && (kind != reflect.Interface) { |
|||
if handled := handleMethods(d.cs, d.w, v); handled { |
|||
return |
|||
} |
|||
} |
|||
} |
|||
|
|||
switch kind { |
|||
case reflect.Invalid: |
|||
// Do nothing. We should never get here since invalid has already
|
|||
// been handled above.
|
|||
|
|||
case reflect.Bool: |
|||
printBool(d.w, v.Bool()) |
|||
|
|||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: |
|||
printInt(d.w, v.Int(), 10) |
|||
|
|||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: |
|||
printUint(d.w, v.Uint(), 10) |
|||
|
|||
case reflect.Float32: |
|||
printFloat(d.w, v.Float(), 32) |
|||
|
|||
case reflect.Float64: |
|||
printFloat(d.w, v.Float(), 64) |
|||
|
|||
case reflect.Complex64: |
|||
printComplex(d.w, v.Complex(), 32) |
|||
|
|||
case reflect.Complex128: |
|||
printComplex(d.w, v.Complex(), 64) |
|||
|
|||
case reflect.Slice: |
|||
if v.IsNil() { |
|||
d.w.Write(nilAngleBytes) |
|||
break |
|||
} |
|||
fallthrough |
|||
|
|||
case reflect.Array: |
|||
d.w.Write(openBraceNewlineBytes) |
|||
d.depth++ |
|||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { |
|||
d.indent() |
|||
d.w.Write(maxNewlineBytes) |
|||
} else { |
|||
d.dumpSlice(v) |
|||
} |
|||
d.depth-- |
|||
d.indent() |
|||
d.w.Write(closeBraceBytes) |
|||
|
|||
case reflect.String: |
|||
d.w.Write([]byte(strconv.Quote(v.String()))) |
|||
|
|||
case reflect.Interface: |
|||
// The only time we should get here is for nil interfaces due to
|
|||
// unpackValue calls.
|
|||
if v.IsNil() { |
|||
d.w.Write(nilAngleBytes) |
|||
} |
|||
|
|||
case reflect.Ptr: |
|||
// Do nothing. We should never get here since pointers have already
|
|||
// been handled above.
|
|||
|
|||
case reflect.Map: |
|||
// nil maps should be indicated as different than empty maps
|
|||
if v.IsNil() { |
|||
d.w.Write(nilAngleBytes) |
|||
break |
|||
} |
|||
|
|||
d.w.Write(openBraceNewlineBytes) |
|||
d.depth++ |
|||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { |
|||
d.indent() |
|||
d.w.Write(maxNewlineBytes) |
|||
} else { |
|||
numEntries := v.Len() |
|||
keys := v.MapKeys() |
|||
if d.cs.SortKeys { |
|||
sortValues(keys, d.cs) |
|||
} |
|||
for i, key := range keys { |
|||
d.dump(d.unpackValue(key)) |
|||
d.w.Write(colonSpaceBytes) |
|||
d.ignoreNextIndent = true |
|||
d.dump(d.unpackValue(v.MapIndex(key))) |
|||
if i < (numEntries - 1) { |
|||
d.w.Write(commaNewlineBytes) |
|||
} else { |
|||
d.w.Write(newlineBytes) |
|||
} |
|||
} |
|||
} |
|||
d.depth-- |
|||
d.indent() |
|||
d.w.Write(closeBraceBytes) |
|||
|
|||
case reflect.Struct: |
|||
d.w.Write(openBraceNewlineBytes) |
|||
d.depth++ |
|||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { |
|||
d.indent() |
|||
d.w.Write(maxNewlineBytes) |
|||
} else { |
|||
vt := v.Type() |
|||
numFields := v.NumField() |
|||
for i := 0; i < numFields; i++ { |
|||
d.indent() |
|||
vtf := vt.Field(i) |
|||
d.w.Write([]byte(vtf.Name)) |
|||
d.w.Write(colonSpaceBytes) |
|||
d.ignoreNextIndent = true |
|||
d.dump(d.unpackValue(v.Field(i))) |
|||
if i < (numFields - 1) { |
|||
d.w.Write(commaNewlineBytes) |
|||
} else { |
|||
d.w.Write(newlineBytes) |
|||
} |
|||
} |
|||
} |
|||
d.depth-- |
|||
d.indent() |
|||
d.w.Write(closeBraceBytes) |
|||
|
|||
case reflect.Uintptr: |
|||
printHexPtr(d.w, uintptr(v.Uint())) |
|||
|
|||
case reflect.UnsafePointer, reflect.Chan, reflect.Func: |
|||
printHexPtr(d.w, v.Pointer()) |
|||
|
|||
// There were not any other types at the time this code was written, but
|
|||
// fall back to letting the default fmt package handle it in case any new
|
|||
// types are added.
|
|||
default: |
|||
if v.CanInterface() { |
|||
fmt.Fprintf(d.w, "%v", v.Interface()) |
|||
} else { |
|||
fmt.Fprintf(d.w, "%v", v.String()) |
|||
} |
|||
} |
|||
} |
|||
|
|||
// fdump is a helper function to consolidate the logic from the various public
|
|||
// methods which take varying writers and config states.
|
|||
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) { |
|||
for _, arg := range a { |
|||
if arg == nil { |
|||
w.Write(interfaceBytes) |
|||
w.Write(spaceBytes) |
|||
w.Write(nilAngleBytes) |
|||
w.Write(newlineBytes) |
|||
continue |
|||
} |
|||
|
|||
d := dumpState{w: w, cs: cs} |
|||
d.pointers = make(map[uintptr]int) |
|||
d.dump(reflect.ValueOf(arg)) |
|||
d.w.Write(newlineBytes) |
|||
} |
|||
} |
|||
|
|||
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
|||
// exactly the same as Dump.
|
|||
func Fdump(w io.Writer, a ...interface{}) { |
|||
fdump(&Config, w, a...) |
|||
} |
|||
|
|||
// Sdump returns a string with the passed arguments formatted exactly the same
|
|||
// as Dump.
|
|||
func Sdump(a ...interface{}) string { |
|||
var buf bytes.Buffer |
|||
fdump(&Config, &buf, a...) |
|||
return buf.String() |
|||
} |
|||
|
|||
/* |
|||
Dump displays the passed parameters to standard out with newlines, customizable |
|||
indentation, and additional debug information such as complete types and all |
|||
pointer addresses used to indirect to the final value. It provides the |
|||
following features over the built-in printing facilities provided by the fmt |
|||
package: |
|||
|
|||
* Pointers are dereferenced and followed |
|||
* Circular data structures are detected and handled properly |
|||
* Custom Stringer/error interfaces are optionally invoked, including |
|||
on unexported types |
|||
* Custom types which only implement the Stringer/error interfaces via |
|||
a pointer receiver are optionally invoked when passing non-pointer |
|||
variables |
|||
* Byte arrays and slices are dumped like the hexdump -C command which |
|||
includes offsets, byte values in hex, and ASCII output |
|||
|
|||
The configuration options are controlled by an exported package global, |
|||
spew.Config. See ConfigState for options documentation. |
|||
|
|||
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to |
|||
get the formatted result as a string. |
|||
*/ |
|||
func Dump(a ...interface{}) { |
|||
fdump(&Config, os.Stdout, a...) |
|||
} |
|||
@ -1,419 +0,0 @@ |
|||
/* |
|||
* Copyright (c) 2013 Dave Collins <dave@davec.name> |
|||
* |
|||
* Permission to use, copy, modify, and distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
package spew |
|||
|
|||
import ( |
|||
"bytes" |
|||
"fmt" |
|||
"reflect" |
|||
"strconv" |
|||
"strings" |
|||
) |
|||
|
|||
// supportedFlags is a list of all the character flags supported by fmt package.
|
|||
const supportedFlags = "0-+# " |
|||
|
|||
// formatState implements the fmt.Formatter interface and contains information
|
|||
// about the state of a formatting operation. The NewFormatter function can
|
|||
// be used to get a new Formatter which can be used directly as arguments
|
|||
// in standard fmt package printing calls.
|
|||
type formatState struct { |
|||
value interface{} |
|||
fs fmt.State |
|||
depth int |
|||
pointers map[uintptr]int |
|||
ignoreNextType bool |
|||
cs *ConfigState |
|||
} |
|||
|
|||
// buildDefaultFormat recreates the original format string without precision
|
|||
// and width information to pass in to fmt.Sprintf in the case of an
|
|||
// unrecognized type. Unless new types are added to the language, this
|
|||
// function won't ever be called.
|
|||
func (f *formatState) buildDefaultFormat() (format string) { |
|||
buf := bytes.NewBuffer(percentBytes) |
|||
|
|||
for _, flag := range supportedFlags { |
|||
if f.fs.Flag(int(flag)) { |
|||
buf.WriteRune(flag) |
|||
} |
|||
} |
|||
|
|||
buf.WriteRune('v') |
|||
|
|||
format = buf.String() |
|||
return format |
|||
} |
|||
|
|||
// constructOrigFormat recreates the original format string including precision
|
|||
// and width information to pass along to the standard fmt package. This allows
|
|||
// automatic deferral of all format strings this package doesn't support.
|
|||
func (f *formatState) constructOrigFormat(verb rune) (format string) { |
|||
buf := bytes.NewBuffer(percentBytes) |
|||
|
|||
for _, flag := range supportedFlags { |
|||
if f.fs.Flag(int(flag)) { |
|||
buf.WriteRune(flag) |
|||
} |
|||
} |
|||
|
|||
if width, ok := f.fs.Width(); ok { |
|||
buf.WriteString(strconv.Itoa(width)) |
|||
} |
|||
|
|||
if precision, ok := f.fs.Precision(); ok { |
|||
buf.Write(precisionBytes) |
|||
buf.WriteString(strconv.Itoa(precision)) |
|||
} |
|||
|
|||
buf.WriteRune(verb) |
|||
|
|||
format = buf.String() |
|||
return format |
|||
} |
|||
|
|||
// unpackValue returns values inside of non-nil interfaces when possible and
|
|||
// ensures that types for values which have been unpacked from an interface
|
|||
// are displayed when the show types flag is also set.
|
|||
// This is useful for data types like structs, arrays, slices, and maps which
|
|||
// can contain varying types packed inside an interface.
|
|||
func (f *formatState) unpackValue(v reflect.Value) reflect.Value { |
|||
if v.Kind() == reflect.Interface { |
|||
f.ignoreNextType = false |
|||
if !v.IsNil() { |
|||
v = v.Elem() |
|||
} |
|||
} |
|||
return v |
|||
} |
|||
|
|||
// formatPtr handles formatting of pointers by indirecting them as necessary.
|
|||
func (f *formatState) formatPtr(v reflect.Value) { |
|||
// Display nil if top level pointer is nil.
|
|||
showTypes := f.fs.Flag('#') |
|||
if v.IsNil() && (!showTypes || f.ignoreNextType) { |
|||
f.fs.Write(nilAngleBytes) |
|||
return |
|||
} |
|||
|
|||
// Remove pointers at or below the current depth from map used to detect
|
|||
// circular refs.
|
|||
for k, depth := range f.pointers { |
|||
if depth >= f.depth { |
|||
delete(f.pointers, k) |
|||
} |
|||
} |
|||
|
|||
// Keep list of all dereferenced pointers to possibly show later.
|
|||
pointerChain := make([]uintptr, 0) |
|||
|
|||
// Figure out how many levels of indirection there are by derferencing
|
|||
// pointers and unpacking interfaces down the chain while detecting circular
|
|||
// references.
|
|||
nilFound := false |
|||
cycleFound := false |
|||
indirects := 0 |
|||
ve := v |
|||
for ve.Kind() == reflect.Ptr { |
|||
if ve.IsNil() { |
|||
nilFound = true |
|||
break |
|||
} |
|||
indirects++ |
|||
addr := ve.Pointer() |
|||
pointerChain = append(pointerChain, addr) |
|||
if pd, ok := f.pointers[addr]; ok && pd < f.depth { |
|||
cycleFound = true |
|||
indirects-- |
|||
break |
|||
} |
|||
f.pointers[addr] = f.depth |
|||
|
|||
ve = ve.Elem() |
|||
if ve.Kind() == reflect.Interface { |
|||
if ve.IsNil() { |
|||
nilFound = true |
|||
break |
|||
} |
|||
ve = ve.Elem() |
|||
} |
|||
} |
|||
|
|||
// Display type or indirection level depending on flags.
|
|||
if showTypes && !f.ignoreNextType { |
|||
f.fs.Write(openParenBytes) |
|||
f.fs.Write(bytes.Repeat(asteriskBytes, indirects)) |
|||
f.fs.Write([]byte(ve.Type().String())) |
|||
f.fs.Write(closeParenBytes) |
|||
} else { |
|||
if nilFound || cycleFound { |
|||
indirects += strings.Count(ve.Type().String(), "*") |
|||
} |
|||
f.fs.Write(openAngleBytes) |
|||
f.fs.Write([]byte(strings.Repeat("*", indirects))) |
|||
f.fs.Write(closeAngleBytes) |
|||
} |
|||
|
|||
// Display pointer information depending on flags.
|
|||
if f.fs.Flag('+') && (len(pointerChain) > 0) { |
|||
f.fs.Write(openParenBytes) |
|||
for i, addr := range pointerChain { |
|||
if i > 0 { |
|||
f.fs.Write(pointerChainBytes) |
|||
} |
|||
printHexPtr(f.fs, addr) |
|||
} |
|||
f.fs.Write(closeParenBytes) |
|||
} |
|||
|
|||
// Display dereferenced value.
|
|||
switch { |
|||
case nilFound == true: |
|||
f.fs.Write(nilAngleBytes) |
|||
|
|||
case cycleFound == true: |
|||
f.fs.Write(circularShortBytes) |
|||
|
|||
default: |
|||
f.ignoreNextType = true |
|||
f.format(ve) |
|||
} |
|||
} |
|||
|
|||
// format is the main workhorse for providing the Formatter interface. It
|
|||
// uses the passed reflect value to figure out what kind of object we are
|
|||
// dealing with and formats it appropriately. It is a recursive function,
|
|||
// however circular data structures are detected and handled properly.
|
|||
func (f *formatState) format(v reflect.Value) { |
|||
// Handle invalid reflect values immediately.
|
|||
kind := v.Kind() |
|||
if kind == reflect.Invalid { |
|||
f.fs.Write(invalidAngleBytes) |
|||
return |
|||
} |
|||
|
|||
// Handle pointers specially.
|
|||
if kind == reflect.Ptr { |
|||
f.formatPtr(v) |
|||
return |
|||
} |
|||
|
|||
// Print type information unless already handled elsewhere.
|
|||
if !f.ignoreNextType && f.fs.Flag('#') { |
|||
f.fs.Write(openParenBytes) |
|||
f.fs.Write([]byte(v.Type().String())) |
|||
f.fs.Write(closeParenBytes) |
|||
} |
|||
f.ignoreNextType = false |
|||
|
|||
// Call Stringer/error interfaces if they exist and the handle methods
|
|||
// flag is enabled.
|
|||
if !f.cs.DisableMethods { |
|||
if (kind != reflect.Invalid) && (kind != reflect.Interface) { |
|||
if handled := handleMethods(f.cs, f.fs, v); handled { |
|||
return |
|||
} |
|||
} |
|||
} |
|||
|
|||
switch kind { |
|||
case reflect.Invalid: |
|||
// Do nothing. We should never get here since invalid has already
|
|||
// been handled above.
|
|||
|
|||
case reflect.Bool: |
|||
printBool(f.fs, v.Bool()) |
|||
|
|||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: |
|||
printInt(f.fs, v.Int(), 10) |
|||
|
|||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: |
|||
printUint(f.fs, v.Uint(), 10) |
|||
|
|||
case reflect.Float32: |
|||
printFloat(f.fs, v.Float(), 32) |
|||
|
|||
case reflect.Float64: |
|||
printFloat(f.fs, v.Float(), 64) |
|||
|
|||
case reflect.Complex64: |
|||
printComplex(f.fs, v.Complex(), 32) |
|||
|
|||
case reflect.Complex128: |
|||
printComplex(f.fs, v.Complex(), 64) |
|||
|
|||
case reflect.Slice: |
|||
if v.IsNil() { |
|||
f.fs.Write(nilAngleBytes) |
|||
break |
|||
} |
|||
fallthrough |
|||
|
|||
case reflect.Array: |
|||
f.fs.Write(openBracketBytes) |
|||
f.depth++ |
|||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { |
|||
f.fs.Write(maxShortBytes) |
|||
} else { |
|||
numEntries := v.Len() |
|||
for i := 0; i < numEntries; i++ { |
|||
if i > 0 { |
|||
f.fs.Write(spaceBytes) |
|||
} |
|||
f.ignoreNextType = true |
|||
f.format(f.unpackValue(v.Index(i))) |
|||
} |
|||
} |
|||
f.depth-- |
|||
f.fs.Write(closeBracketBytes) |
|||
|
|||
case reflect.String: |
|||
f.fs.Write([]byte(v.String())) |
|||
|
|||
case reflect.Interface: |
|||
// The only time we should get here is for nil interfaces due to
|
|||
// unpackValue calls.
|
|||
if v.IsNil() { |
|||
f.fs.Write(nilAngleBytes) |
|||
} |
|||
|
|||
case reflect.Ptr: |
|||
// Do nothing. We should never get here since pointers have already
|
|||
// been handled above.
|
|||
|
|||
case reflect.Map: |
|||
// nil maps should be indicated as different than empty maps
|
|||
if v.IsNil() { |
|||
f.fs.Write(nilAngleBytes) |
|||
break |
|||
} |
|||
|
|||
f.fs.Write(openMapBytes) |
|||
f.depth++ |
|||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { |
|||
f.fs.Write(maxShortBytes) |
|||
} else { |
|||
keys := v.MapKeys() |
|||
if f.cs.SortKeys { |
|||
sortValues(keys, f.cs) |
|||
} |
|||
for i, key := range keys { |
|||
if i > 0 { |
|||
f.fs.Write(spaceBytes) |
|||
} |
|||
f.ignoreNextType = true |
|||
f.format(f.unpackValue(key)) |
|||
f.fs.Write(colonBytes) |
|||
f.ignoreNextType = true |
|||
f.format(f.unpackValue(v.MapIndex(key))) |
|||
} |
|||
} |
|||
f.depth-- |
|||
f.fs.Write(closeMapBytes) |
|||
|
|||
case reflect.Struct: |
|||
numFields := v.NumField() |
|||
f.fs.Write(openBraceBytes) |
|||
f.depth++ |
|||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { |
|||
f.fs.Write(maxShortBytes) |
|||
} else { |
|||
vt := v.Type() |
|||
for i := 0; i < numFields; i++ { |
|||
if i > 0 { |
|||
f.fs.Write(spaceBytes) |
|||
} |
|||
vtf := vt.Field(i) |
|||
if f.fs.Flag('+') || f.fs.Flag('#') { |
|||
f.fs.Write([]byte(vtf.Name)) |
|||
f.fs.Write(colonBytes) |
|||
} |
|||
f.format(f.unpackValue(v.Field(i))) |
|||
} |
|||
} |
|||
f.depth-- |
|||
f.fs.Write(closeBraceBytes) |
|||
|
|||
case reflect.Uintptr: |
|||
printHexPtr(f.fs, uintptr(v.Uint())) |
|||
|
|||
case reflect.UnsafePointer, reflect.Chan, reflect.Func: |
|||
printHexPtr(f.fs, v.Pointer()) |
|||
|
|||
// There were not any other types at the time this code was written, but
|
|||
// fall back to letting the default fmt package handle it if any get added.
|
|||
default: |
|||
format := f.buildDefaultFormat() |
|||
if v.CanInterface() { |
|||
fmt.Fprintf(f.fs, format, v.Interface()) |
|||
} else { |
|||
fmt.Fprintf(f.fs, format, v.String()) |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
|
|||
// details.
|
|||
func (f *formatState) Format(fs fmt.State, verb rune) { |
|||
f.fs = fs |
|||
|
|||
// Use standard formatting for verbs that are not v.
|
|||
if verb != 'v' { |
|||
format := f.constructOrigFormat(verb) |
|||
fmt.Fprintf(fs, format, f.value) |
|||
return |
|||
} |
|||
|
|||
if f.value == nil { |
|||
if fs.Flag('#') { |
|||
fs.Write(interfaceBytes) |
|||
} |
|||
fs.Write(nilAngleBytes) |
|||
return |
|||
} |
|||
|
|||
f.format(reflect.ValueOf(f.value)) |
|||
} |
|||
|
|||
// newFormatter is a helper function to consolidate the logic from the various
|
|||
// public methods which take varying config states.
|
|||
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter { |
|||
fs := &formatState{value: v, cs: cs} |
|||
fs.pointers = make(map[uintptr]int) |
|||
return fs |
|||
} |
|||
|
|||
/* |
|||
NewFormatter returns a custom formatter that satisfies the fmt.Formatter |
|||
interface. As a result, it integrates cleanly with standard fmt package |
|||
printing functions. The formatter is useful for inline printing of smaller data |
|||
types similar to the standard %v format specifier. |
|||
|
|||
The custom formatter only responds to the %v (most compact), %+v (adds pointer |
|||
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb |
|||
combinations. Any other verbs such as %x and %q will be sent to the the |
|||
standard fmt package for formatting. In addition, the custom formatter ignores |
|||
the width and precision arguments (however they will still work on the format |
|||
specifiers not handled by the custom formatter). |
|||
|
|||
Typically this function shouldn't be called directly. It is much easier to make |
|||
use of the custom formatter by calling one of the convenience functions such as |
|||
Printf, Println, or Fprintf. |
|||
*/ |
|||
func NewFormatter(v interface{}) fmt.Formatter { |
|||
return newFormatter(&Config, v) |
|||
} |
|||
@ -1,148 +0,0 @@ |
|||
/* |
|||
* Copyright (c) 2013 Dave Collins <dave@davec.name> |
|||
* |
|||
* Permission to use, copy, modify, and distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
*/ |
|||
|
|||
package spew |
|||
|
|||
import ( |
|||
"fmt" |
|||
"io" |
|||
) |
|||
|
|||
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
|||
// passed with a default Formatter interface returned by NewFormatter. It
|
|||
// returns the formatted string as a value that satisfies error. See
|
|||
// NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
|||
func Errorf(format string, a ...interface{}) (err error) { |
|||
return fmt.Errorf(format, convertArgs(a)...) |
|||
} |
|||
|
|||
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
|||
// passed with a default Formatter interface returned by NewFormatter. It
|
|||
// returns the number of bytes written and any write error encountered. See
|
|||
// NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
|
|||
func Fprint(w io.Writer, a ...interface{}) (n int, err error) { |
|||
return fmt.Fprint(w, convertArgs(a)...) |
|||
} |
|||
|
|||
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
|||
// passed with a default Formatter interface returned by NewFormatter. It
|
|||
// returns the number of bytes written and any write error encountered. See
|
|||
// NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
|
|||
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { |
|||
return fmt.Fprintf(w, format, convertArgs(a)...) |
|||
} |
|||
|
|||
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
|||
// passed with a default Formatter interface returned by NewFormatter. See
|
|||
// NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
|
|||
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { |
|||
return fmt.Fprintln(w, convertArgs(a)...) |
|||
} |
|||
|
|||
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
|||
// passed with a default Formatter interface returned by NewFormatter. It
|
|||
// returns the number of bytes written and any write error encountered. See
|
|||
// NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
|
|||
func Print(a ...interface{}) (n int, err error) { |
|||
return fmt.Print(convertArgs(a)...) |
|||
} |
|||
|
|||
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
|||
// passed with a default Formatter interface returned by NewFormatter. It
|
|||
// returns the number of bytes written and any write error encountered. See
|
|||
// NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
|||
func Printf(format string, a ...interface{}) (n int, err error) { |
|||
return fmt.Printf(format, convertArgs(a)...) |
|||
} |
|||
|
|||
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
|||
// passed with a default Formatter interface returned by NewFormatter. It
|
|||
// returns the number of bytes written and any write error encountered. See
|
|||
// NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
|
|||
func Println(a ...interface{}) (n int, err error) { |
|||
return fmt.Println(convertArgs(a)...) |
|||
} |
|||
|
|||
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
|
|||
// passed with a default Formatter interface returned by NewFormatter. It
|
|||
// returns the resulting string. See NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
|
|||
func Sprint(a ...interface{}) string { |
|||
return fmt.Sprint(convertArgs(a)...) |
|||
} |
|||
|
|||
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
|
|||
// passed with a default Formatter interface returned by NewFormatter. It
|
|||
// returns the resulting string. See NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
|||
func Sprintf(format string, a ...interface{}) string { |
|||
return fmt.Sprintf(format, convertArgs(a)...) |
|||
} |
|||
|
|||
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
|
|||
// were passed with a default Formatter interface returned by NewFormatter. It
|
|||
// returns the resulting string. See NewFormatter for formatting details.
|
|||
//
|
|||
// This function is shorthand for the following syntax:
|
|||
//
|
|||
// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
|
|||
func Sprintln(a ...interface{}) string { |
|||
return fmt.Sprintln(convertArgs(a)...) |
|||
} |
|||
|
|||
// convertArgs accepts a slice of arguments and returns a slice of the same
|
|||
// length with each argument converted to a default spew Formatter interface.
|
|||
func convertArgs(args []interface{}) (formatters []interface{}) { |
|||
formatters = make([]interface{}, len(args)) |
|||
for index, arg := range args { |
|||
formatters[index] = NewFormatter(arg) |
|||
} |
|||
return formatters |
|||
} |
|||
@ -1,2 +1,2 @@ |
|||
# pht |
|||
Pure HTTP Tunnel - Tunnel over HTTP using only GET and POST requests. |
|||
Plain HTTP Tunnel - Tunnel over HTTP using only GET and POST requests, NO Websocket, NO CONNECT method. |
|||
|
|||
@ -0,0 +1,17 @@ |
|||
# Changelog |
|||
|
|||
## v0.6.0 (unreleased) |
|||
|
|||
- Added `quic.Config` options for maximal flow control windows |
|||
- Add a `quic.Config` option for QUIC versions |
|||
- Add a `quic.Config` option to request truncation of the connection ID from a server |
|||
- Add a `quic.Config` option to configure the source address validation |
|||
- Add a `quic.Config` option to configure the handshake timeout |
|||
- Add a `quic.Config` option to configure keep-alive |
|||
- Implement `net.Conn`-style deadlines for streams |
|||
- Remove the `tls.Config` from the `quic.Config`. The `tls.Config` must now be passed to the `Dial` and `Listen` functions as a separate parameter. See the [Godoc](https://godoc.org/github.com/lucas-clemente/quic-go) for details. |
|||
- Changed the log level environment variable to only accept strings ("DEBUG", "INFO", "ERROR"), see [the wiki](https://github.com/lucas-clemente/quic-go/wiki/Logging) for more details. |
|||
- Rename the `h2quic.QuicRoundTripper` to `h2quic.RoundTripper` |
|||
- Changed `h2quic.Server.Serve()` to accept a `net.PacketConn` |
|||
- Drop support for Go 1.7. |
|||
- Various bugfixes |
|||
@ -0,0 +1,38 @@ |
|||
package ackhandler |
|||
|
|||
import ( |
|||
"github.com/lucas-clemente/quic-go/frames" |
|||
) |
|||
|
|||
// Returns a new slice with all non-retransmittable frames deleted.
|
|||
func stripNonRetransmittableFrames(fs []frames.Frame) []frames.Frame { |
|||
res := make([]frames.Frame, 0, len(fs)) |
|||
for _, f := range fs { |
|||
if IsFrameRetransmittable(f) { |
|||
res = append(res, f) |
|||
} |
|||
} |
|||
return res |
|||
} |
|||
|
|||
// IsFrameRetransmittable returns true if the frame should be retransmitted.
|
|||
func IsFrameRetransmittable(f frames.Frame) bool { |
|||
switch f.(type) { |
|||
case *frames.StopWaitingFrame: |
|||
return false |
|||
case *frames.AckFrame: |
|||
return false |
|||
default: |
|||
return true |
|||
} |
|||
} |
|||
|
|||
// HasRetransmittableFrames returns true if at least one frame is retransmittable.
|
|||
func HasRetransmittableFrames(fs []frames.Frame) bool { |
|||
for _, f := range fs { |
|||
if IsFrameRetransmittable(f) { |
|||
return true |
|||
} |
|||
} |
|||
return false |
|||
} |
|||
@ -1,12 +0,0 @@ |
|||
package congestion |
|||
|
|||
import "github.com/lucas-clemente/quic-go/protocol" |
|||
|
|||
// PacketInfo combines packet number and length of a packet for congestion calculation
|
|||
type PacketInfo struct { |
|||
Number protocol.PacketNumber |
|||
Length protocol.ByteCount |
|||
} |
|||
|
|||
// PacketVector is passed to the congestion algorithm
|
|||
type PacketVector []PacketInfo |
|||
@ -0,0 +1,54 @@ |
|||
package quic |
|||
|
|||
import ( |
|||
"net" |
|||
"sync" |
|||
) |
|||
|
|||
type connection interface { |
|||
Write([]byte) error |
|||
Read([]byte) (int, net.Addr, error) |
|||
Close() error |
|||
LocalAddr() net.Addr |
|||
RemoteAddr() net.Addr |
|||
SetCurrentRemoteAddr(net.Addr) |
|||
} |
|||
|
|||
type conn struct { |
|||
mutex sync.RWMutex |
|||
|
|||
pconn net.PacketConn |
|||
currentAddr net.Addr |
|||
} |
|||
|
|||
var _ connection = &conn{} |
|||
|
|||
func (c *conn) Write(p []byte) error { |
|||
_, err := c.pconn.WriteTo(p, c.currentAddr) |
|||
return err |
|||
} |
|||
|
|||
func (c *conn) Read(p []byte) (int, net.Addr, error) { |
|||
return c.pconn.ReadFrom(p) |
|||
} |
|||
|
|||
func (c *conn) SetCurrentRemoteAddr(addr net.Addr) { |
|||
c.mutex.Lock() |
|||
c.currentAddr = addr |
|||
c.mutex.Unlock() |
|||
} |
|||
|
|||
func (c *conn) LocalAddr() net.Addr { |
|||
return c.pconn.LocalAddr() |
|||
} |
|||
|
|||
func (c *conn) RemoteAddr() net.Addr { |
|||
c.mutex.RLock() |
|||
addr := c.currentAddr |
|||
c.mutex.RUnlock() |
|||
return addr |
|||
} |
|||
|
|||
func (c *conn) Close() error { |
|||
return c.pconn.Close() |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
// +build go1.7
|
|||
|
|||
package h2quic |
|||
|
|||
import "net/http" |
|||
|
|||
func setUncompressed(res *http.Response) { |
|||
res.Uncompressed = true |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
// +build !go1.7
|
|||
|
|||
package h2quic |
|||
|
|||
import "net/http" |
|||
|
|||
func setUncompressed(res *http.Response) { |
|||
// http.Response.Uncompressed was introduced in go 1.7
|
|||
} |
|||
@ -1,16 +0,0 @@ |
|||
package handshake |
|||
|
|||
import "github.com/lucas-clemente/quic-go/protocol" |
|||
|
|||
// CryptoSetup is a crypto setup
|
|||
type CryptoSetup interface { |
|||
HandleCryptoStream() error |
|||
Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) |
|||
Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte |
|||
LockForSealing() |
|||
UnlockForSealing() |
|||
HandshakeComplete() bool |
|||
// TODO: clean up this interface
|
|||
DiversificationNonce() []byte // only needed for cryptoSetupServer
|
|||
SetDiversificationNonce([]byte) error // only needed for cryptoSetupClient
|
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
package handshake |
|||
|
|||
import "github.com/lucas-clemente/quic-go/protocol" |
|||
|
|||
// Sealer seals a packet
|
|||
type Sealer func(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte |
|||
|
|||
// CryptoSetup is a crypto setup
|
|||
type CryptoSetup interface { |
|||
Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error) |
|||
HandleCryptoStream() error |
|||
// TODO: clean up this interface
|
|||
DiversificationNonce() []byte // only needed for cryptoSetupServer
|
|||
SetDiversificationNonce([]byte) // only needed for cryptoSetupClient
|
|||
|
|||
GetSealer() (protocol.EncryptionLevel, Sealer) |
|||
GetSealerWithEncryptionLevel(protocol.EncryptionLevel) (Sealer, error) |
|||
GetSealerForCryptoStream() (protocol.EncryptionLevel, Sealer) |
|||
} |
|||
|
|||
// TransportParameters are parameters sent to the peer during the handshake
|
|||
type TransportParameters struct { |
|||
RequestConnectionIDTruncation bool |
|||
} |
|||
@ -0,0 +1,100 @@ |
|||
package handshake |
|||
|
|||
import ( |
|||
"encoding/asn1" |
|||
"fmt" |
|||
"net" |
|||
"time" |
|||
|
|||
"github.com/lucas-clemente/quic-go/crypto" |
|||
) |
|||
|
|||
const ( |
|||
stkPrefixIP byte = iota |
|||
stkPrefixString |
|||
) |
|||
|
|||
// An STK is a source address token
|
|||
type STK struct { |
|||
RemoteAddr string |
|||
SentTime time.Time |
|||
} |
|||
|
|||
// token is the struct that is used for ASN1 serialization and deserialization
|
|||
type token struct { |
|||
Data []byte |
|||
Timestamp int64 |
|||
} |
|||
|
|||
// An STKGenerator generates STKs
|
|||
type STKGenerator struct { |
|||
stkSource crypto.StkSource |
|||
} |
|||
|
|||
// NewSTKGenerator initializes a new STKGenerator
|
|||
func NewSTKGenerator() (*STKGenerator, error) { |
|||
stkSource, err := crypto.NewStkSource() |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return &STKGenerator{ |
|||
stkSource: stkSource, |
|||
}, nil |
|||
} |
|||
|
|||
// NewToken generates a new STK token for a given source address
|
|||
func (g *STKGenerator) NewToken(raddr net.Addr) ([]byte, error) { |
|||
data, err := asn1.Marshal(token{ |
|||
Data: encodeRemoteAddr(raddr), |
|||
Timestamp: time.Now().Unix(), |
|||
}) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return g.stkSource.NewToken(data) |
|||
} |
|||
|
|||
// DecodeToken decodes an STK token
|
|||
func (g *STKGenerator) DecodeToken(encrypted []byte) (*STK, error) { |
|||
// if the client didn't send any STK, DecodeToken will be called with a nil-slice
|
|||
if len(encrypted) == 0 { |
|||
return nil, nil |
|||
} |
|||
|
|||
data, err := g.stkSource.DecodeToken(encrypted) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
t := &token{} |
|||
rest, err := asn1.Unmarshal(data, t) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
if len(rest) != 0 { |
|||
return nil, fmt.Errorf("rest when unpacking token: %d", len(rest)) |
|||
} |
|||
return &STK{ |
|||
RemoteAddr: decodeRemoteAddr(t.Data), |
|||
SentTime: time.Unix(t.Timestamp, 0), |
|||
}, nil |
|||
} |
|||
|
|||
// encodeRemoteAddr encodes a remote address such that it can be saved in the STK
|
|||
func encodeRemoteAddr(remoteAddr net.Addr) []byte { |
|||
if udpAddr, ok := remoteAddr.(*net.UDPAddr); ok { |
|||
return append([]byte{stkPrefixIP}, udpAddr.IP...) |
|||
} |
|||
return append([]byte{stkPrefixString}, []byte(remoteAddr.String())...) |
|||
} |
|||
|
|||
// decodeRemoteAddr decodes the remote address saved in the STK
|
|||
func decodeRemoteAddr(data []byte) string { |
|||
// data will never be empty for an STK that we generated. Check it to be on the safe side
|
|||
if len(data) == 0 { |
|||
return "" |
|||
} |
|||
if data[0] == stkPrefixIP { |
|||
return net.IP(data[1:]).String() |
|||
} |
|||
return string(data[1:]) |
|||
} |
|||
@ -0,0 +1,121 @@ |
|||
package quic |
|||
|
|||
import ( |
|||
"io" |
|||
"net" |
|||
"time" |
|||
|
|||
"github.com/lucas-clemente/quic-go/protocol" |
|||
) |
|||
|
|||
// Stream is the interface implemented by QUIC streams
|
|||
type Stream interface { |
|||
// Read reads data from the stream.
|
|||
// Read can be made to time out and return a net.Error with Timeout() == true
|
|||
// after a fixed time limit; see SetDeadline and SetReadDeadline.
|
|||
io.Reader |
|||
// Write writes data to the stream.
|
|||
// Write can be made to time out and return a net.Error with Timeout() == true
|
|||
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
|
|||
io.Writer |
|||
io.Closer |
|||
StreamID() protocol.StreamID |
|||
// Reset closes the stream with an error.
|
|||
Reset(error) |
|||
// SetReadDeadline sets the deadline for future Read calls and
|
|||
// any currently-blocked Read call.
|
|||
// A zero value for t means Read will not time out.
|
|||
SetReadDeadline(t time.Time) error |
|||
// SetWriteDeadline sets the deadline for future Write calls
|
|||
// and any currently-blocked Write call.
|
|||
// Even if write times out, it may return n > 0, indicating that
|
|||
// some of the data was successfully written.
|
|||
// A zero value for t means Write will not time out.
|
|||
SetWriteDeadline(t time.Time) error |
|||
// SetDeadline sets the read and write deadlines associated
|
|||
// with the connection. It is equivalent to calling both
|
|||
// SetReadDeadline and SetWriteDeadline.
|
|||
SetDeadline(t time.Time) error |
|||
} |
|||
|
|||
// A Session is a QUIC connection between two peers.
|
|||
type Session interface { |
|||
// AcceptStream returns the next stream opened by the peer, blocking until one is available.
|
|||
// Since stream 1 is reserved for the crypto stream, the first stream is either 2 (for a client) or 3 (for a server).
|
|||
AcceptStream() (Stream, error) |
|||
// OpenStream opens a new QUIC stream, returning a special error when the peeer's concurrent stream limit is reached.
|
|||
// New streams always have the smallest possible stream ID.
|
|||
// TODO: Enable testing for the special error
|
|||
OpenStream() (Stream, error) |
|||
// OpenStreamSync opens a new QUIC stream, blocking until the peer's concurrent stream limit allows a new stream to be opened.
|
|||
// It always picks the smallest possible stream ID.
|
|||
OpenStreamSync() (Stream, error) |
|||
// LocalAddr returns the local address.
|
|||
LocalAddr() net.Addr |
|||
// RemoteAddr returns the address of the peer.
|
|||
RemoteAddr() net.Addr |
|||
// Close closes the connection. The error will be sent to the remote peer in a CONNECTION_CLOSE frame. An error value of nil is allowed and will cause a normal PeerGoingAway to be sent.
|
|||
Close(error) error |
|||
// WaitUntilClosed() blocks until the session is closed.
|
|||
// Warning: This API should not be considered stable and might change soon.
|
|||
WaitUntilClosed() |
|||
} |
|||
|
|||
// A NonFWSession is a QUIC connection between two peers half-way through the handshake.
|
|||
// The communication is encrypted, but not yet forward secure.
|
|||
type NonFWSession interface { |
|||
Session |
|||
WaitUntilHandshakeComplete() error |
|||
} |
|||
|
|||
// An STK is a Source Address token.
|
|||
// It is issued by the server and sent to the client. For the client, it is an opaque blob.
|
|||
// The client can send the STK in subsequent handshakes to prove ownership of its IP address.
|
|||
type STK struct { |
|||
// The remote address this token was issued for.
|
|||
// If the server is run on a net.UDPConn, this is the string representation of the IP address (net.IP.String())
|
|||
// Otherwise, this is the string representation of the net.Addr (net.Addr.String())
|
|||
remoteAddr string |
|||
// The time that the STK was issued (resolution 1 second)
|
|||
sentTime time.Time |
|||
} |
|||
|
|||
// Config contains all configuration data needed for a QUIC server or client.
|
|||
// More config parameters (such as timeouts) will be added soon, see e.g. https://github.com/lucas-clemente/quic-go/issues/441.
|
|||
type Config struct { |
|||
// The QUIC versions that can be negotiated.
|
|||
// If not set, it uses all versions available.
|
|||
// Warning: This API should not be considered stable and will change soon.
|
|||
Versions []protocol.VersionNumber |
|||
// Ask the server to truncate the connection ID sent in the Public Header.
|
|||
// This saves 8 bytes in the Public Header in every packet. However, if the IP address of the server changes, the connection cannot be migrated.
|
|||
// Currently only valid for the client.
|
|||
RequestConnectionIDTruncation bool |
|||
// HandshakeTimeout is the maximum duration that the cryptographic handshake may take.
|
|||
// If the timeout is exceeded, the connection is closed.
|
|||
// If this value is zero, the timeout is set to 10 seconds.
|
|||
HandshakeTimeout time.Duration |
|||
// AcceptSTK determines if an STK is accepted.
|
|||
// It is called with stk = nil if the client didn't send an STK.
|
|||
// If not set, it verifies that the address matches, and that the STK was issued within the last 24 hours.
|
|||
// This option is only valid for the server.
|
|||
AcceptSTK func(clientAddr net.Addr, stk *STK) bool |
|||
// MaxReceiveStreamFlowControlWindow is the maximum stream-level flow control window for receiving data.
|
|||
// If this value is zero, it will default to 1 MB for the server and 6 MB for the client.
|
|||
MaxReceiveStreamFlowControlWindow protocol.ByteCount |
|||
// MaxReceiveConnectionFlowControlWindow is the connection-level flow control window for receiving data.
|
|||
// If this value is zero, it will default to 1.5 MB for the server and 15 MB for the client.
|
|||
MaxReceiveConnectionFlowControlWindow protocol.ByteCount |
|||
// KeepAlive defines whether this peer will periodically send PING frames to keep the connection alive.
|
|||
KeepAlive bool |
|||
} |
|||
|
|||
// A Listener for incoming QUIC connections
|
|||
type Listener interface { |
|||
// Close the server, sending CONNECTION_CLOSE frames to each peer.
|
|||
Close() error |
|||
// Addr returns the local network addr that the server is listening on.
|
|||
Addr() net.Addr |
|||
// Accept returns new sessions. It should be called in a loop.
|
|||
Accept() (Session, error) |
|||
} |
|||
@ -0,0 +1,94 @@ |
|||
package utils |
|||
|
|||
import ( |
|||
"fmt" |
|||
"log" |
|||
"os" |
|||
"time" |
|||
) |
|||
|
|||
// LogLevel of quic-go
|
|||
type LogLevel uint8 |
|||
|
|||
const logEnv = "QUIC_GO_LOG_LEVEL" |
|||
|
|||
const ( |
|||
// LogLevelNothing disables
|
|||
LogLevelNothing LogLevel = iota |
|||
// LogLevelError enables err logs
|
|||
LogLevelError |
|||
// LogLevelInfo enables info logs (e.g. packets)
|
|||
LogLevelInfo |
|||
// LogLevelDebug enables debug logs (e.g. packet contents)
|
|||
LogLevelDebug |
|||
) |
|||
|
|||
var ( |
|||
logLevel = LogLevelNothing |
|||
timeFormat = "" |
|||
) |
|||
|
|||
// SetLogLevel sets the log level
|
|||
func SetLogLevel(level LogLevel) { |
|||
logLevel = level |
|||
} |
|||
|
|||
// SetLogTimeFormat sets the format of the timestamp
|
|||
// an empty string disables the logging of timestamps
|
|||
func SetLogTimeFormat(format string) { |
|||
log.SetFlags(0) // disable timestamp logging done by the log package
|
|||
timeFormat = format |
|||
} |
|||
|
|||
// Debugf logs something
|
|||
func Debugf(format string, args ...interface{}) { |
|||
if logLevel == LogLevelDebug { |
|||
logMessage(format, args...) |
|||
} |
|||
} |
|||
|
|||
// Infof logs something
|
|||
func Infof(format string, args ...interface{}) { |
|||
if logLevel >= LogLevelInfo { |
|||
logMessage(format, args...) |
|||
} |
|||
} |
|||
|
|||
// Errorf logs something
|
|||
func Errorf(format string, args ...interface{}) { |
|||
if logLevel >= LogLevelError { |
|||
logMessage(format, args...) |
|||
} |
|||
} |
|||
|
|||
func logMessage(format string, args ...interface{}) { |
|||
if len(timeFormat) > 0 { |
|||
log.Printf(time.Now().Format(timeFormat)+" "+format, args...) |
|||
} else { |
|||
log.Printf(format, args...) |
|||
} |
|||
} |
|||
|
|||
// Debug returns true if the log level is LogLevelDebug
|
|||
func Debug() bool { |
|||
return logLevel == LogLevelDebug |
|||
} |
|||
|
|||
func init() { |
|||
readLoggingEnv() |
|||
} |
|||
|
|||
func readLoggingEnv() { |
|||
switch os.Getenv(logEnv) { |
|||
case "": |
|||
return |
|||
case "DEBUG": |
|||
logLevel = LogLevelDebug |
|||
case "INFO": |
|||
logLevel = LogLevelInfo |
|||
case "ERROR": |
|||
logLevel = LogLevelError |
|||
default: |
|||
fmt.Fprintln(os.Stderr, "invalid quic-go log level, see https://github.com/lucas-clemente/quic-go/wiki/Logging") |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
package utils |
|||
|
|||
import "time" |
|||
|
|||
// A Timer wrapper that behaves correctly when resetting
|
|||
type Timer struct { |
|||
t *time.Timer |
|||
read bool |
|||
deadline time.Time |
|||
} |
|||
|
|||
// NewTimer creates a new timer that is not set
|
|||
func NewTimer() *Timer { |
|||
return &Timer{t: time.NewTimer(0)} |
|||
} |
|||
|
|||
// Chan returns the channel of the wrapped timer
|
|||
func (t *Timer) Chan() <-chan time.Time { |
|||
return t.t.C |
|||
} |
|||
|
|||
// Reset the timer, no matter whether the value was read or not
|
|||
func (t *Timer) Reset(deadline time.Time) { |
|||
if deadline.Equal(t.deadline) { |
|||
// No need to reset the timer
|
|||
return |
|||
} |
|||
|
|||
// We need to drain the timer if the value from its channel was not read yet.
|
|||
// See https://groups.google.com/forum/#!topic/golang-dev/c9UUfASVPoU
|
|||
if !t.t.Stop() && !t.read { |
|||
<-t.t.C |
|||
} |
|||
t.t.Reset(deadline.Sub(time.Now())) |
|||
|
|||
t.read = false |
|||
t.deadline = deadline |
|||
} |
|||
|
|||
// SetRead should be called after the value from the chan was read
|
|||
func (t *Timer) SetRead() { |
|||
t.read = true |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue