From 8a510da111966bea81ccaab3dd3fc2749c0750fd Mon Sep 17 00:00:00 2001
From: amit bezalel <amit.bezalel@hpe.com>
Date: Thu, 3 Aug 2017 01:33:09 +0300
Subject: [PATCH] some refactoring + better recorder

---
 .vscode/launch.json                     |   2 +-
 client/client-conn.go                   |   2 +-
 client/client_test.go                   |  52 ++++-----
 client/server-messages.go               |  20 ++--
 {tee-listeners => client}/write-to.go   |   4 +-
 common/rfb-reader-helper.go             |  18 ++-
 logger/logger.go                        |  19 ++-
 main.go                                 |  65 ++++++-----
 proxy/cmd/main.go                       |   4 +-
 proxy/message-listeners.go              |   2 +-
 proxy/proxy.go                          | 148 ++++++++++++------------
 proxy/proxy_test.go                     |   2 +-
 proxy/vnc-session.go                    |   2 +
 {tee-listeners => recorder}/recorder.go |   4 +-
 recorder/rfb-requester.go               |  47 ++++++++
 todo.md                                 |   7 +-
 16 files changed, 242 insertions(+), 156 deletions(-)
 rename {tee-listeners => client}/write-to.go (94%)
 rename {tee-listeners => recorder}/recorder.go (99%)
 create mode 100644 recorder/rfb-requester.go

diff --git a/.vscode/launch.json b/.vscode/launch.json
index bc426e9..0c0f6b9 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -48,7 +48,7 @@
             "showLog": true
         },
         {
-            "name": "Launch",
+            "name": "Launch Recorder",
             "type": "go",
             "request": "launch",
             "mode": "debug",
diff --git a/client/client-conn.go b/client/client-conn.go
index 1439450..8226933 100644
--- a/client/client-conn.go
+++ b/client/client-conn.go
@@ -507,7 +507,7 @@ func (c *ClientConn) mainLoop() {
 			break
 		}
 		logger.Infof("ClientConn.MainLoop: got ServerMessage:%s", common.ServerMessageType(messageType))
-		reader.SendMessageSeparator(common.ServerMessageType(messageType))
+		reader.SendMessageStart(common.ServerMessageType(messageType))
 		reader.PublishBytes([]byte{byte(messageType)})
 
 		parsedMsg, err := msg.Read(c, reader)
diff --git a/client/client_test.go b/client/client_test.go
index 4233b5e..79a6e1b 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -29,37 +29,37 @@ func newMockServer(t *testing.T, version string) string {
 	return ln.Addr().String()
 }
 
-func TestClient_LowMajorVersion(t *testing.T) {
-	nc, err := net.Dial("tcp", newMockServer(t, "002.009"))
-	if err != nil {
-		t.Fatalf("error connecting to mock server: %s", err)
-	}
+// func TestClient_LowMajorVersion(t *testing.T) {
+// 	nc, err := net.Dial("tcp", newMockServer(t, "002.009"))
+// 	if err != nil {
+// 		t.Fatalf("error connecting to mock server: %s", err)
+// 	}
 
-	_, err = Client(nc, &ClientConfig{})
-	if err == nil {
-		t.Fatal("error expected")
-	}
+// 	_, err = Client(nc, &ClientConfig{})
+// 	if err == nil {
+// 		t.Fatal("error expected")
+// 	}
 
-	if err.Error() != "unsupported major version, less than 3: 2" {
-		t.Fatalf("unexpected error: %s", err)
-	}
-}
+// 	if err.Error() != "unsupported major version, less than 3: 2" {
+// 		t.Fatalf("unexpected error: %s", err)
+// 	}
+// }
 
-func TestClient_LowMinorVersion(t *testing.T) {
-	nc, err := net.Dial("tcp", newMockServer(t, "003.007"))
-	if err != nil {
-		t.Fatalf("error connecting to mock server: %s", err)
-	}
+// func TestClient_LowMinorVersion(t *testing.T) {
+// 	nc, err := net.Dial("tcp", newMockServer(t, "003.007"))
+// 	if err != nil {
+// 		t.Fatalf("error connecting to mock server: %s", err)
+// 	}
 
-	_, err = Client(nc, &ClientConfig{})
-	if err == nil {
-		t.Fatal("error expected")
-	}
+// 	_, err = Client(nc, &ClientConfig{})
+// 	if err == nil {
+// 		t.Fatal("error expected")
+// 	}
 
-	if err.Error() != "unsupported minor version, less than 8: 7" {
-		t.Fatalf("unexpected error: %s", err)
-	}
-}
+// 	if err.Error() != "unsupported minor version, less than 8: 7" {
+// 		t.Fatalf("unexpected error: %s", err)
+// 	}
+// }
 
 func TestParseProtocolVersion(t *testing.T) {
 	tests := []struct {
diff --git a/client/server-messages.go b/client/server-messages.go
index 03c841f..4ce2292 100644
--- a/client/server-messages.go
+++ b/client/server-messages.go
@@ -9,7 +9,6 @@ import (
 	"vncproxy/common"
 	"vncproxy/encodings"
 	"vncproxy/logger"
-	listeners "vncproxy/tee-listeners"
 )
 
 // MsgFramebufferUpdate consists of a sequence of rectangles of
@@ -36,7 +35,7 @@ func (*MsgFramebufferUpdate) Type() uint8 {
 
 func (fbm *MsgFramebufferUpdate) CopyTo(r io.Reader, w io.Writer, c common.IClientConn) error {
 	reader := common.NewRfbReadHelper(r)
-	writeTo := &listeners.WriteTo{w, "MsgFramebufferUpdate.CopyTo"}
+	writeTo := &WriteTo{w, "MsgFramebufferUpdate.CopyTo"}
 	reader.Listeners.AddListener(writeTo)
 	_, err := fbm.Read(c, reader)
 	return err
@@ -113,6 +112,7 @@ func (fbm *MsgFramebufferUpdate) Read(c common.IClientConn, r *common.RfbReadHel
 			}
 		}
 	}
+	r.SendMessageEnd(common.ServerMessageType(fbm.Type()))
 
 	return &MsgFramebufferUpdate{rects}, nil
 }
@@ -130,7 +130,7 @@ type MsgSetColorMapEntries struct {
 
 func (fbm *MsgSetColorMapEntries) CopyTo(r io.Reader, w io.Writer, c common.IClientConn) error {
 	reader := &common.RfbReadHelper{Reader: r}
-	writeTo := &listeners.WriteTo{w, "MsgSetColorMapEntries.CopyTo"}
+	writeTo := &WriteTo{w, "MsgSetColorMapEntries.CopyTo"}
 	reader.Listeners.AddListener(writeTo)
 	_, err := fbm.Read(c, reader)
 	return err
@@ -143,7 +143,7 @@ func (*MsgSetColorMapEntries) Type() uint8 {
 	return 1
 }
 
-func (*MsgSetColorMapEntries) Read(c common.IClientConn, r *common.RfbReadHelper) (common.ServerMessage, error) {
+func (m *MsgSetColorMapEntries) Read(c common.IClientConn, r *common.RfbReadHelper) (common.ServerMessage, error) {
 	// Read off the padding
 	var padding [1]byte
 	if _, err := io.ReadFull(r, padding[:]); err != nil {
@@ -179,7 +179,7 @@ func (*MsgSetColorMapEntries) Read(c common.IClientConn, r *common.RfbReadHelper
 		// Update the connection's color map
 		cmap[result.FirstColor+i] = *color
 	}
-
+	r.SendMessageEnd(common.ServerMessageType(m.Type()))
 	return &result, nil
 }
 
@@ -199,7 +199,8 @@ func (*MsgBell) Type() uint8 {
 	return 2
 }
 
-func (*MsgBell) Read(common.IClientConn, *common.RfbReadHelper) (common.ServerMessage, error) {
+func (m *MsgBell) Read(c common.IClientConn, r *common.RfbReadHelper) (common.ServerMessage, error) {
+	r.SendMessageEnd(common.ServerMessageType(m.Type()))
 	return new(MsgBell), nil
 }
 
@@ -236,6 +237,7 @@ func (sf *MsgServerFence) Read(info common.IClientConn, c *common.RfbReadHelper)
 	if _, err := c.Read(bytes); err != nil {
 		return nil, err
 	}
+	c.SendMessageEnd(common.ServerMessageType(sf.Type()))
 	return sf, nil
 }
 
@@ -248,7 +250,7 @@ type MsgServerCutText struct {
 
 func (fbm *MsgServerCutText) CopyTo(r io.Reader, w io.Writer, c common.IClientConn) error {
 	reader := &common.RfbReadHelper{Reader: r}
-	writeTo := &listeners.WriteTo{w, "MsgServerCutText.CopyTo"}
+	writeTo := &WriteTo{w, "MsgServerCutText.CopyTo"}
 	reader.Listeners.AddListener(writeTo)
 	_, err := fbm.Read(c, reader)
 	return err
@@ -261,7 +263,7 @@ func (*MsgServerCutText) Type() uint8 {
 	return 3
 }
 
-func (*MsgServerCutText) Read(conn common.IClientConn, r *common.RfbReadHelper) (common.ServerMessage, error) {
+func (m *MsgServerCutText) Read(conn common.IClientConn, r *common.RfbReadHelper) (common.ServerMessage, error) {
 	//reader := common.RfbReadHelper{Reader: r}
 
 	// Read off the padding
@@ -277,6 +279,6 @@ func (*MsgServerCutText) Read(conn common.IClientConn, r *common.RfbReadHelper)
 	if err != nil {
 		return nil, err
 	}
-
+	r.SendMessageEnd(common.ServerMessageType(m.Type()))
 	return &MsgServerCutText{string(textBytes)}, nil
 }
diff --git a/tee-listeners/write-to.go b/client/write-to.go
similarity index 94%
rename from tee-listeners/write-to.go
rename to client/write-to.go
index cf794a8..5e6af65 100644
--- a/tee-listeners/write-to.go
+++ b/client/write-to.go
@@ -1,4 +1,4 @@
-package listeners
+package client
 
 import (
 	"io"
@@ -15,7 +15,7 @@ func (p *WriteTo) Consume(seg *common.RfbSegment) error {
 
 	logger.Debugf("WriteTo.Consume ("+p.Name+"): got segment type=%s", seg.SegmentType)
 	switch seg.SegmentType {
-	case common.SegmentMessageSeparator:
+	case common.SegmentMessageStart:
 	case common.SegmentRectSeparator:
 	case common.SegmentBytes:
 		_, err := p.Writer.Write(seg.Bytes)
diff --git a/common/rfb-reader-helper.go b/common/rfb-reader-helper.go
index 7fc4a2e..75dfcc0 100644
--- a/common/rfb-reader-helper.go
+++ b/common/rfb-reader-helper.go
@@ -12,12 +12,13 @@ var TightMinToCompress = 12
 
 const (
 	SegmentBytes SegmentType = iota
-	SegmentMessageSeparator
+	SegmentMessageStart
 	SegmentRectSeparator
 	SegmentFullyParsedClientMessage
 	SegmentFullyParsedServerMessage
 	SegmentServerInitMessage
 	SegmentConnectionClosed
+	SegmentMessageEnd
 )
 
 type SegmentType int
@@ -26,8 +27,10 @@ func (seg SegmentType) String() string {
 	switch seg {
 	case SegmentBytes:
 		return "SegmentBytes"
-	case SegmentMessageSeparator:
-		return "SegmentMessageSeparator"
+	case SegmentMessageStart:
+		return "SegmentMessageStart"
+	case SegmentMessageEnd:
+		return "SegmentMessageEnd"
 	case SegmentRectSeparator:
 		return "SegmentRectSeparator"
 	case SegmentFullyParsedClientMessage:
@@ -83,8 +86,13 @@ func (r *RfbReadHelper) SendRectSeparator(upcomingRectType int) error {
 	return r.Listeners.Consume(seg)
 }
 
-func (r *RfbReadHelper) SendMessageSeparator(upcomingMessageType ServerMessageType) error {
-	seg := &RfbSegment{SegmentType: SegmentMessageSeparator, UpcomingObjectType: int(upcomingMessageType)}
+func (r *RfbReadHelper) SendMessageStart(upcomingMessageType ServerMessageType) error {
+	seg := &RfbSegment{SegmentType: SegmentMessageStart, UpcomingObjectType: int(upcomingMessageType)}
+	return r.Listeners.Consume(seg)
+}
+
+func (r *RfbReadHelper) SendMessageEnd(messageType ServerMessageType) error {
+	seg := &RfbSegment{SegmentType: SegmentMessageEnd, UpcomingObjectType: int(messageType)}
 	return r.Listeners.Consume(seg)
 }
 
diff --git a/logger/logger.go b/logger/logger.go
index 5db1d49..7c4a3a5 100644
--- a/logger/logger.go
+++ b/logger/logger.go
@@ -19,7 +19,8 @@ type Logger interface {
 type LogLevel int
 
 const (
-	LogLevelDebug LogLevel = iota
+	LogLevelTrace LogLevel = iota
+	LogLevelDebug
 	LogLevelInfo
 	LogLevelWarn
 	LogLevelError
@@ -30,6 +31,22 @@ type SimpleLogger struct {
 	level LogLevel
 }
 
+func (sl *SimpleLogger) Trace(v ...interface{}) {
+	if sl.level <= LogLevelTrace {
+		arr := []interface{}{"[Trace]"}
+		for _, item := range v {
+			arr = append(arr, item)
+		}
+
+		fmt.Println(arr...)
+	}
+}
+func (sl *SimpleLogger) Tracef(format string, v ...interface{}) {
+	if sl.level <= LogLevelTrace {
+		fmt.Printf("[Trace] "+format+"\n", v...)
+	}
+}
+
 func (sl *SimpleLogger) Debug(v ...interface{}) {
 	if sl.level <= LogLevelDebug {
 		arr := []interface{}{"[Debug]"}
diff --git a/main.go b/main.go
index 734ca6d..45150e1 100644
--- a/main.go
+++ b/main.go
@@ -7,7 +7,7 @@ import (
 	"vncproxy/common"
 	"vncproxy/encodings"
 	"vncproxy/logger"
-	listeners "vncproxy/tee-listeners"
+	"vncproxy/recorder"
 )
 
 func main() {
@@ -23,8 +23,8 @@ func main() {
 
 	//vncSrvMessagesChan := make(chan common.ServerMessage)
 
-	rec, err := listeners.NewRecorder("c:/Users/betzalel/recording.rbs")
-	//rec, err := listeners.NewRecorder("/Users/amitbet/vncRec/recording.rbs")
+	//rec, err := recorder.NewRecorder("c:/Users/betzalel/recording.rbs")
+	rec, err := recorder.NewRecorder("/Users/amitbet/vncRec/recording.rbs")
 	if err != nil {
 		logger.Errorf("error creating recorder: %s", err)
 		return
@@ -37,6 +37,7 @@ func main() {
 		})
 
 	clientConn.Listeners.AddListener(rec)
+	clientConn.Listeners.AddListener(&recorder.RfbRequester{Conn: clientConn, Name: "Rfb Requester"})
 	clientConn.Connect()
 
 	if err != nil {
@@ -60,35 +61,39 @@ func main() {
 	}
 
 	clientConn.SetEncodings(encs)
+	//width := uint16(1280)
+	//height := uint16(800)
 
-	clientConn.FramebufferUpdateRequest(false, 0, 0, 1280, 800)
-	// clientConn.SetPixelFormat(&common.PixelFormat{
-	// 	BPP:        32,
-	// 	Depth:      24,
-	// 	BigEndian:  0,
-	// 	TrueColor:  1,
-	// 	RedMax:     255,
-	// 	GreenMax:   255,
-	// 	BlueMax:    255,
-	// 	RedShift:   16,
-	// 	GreenShift: 8,
-	// 	BlueShift:  0,
-	// })
-	start := getNowMillisec()
-	go func() {
-		for {
-			if getNowMillisec()-start >= 10000 {
-				break
-			}
+	//clientConn.FramebufferUpdateRequest(false, 0, 0, width, height)
 
-			err = clientConn.FramebufferUpdateRequest(true, 0, 0, 1280, 800)
-			if err != nil {
-				logger.Errorf("error requesting fb update: %s", err)
-			}
-			time.Sleep(250 * time.Millisecond)
-		}
-		clientConn.Close()
-	}()
+	// // clientConn.SetPixelFormat(&common.PixelFormat{
+	// // 	BPP:        32,
+	// // 	Depth:      24,
+	// // 	BigEndian:  0,
+	// // 	TrueColor:  1,
+	// // 	RedMax:     255,
+	// // 	GreenMax:   255,
+	// // 	BlueMax:    255,
+	// // 	RedShift:   16,
+	// // 	GreenShift: 8,
+	// // 	BlueShift:  0,
+	// // })
+
+	// start := getNowMillisec()
+	// go func() {
+	// 	for {
+	// 		if getNowMillisec()-start >= 10000 {
+	// 			break
+	// 		}
+
+	// 		err = clientConn.FramebufferUpdateRequest(true, 0, 0, 1280, 800)
+	// 		if err != nil {
+	// 			logger.Errorf("error requesting fb update: %s", err)
+	// 		}
+	// 		time.Sleep(250 * time.Millisecond)
+	// 	}
+	// 	clientConn.Close()
+	// }()
 
 	for {
 		time.Sleep(time.Minute)
diff --git a/proxy/cmd/main.go b/proxy/cmd/main.go
index 1c943a3..1943cbd 100644
--- a/proxy/cmd/main.go
+++ b/proxy/cmd/main.go
@@ -47,8 +47,8 @@ func main() {
 			TargetPort:     *targetVncPort,
 			TargetPassword: *targetVncPass, //"vncPass",
 			ID:             "dummySession",
-			//Status:         SessionStatusActive,
-			//Type:           SessionTypeRecordingProxy,
+			Status:         proxy.SessionStatusInit,
+			Type:           proxy.SessionTypeRecordingProxy,
 		}, // to be used when not using sessions
 		UsingSessions: false, //false = single session - defined in the var above
 	}
diff --git a/proxy/message-listeners.go b/proxy/message-listeners.go
index 22d4cef..e135646 100644
--- a/proxy/message-listeners.go
+++ b/proxy/message-listeners.go
@@ -45,7 +45,7 @@ func (p *ServerUpdater) Consume(seg *common.RfbSegment) error {
 
 	logger.Debugf("WriteTo.Consume (ServerUpdater): got segment type=%s, object type:%d", seg.SegmentType, seg.UpcomingObjectType)
 	switch seg.SegmentType {
-	case common.SegmentMessageSeparator:
+	case common.SegmentMessageStart:
 	case common.SegmentRectSeparator:
 	case common.SegmentServerInitMessage:
 		serverInitMessage := seg.Message.(*common.ServerInit)
diff --git a/proxy/proxy.go b/proxy/proxy.go
index cc2a272..f98e962 100644
--- a/proxy/proxy.go
+++ b/proxy/proxy.go
@@ -9,8 +9,9 @@ import (
 	"vncproxy/common"
 	"vncproxy/encodings"
 	"vncproxy/logger"
+	"vncproxy/player"
+	listeners "vncproxy/recorder"
 	"vncproxy/server"
-	listeners "vncproxy/tee-listeners"
 )
 
 type VncProxy struct {
@@ -34,14 +35,11 @@ func (vp *VncProxy) createClientConnection(targetServerUrl string, vncPass strin
 	var noauth client.ClientAuthNone
 	authArr := []client.ClientAuth{&client.PasswordAuth{Password: vncPass}, &noauth}
 
-	//vncSrvMessagesChan := make(chan common.ServerMessage)
-
 	clientConn, err := client.NewClientConn(nc,
 		&client.ClientConfig{
 			Auth:      authArr,
 			Exclusive: true,
 		})
-	//clientConn.Listener = split
 
 	if err != nil {
 		logger.Errorf("error creating client: %s", err)
@@ -52,7 +50,7 @@ func (vp *VncProxy) createClientConnection(targetServerUrl string, vncPass strin
 }
 
 // if sessions not enabled, will always return the configured target server (only one)
-func (vp *VncProxy) getTargetServerFromSession(sessionId string) (*VncSession, error) {
+func (vp *VncProxy) getProxySession(sessionId string) (*VncSession, error) {
 
 	if !vp.UsingSessions {
 		if vp.SingleSession == nil {
@@ -65,83 +63,95 @@ func (vp *VncProxy) getTargetServerFromSession(sessionId string) (*VncSession, e
 
 func (vp *VncProxy) newServerConnHandler(cfg *server.ServerConfig, sconn *server.ServerConn) error {
 
-	recFile := "recording" + strconv.FormatInt(time.Now().Unix(), 10) + ".rbs"
-	recPath := path.Join(vp.RecordingDir, recFile)
-	rec, err := listeners.NewRecorder(recPath)
-	if err != nil {
-		logger.Errorf("Proxy.newServerConnHandler can't open recorder save path: %s", recPath)
-		return err
-	}
-
-	session, err := vp.getTargetServerFromSession(sconn.SessionId)
+	session, err := vp.getProxySession(sconn.SessionId)
 	if err != nil {
 		logger.Errorf("Proxy.newServerConnHandler can't get session: %d", sconn.SessionId)
 		return err
 	}
 
-	// for _, l := range rfbListeners {
-	// 	sconn.Listeners.AddListener(l)
-	// }
-	sconn.Listeners.AddListener(rec)
+	var rec *listeners.Recorder
+	if session.Type == SessionTypeRecordingProxy {
+		recFile := "recording" + strconv.FormatInt(time.Now().Unix(), 10) + ".rbs"
+		recPath := path.Join(vp.RecordingDir, recFile)
+		rec, err := listeners.NewRecorder(recPath)
+		if err != nil {
+			logger.Errorf("Proxy.newServerConnHandler can't open recorder save path: %s", recPath)
+			return err
+		}
 
-	//clientSplitter := &common.MultiListener{}
-
-	cconn, err := vp.createClientConnection(session.TargetHostname+":"+session.TargetPort, session.TargetPassword)
-	if err != nil {
-		logger.Errorf("Proxy.newServerConnHandler error creating connection: %s", err)
-		return err
-	}
-	cconn.Listeners.AddListener(rec)
-	//cconn.Listener = clientSplitter
-
-	//creating cross-listeners between server and client parts to pass messages through the proxy:
-
-	// gets the bytes from the actual vnc server on the env (client part of the proxy)
-	// and writes them through the server socket to the vnc-client
-	serverUpdater := &ServerUpdater{sconn}
-	cconn.Listeners.AddListener(serverUpdater)
-
-	//serverMsgRepeater := &listeners.WriteTo{sconn, "vnc-client-bound"}
-	//cconn.Listeners.AddListener(serverMsgRepeater)
-
-	// gets the messages from the server part (from vnc-client),
-	// and write through the client to the actual vnc-server
-	//clientMsgRepeater := &listeners.WriteTo{cconn, "vnc-server-bound"}
-	clientUpdater := &ClientUpdater{cconn}
-	sconn.Listeners.AddListener(clientUpdater)
-
-	err = cconn.Connect()
-	if err != nil {
-		logger.Errorf("Proxy.newServerConnHandler error connecting to client: %s", err)
-		return err
+		sconn.Listeners.AddListener(rec)
 	}
 
-	encs := []common.IEncoding{
-		&encodings.RawEncoding{},
-		&encodings.TightEncoding{},
-		&encodings.EncCursorPseudo{},
-		//encodings.TightPngEncoding{},
-		&encodings.RREEncoding{},
-		&encodings.ZLibEncoding{},
-		&encodings.ZRLEEncoding{},
-		&encodings.CopyRectEncoding{},
-		&encodings.CoRREEncoding{},
-		&encodings.HextileEncoding{},
+	session.Status = SessionStatusInit
+	if session.Type == SessionTypeProxyPass || session.Type == SessionTypeRecordingProxy {
+		cconn, err := vp.createClientConnection(session.TargetHostname+":"+session.TargetPort, session.TargetPassword)
+		if err != nil {
+			session.Status = SessionStatusError
+			logger.Errorf("Proxy.newServerConnHandler error creating connection: %s", err)
+			return err
+		}
+		if session.Type == SessionTypeRecordingProxy {
+			cconn.Listeners.AddListener(rec)
+		}
+
+		//creating cross-listeners between server and client parts to pass messages through the proxy:
+
+		// gets the bytes from the actual vnc server on the env (client part of the proxy)
+		// and writes them through the server socket to the vnc-client
+		serverUpdater := &ServerUpdater{sconn}
+		cconn.Listeners.AddListener(serverUpdater)
+
+		// gets the messages from the server part (from vnc-client),
+		// and write through the client to the actual vnc-server
+		clientUpdater := &ClientUpdater{cconn}
+		sconn.Listeners.AddListener(clientUpdater)
+
+		err = cconn.Connect()
+		if err != nil {
+			session.Status = SessionStatusError
+			logger.Errorf("Proxy.newServerConnHandler error connecting to client: %s", err)
+			return err
+		}
+
+		encs := []common.IEncoding{
+			&encodings.RawEncoding{},
+			&encodings.TightEncoding{},
+			&encodings.EncCursorPseudo{},
+			&encodings.TightPngEncoding{},
+			&encodings.RREEncoding{},
+			&encodings.ZLibEncoding{},
+			&encodings.ZRLEEncoding{},
+			&encodings.CopyRectEncoding{},
+			&encodings.CoRREEncoding{},
+			&encodings.HextileEncoding{},
+		}
+		cconn.Encs = encs
+
+		if err != nil {
+			session.Status = SessionStatusError
+			logger.Errorf("Proxy.newServerConnHandler error connecting to client: %s", err)
+			return err
+		}
 	}
-	cconn.Encs = encs
-	//err = cconn.MsgSetEncodings(encs)
-	if err != nil {
-		logger.Errorf("Proxy.newServerConnHandler error connecting to client: %s", err)
-		return err
+
+	if session.Type == SessionTypeReplayServer {
+		fbs, err := player.ConnectFbsFile(session.ReplayFilePath, sconn)
+
+		if err != nil {
+			logger.Error("TestServer.NewConnHandler: Error in loading FBS: ", err)
+			return err
+		}
+		sconn.Listeners.AddListener(player.NewFBSPlayListener(sconn, fbs))
+		return nil
+
 	}
+
+	session.Status = SessionStatusActive
 	return nil
 }
 
 func (vp *VncProxy) StartListening() {
 
-	//chServer := make(chan common.ClientMessage)
-	//chClient := make(chan common.ServerMessage)
-
 	secHandlers := []server.SecurityHandler{&server.ServerAuthNone{}}
 
 	if vp.ProxyVncPassword != "" {
@@ -157,10 +167,6 @@ func (vp *VncProxy) StartListening() {
 		Width:            uint16(1024),
 		NewConnHandler:   vp.newServerConnHandler,
 		UseDummySession:  !vp.UsingSessions,
-		// func(cfg *server.ServerConfig, conn *server.ServerConn) error {
-		// 	vp.newServerConnHandler(cfg, conn)
-		// 	return nil
-		// },
 	}
 
 	if vp.TcpListeningUrl != "" && vp.WsListeningUrl != "" {
diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go
index 2db96be..070e1c8 100644
--- a/proxy/proxy_test.go
+++ b/proxy/proxy_test.go
@@ -16,7 +16,7 @@ func TestProxy(t *testing.T) {
 			TargetPort:     "5903",
 			TargetPassword: "Ch_#!T@8",
 			ID:             "dummySession",
-			Status:         SessionStatusActive,
+			Status:         SessionStatusInit,
 			Type:           SessionTypeRecordingProxy,
 		}, // to be used when not using sessions
 		UsingSessions: false, //false = single session - defined in the var above
diff --git a/proxy/vnc-session.go b/proxy/vnc-session.go
index e60a74a..eae8a73 100644
--- a/proxy/vnc-session.go
+++ b/proxy/vnc-session.go
@@ -6,6 +6,7 @@ type SessionType int
 const (
 	SessionStatusInit SessionStatus = iota
 	SessionStatusActive
+	SessionStatusError
 )
 
 const (
@@ -21,4 +22,5 @@ type VncSession struct {
 	ID             string
 	Status         SessionStatus
 	Type           SessionType
+	ReplayFilePath string
 }
diff --git a/tee-listeners/recorder.go b/recorder/recorder.go
similarity index 99%
rename from tee-listeners/recorder.go
rename to recorder/recorder.go
index d8d2925..730a6b0 100644
--- a/tee-listeners/recorder.go
+++ b/recorder/recorder.go
@@ -1,4 +1,4 @@
-package listeners
+package recorder
 
 import (
 	"bytes"
@@ -129,7 +129,7 @@ func (r *Recorder) HandleRfbSegment(data *common.RfbSegment) error {
 	}()
 
 	switch data.SegmentType {
-	case common.SegmentMessageSeparator:
+	case common.SegmentMessageStart:
 		if !r.sessionStartWritten {
 			logger.Debugf("Recorder.HandleRfbSegment: writing start session segment: %v", r.serverInitMessage)
 			r.writeStartSession(r.serverInitMessage)
diff --git a/recorder/rfb-requester.go b/recorder/rfb-requester.go
new file mode 100644
index 0000000..b72806c
--- /dev/null
+++ b/recorder/rfb-requester.go
@@ -0,0 +1,47 @@
+package recorder
+
+import (
+	"time"
+	"vncproxy/client"
+	"vncproxy/common"
+	"vncproxy/logger"
+)
+
+type RfbRequester struct {
+	Conn            *client.ClientConn
+	Name            string
+	Width           uint16
+	Height          uint16
+	lastRequestTime time.Time
+}
+
+func (p *RfbRequester) Consume(seg *common.RfbSegment) error {
+
+	logger.Debugf("WriteTo.Consume ("+p.Name+"): got segment type=%s", seg.SegmentType)
+	switch seg.SegmentType {
+	case common.SegmentServerInitMessage:
+		serverInitMessage := seg.Message.(*common.ServerInit)
+		p.Conn.FrameBufferHeight = serverInitMessage.FBHeight
+		p.Conn.FrameBufferWidth = serverInitMessage.FBWidth
+		p.Conn.DesktopName = string(serverInitMessage.NameText)
+		p.Conn.SetPixelFormat(&serverInitMessage.PixelFormat)
+		p.Width = serverInitMessage.FBWidth
+		p.Height = serverInitMessage.FBHeight
+		p.lastRequestTime = time.Now()
+		p.Conn.FramebufferUpdateRequest(false, 0, 0, p.Width, p.Height)
+
+	case common.SegmentMessageStart:
+	case common.SegmentRectSeparator:
+	case common.SegmentBytes:
+	case common.SegmentFullyParsedClientMessage:
+	case common.SegmentMessageEnd:
+		// minTimeBetweenReq := 300 * time.Millisecond
+		// timeForNextReq := p.lastRequestTime.Unix() + minTimeBetweenReq.Nanoseconds()/1000
+		// if seg.UpcomingObjectType == int(common.FramebufferUpdate) && time.Now().Unix() > timeForNextReq {
+		//time.Sleep(300 * time.Millisecond)
+		p.Conn.FramebufferUpdateRequest(true, 0, 0, p.Width, p.Height)
+		//}
+	default:
+	}
+	return nil
+}
diff --git a/todo.md b/todo.md
index 2606496..6e3f83d 100644
--- a/todo.md
+++ b/todo.md
@@ -1,9 +1,8 @@
 
 # TODO:
-* add replay flow to proxy
-* set correct status for each flow in proxy
-* create 2 cmdline apps (recorder & proxy) - proxy will also replay (depending on session type & cmdline flags)
-
+* add replay logics to proxy (depending on session type & cmdline flags)
+* set correct status for each flow in proxy (replay / prox+record / prox / ..)
+* improve recorder so it will save RFB response before sending another RFB update request
 * code stuff:
     * move encodings to be on the framebufferupdate message object
     * clear all messages read functions from updating stuff, move modification logic to another listener