Simplified the API: There's only one constructor, and it does everything necessary to initiate the stream. StartSession() and Roster.Update() have both been eliminated.
// The lowest level of XMPP protocol, where TLS is applied after the// initial handshake.packagexmppimport("crypto/tls""io""net""time")varl1interval=time.Secondtypelayer1struct{socknet.ConnrecvSockschan<-net.ConnsendSockschannet.Conn}funcstartLayer1(socknet.Conn,recvWriterio.WriteCloser,sendReaderio.ReadCloser,status<-chanStatus)*layer1{l1:=layer1{sock:sock}recvSocks:=make(channet.Conn)l1.recvSocks=recvSockssendSocks:=make(channet.Conn,1)l1.sendSocks=sendSocksgorecvTransport(recvSocks,recvWriter,status)gosendTransport(sendSocks,sendReader)recvSocks<-socksendSocks<-sockreturn&l1}func(l1*layer1)startTls(conf*tls.Config){sendSockToSender:=func(socknet.Conn){for{select{case<-l1.sendSocks:casel1.sendSocks<-sock:return}}}sendSockToSender(nil)l1.recvSocks<-nill1.sock=tls.Client(l1.sock,conf)sendSockToSender(l1.sock)l1.recvSocks<-l1.sock}funcrecvTransport(socks<-channet.Conn,wio.WriteCloser,status<-chanStatus){deferw.Close()varsocknet.Connp:=make([]byte,1024)for{select{casestat:=<-status:ifstat==StatusShutdown{return}casesock=<-socks:default:}ifsock==nil{time.Sleep(l1interval)}else{sock.SetReadDeadline(time.Now().Add(l1interval))nr,err:=sock.Read(p)ifnr==0{iferrno,ok:=err.(*net.OpError);ok{iferrno.Timeout(){continue}}Warn.Logf("recvTransport: %s",err)return}nw,err:=w.Write(p[:nr])ifnw<nr{Warn.Logf("recvTransport: %s",err)return}}}}funcsendTransport(socks<-channet.Conn,rio.Reader){varsocknet.Connp:=make([]byte,1024)for{nr,err:=r.Read(p)ifnr==0{Warn.Logf("sendTransport: %s",err)break}fornr>0{select{casesock=<-socks:ifsock!=nil{defersock.Close()}default:}ifsock==nil{time.Sleep(l1interval)}else{nw,err:=sock.Write(p[:nr])nr-=nwifnr!=0{Warn.Logf("write: %s",err)break}}}}}