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 |
# 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