func (s *Session) die(errorCode frame.ErrorCode, err error) error {
	if !atomic.CompareAndSwapInt32(&s.diebit, 0, 1) {
		return fmt.Errorf("Shutdown already in progress")
	}
	s.GoAway(errorCode, []byte(err.Error()))
	close(s.accept)
	s.transport.Close()
	s.streams.Each(func(id frame.StreamId, str stream) {
		str.closeWith(fmt.Errorf("Session closed"))
	})
	s.dead <- deadReason{errorCode, err, s.remoteDebug}
	return nil
}
die closes the session cleanly with the given error and protocol error code
package proto
import (
	"fmt"
	"github.com/socketplane/socketplane/Godeps/_workspace/src/github.com/inconshreveable/muxado/proto/frame"
	"io"
	"net"
	"reflect"
	"sync"
	"sync/atomic"
	"time"
)
const (
	defaultWindowSize       = 0x10000  
	defaultAcceptQueueDepth = 100
	MinExtensionType        = 0xFFFFFFFF - 0x100  
)
type stream interface {
	IStream
	handleStreamData(*frame.RStreamData)
	handleStreamWndInc(*frame.RStreamWndInc)
	handleStreamRst(*frame.RStreamRst)
	closeWith(error)
}
type ExtAccept func() (IStream, error)
type Extension interface {
	Start(ISession, ExtAccept) frame.StreamType
}
type deadReason struct {
	errorCode   frame.ErrorCode
	err         error
	remoteDebug []byte
}
type streamFactory func(id frame.StreamId, priority frame.StreamPriority, streamType frame.StreamType, finLocal bool, finRemote bool, windowSize uint32, sess session) stream
type parityFn func(frame.StreamId) bool
type halfState struct {
	goneAway int32   
	lastId   uint32  
}
type Session struct {
	conn              net.Conn                          
	transport         frame.Transport                   
	streams           StreamMap                         
	local             halfState                         
	remote            halfState                         
	syn               *frame.WStreamSyn                 
	wr                sync.Mutex                        
	accept            chan stream                       
	diebit            int32                             
	remoteDebug       []byte                            
	defaultWindowSize uint32                            
	newStream         streamFactory                     
	dead              chan deadReason                   
	isLocal           parityFn                          
	exts              map[frame.StreamType]chan stream  
}
func NewSession(conn net.Conn, newStream streamFactory, isClient bool, exts []Extension) ISession {
	sess := &Session{
		conn:              conn,
		transport:         frame.NewBasicTransport(conn),
		streams:           NewConcurrentStreamMap(),
		local:             halfState{lastId: 0},
		remote:            halfState{lastId: 0},
		syn:               frame.NewWStreamSyn(),
		diebit:            0,
		defaultWindowSize: defaultWindowSize,
		accept:            make(chan stream, defaultAcceptQueueDepth),
		newStream:         newStream,
		dead:              make(chan deadReason, 1),  
		exts:              make(map[frame.StreamType]chan stream),
	}
	if isClient {
		sess.isLocal = sess.isClient
		sess.local.lastId += 1
	} else {
		sess.isLocal = sess.isServer
		sess.remote.lastId += 1
	}
	for _, ext := range exts {
		sess.startExtension(ext)
	}
	go sess.reader()
	return sess
}
func (s *Session) Open() (IStream, error) {
	return s.OpenStream(0, 0, false)
}
func (s *Session) OpenStream(priority frame.StreamPriority, streamType frame.StreamType, fin bool) (ret IStream, err error) {
	if atomic.LoadInt32(&s.remote.goneAway) == 1 {
		return nil, fmt.Errorf("Failed to create stream, remote has gone away.")
	}
	s.wr.Lock()
	nextId := frame.StreamId(atomic.AddUint32(&s.local.lastId, 2))
	str := s.newStream(nextId, priority, streamType, fin, false, s.defaultWindowSize, s)
	s.streams.Set(nextId, str)
	if err = s.syn.Set(nextId, priority, streamType, fin); err != nil {
		s.wr.Unlock()
		s.die(frame.InternalError, err)
		return
	}
	if err = s.transport.WriteFrame(s.syn); err != nil {
		s.wr.Unlock()
		s.die(frame.InternalError, err)
		return
	}
	s.wr.Unlock()
	return str, nil
}
func (s *Session) Accept() (str IStream, err error) {
	var ok bool
	if str, ok = <-s.accept; !ok {
		return nil, fmt.Errorf("Session closed")
	}
	return
}
func (s *Session) Kill() error {
	return s.transport.Close()
}
func (s *Session) Close() error {
	return s.die(frame.NoError, fmt.Errorf("Session Close()"))
}
func (s *Session) GoAway(errorCode frame.ErrorCode, debug []byte) (err error) {
	if !atomic.CompareAndSwapInt32(&s.local.goneAway, 0, 1) {
		return fmt.Errorf("Already sent GoAway!")
	}
	s.wr.Lock()
	f := frame.NewWGoAway()
	remoteId := frame.StreamId(atomic.LoadUint32(&s.remote.lastId))
	if err = f.Set(remoteId, errorCode, debug); err != nil {
		s.wr.Unlock()
		s.die(frame.InternalError, err)
		return
	}
	if err = s.transport.WriteFrame(f); err != nil {
		s.wr.Unlock()
		s.die(frame.InternalError, err)
		return
	}
	s.wr.Unlock()
	return
}
func (s *Session) LocalAddr() net.Addr {
	return s.conn.LocalAddr()
}
func (s *Session) RemoteAddr() net.Addr {
	return s.conn.RemoteAddr()
}
func (s *Session) Wait() (frame.ErrorCode, error, []byte) {
	reason := <-s.dead
	return reason.errorCode, reason.err, reason.remoteDebug
}
func (s *Session) removeStream(id frame.StreamId) {
	s.streams.Delete(id)
	return
}
func (s *Session) writeFrame(f frame.WFrame, dl time.Time) (err error) {
	s.wr.Lock()
	s.conn.SetWriteDeadline(dl)
	err = s.transport.WriteFrame(f)
	s.wr.Unlock()
	return
}