| // Copyright 2014 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | package runner | 
 |  | 
 | import ( | 
 | 	"encoding/binary" | 
 | 	"fmt" | 
 | 	"io" | 
 | 	"net" | 
 | 	"time" | 
 | ) | 
 |  | 
 | // opcodePacket signals a packet, encoded with a 32-bit length prefix, followed | 
 | // by the payload. | 
 | const opcodePacket = byte('P') | 
 |  | 
 | // opcodeTimeout signals a read timeout, encoded by a 64-bit number of | 
 | // nanoseconds. On receipt, the peer should reply with | 
 | // opcodeTimeoutAck. opcodeTimeout may only be sent by the Go side. | 
 | const opcodeTimeout = byte('T') | 
 |  | 
 | // opcodeTimeoutAck acknowledges a read timeout. This opcode has no payload and | 
 | // may only be sent by the C side. Timeout ACKs act as a synchronization point | 
 | // at the timeout, to bracket one flight of messages from C. | 
 | const opcodeTimeoutAck = byte('t') | 
 |  | 
 | type packetAdaptor struct { | 
 | 	net.Conn | 
 | 	debug *recordingConn | 
 | } | 
 |  | 
 | // newPacketAdaptor wraps a reliable streaming net.Conn into a reliable | 
 | // packet-based net.Conn. The stream contains packets and control commands, | 
 | // distinguished by a one byte opcode. | 
 | func newPacketAdaptor(conn net.Conn) *packetAdaptor { | 
 | 	return &packetAdaptor{conn, nil} | 
 | } | 
 |  | 
 | func (p *packetAdaptor) log(message string, data []byte) { | 
 | 	if p.debug == nil { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	p.debug.LogSpecial(message, data) | 
 | } | 
 |  | 
 | func (p *packetAdaptor) readOpcode() (byte, error) { | 
 | 	out := make([]byte, 1) | 
 | 	if _, err := io.ReadFull(p.Conn, out); err != nil { | 
 | 		return 0, err | 
 | 	} | 
 | 	return out[0], nil | 
 | } | 
 |  | 
 | func (p *packetAdaptor) readPacketBody() ([]byte, error) { | 
 | 	var length uint32 | 
 | 	if err := binary.Read(p.Conn, binary.BigEndian, &length); err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	out := make([]byte, length) | 
 | 	if _, err := io.ReadFull(p.Conn, out); err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	return out, nil | 
 | } | 
 |  | 
 | func (p *packetAdaptor) Read(b []byte) (int, error) { | 
 | 	opcode, err := p.readOpcode() | 
 | 	if err != nil { | 
 | 		return 0, err | 
 | 	} | 
 | 	if opcode != opcodePacket { | 
 | 		return 0, fmt.Errorf("unexpected opcode '%d'", opcode) | 
 | 	} | 
 | 	out, err := p.readPacketBody() | 
 | 	if err != nil { | 
 | 		return 0, err | 
 | 	} | 
 | 	return copy(b, out), nil | 
 | } | 
 |  | 
 | func (p *packetAdaptor) Write(b []byte) (int, error) { | 
 | 	payload := make([]byte, 1+4+len(b)) | 
 | 	payload[0] = opcodePacket | 
 | 	binary.BigEndian.PutUint32(payload[1:5], uint32(len(b))) | 
 | 	copy(payload[5:], b) | 
 | 	if _, err := p.Conn.Write(payload); err != nil { | 
 | 		return 0, err | 
 | 	} | 
 | 	return len(b), nil | 
 | } | 
 |  | 
 | // SendReadTimeout instructs the peer to simulate a read timeout. It then waits | 
 | // for acknowledgement of the timeout, buffering any packets received since | 
 | // then. The packets are then returned. | 
 | func (p *packetAdaptor) SendReadTimeout(d time.Duration) ([][]byte, error) { | 
 | 	p.log("Simulating read timeout: "+d.String(), nil) | 
 |  | 
 | 	payload := make([]byte, 1+8) | 
 | 	payload[0] = opcodeTimeout | 
 | 	binary.BigEndian.PutUint64(payload[1:], uint64(d.Nanoseconds())) | 
 | 	if _, err := p.Conn.Write(payload); err != nil { | 
 | 		return nil, err | 
 | 	} | 
 |  | 
 | 	var packets [][]byte | 
 | 	for { | 
 | 		opcode, err := p.readOpcode() | 
 | 		if err != nil { | 
 | 			return nil, err | 
 | 		} | 
 | 		switch opcode { | 
 | 		case opcodeTimeoutAck: | 
 | 			p.log("Received timeout ACK", nil) | 
 | 			// Done! Return the packets buffered and continue. | 
 | 			return packets, nil | 
 | 		case opcodePacket: | 
 | 			// Buffer the packet for the caller to process. | 
 | 			packet, err := p.readPacketBody() | 
 | 			if err != nil { | 
 | 				return nil, err | 
 | 			} | 
 | 			p.log("Simulating dropped packet", packet) | 
 | 			packets = append(packets, packet) | 
 | 		default: | 
 | 			return nil, fmt.Errorf("unexpected opcode '%d'", opcode) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | type replayAdaptor struct { | 
 | 	net.Conn | 
 | 	prevWrite []byte | 
 | } | 
 |  | 
 | // newReplayAdaptor wraps a packeted net.Conn. It transforms it into | 
 | // one which, after writing a packet, always replays the previous | 
 | // write. | 
 | func newReplayAdaptor(conn net.Conn) net.Conn { | 
 | 	return &replayAdaptor{Conn: conn} | 
 | } | 
 |  | 
 | func (r *replayAdaptor) Write(b []byte) (int, error) { | 
 | 	n, err := r.Conn.Write(b) | 
 |  | 
 | 	// Replay the previous packet and save the current one to | 
 | 	// replay next. | 
 | 	if r.prevWrite != nil { | 
 | 		r.Conn.Write(r.prevWrite) | 
 | 	} | 
 | 	r.prevWrite = append(r.prevWrite[:0], b...) | 
 |  | 
 | 	return n, err | 
 | } | 
 |  | 
 | type damageAdaptor struct { | 
 | 	net.Conn | 
 | 	damage bool | 
 | } | 
 |  | 
 | // newDamageAdaptor wraps a packeted net.Conn. It transforms it into one which | 
 | // optionally damages the final byte of every Write() call. | 
 | func newDamageAdaptor(conn net.Conn) *damageAdaptor { | 
 | 	return &damageAdaptor{Conn: conn} | 
 | } | 
 |  | 
 | func (d *damageAdaptor) setDamage(damage bool) { | 
 | 	d.damage = damage | 
 | } | 
 |  | 
 | func (d *damageAdaptor) Write(b []byte) (int, error) { | 
 | 	if d.damage && len(b) > 0 { | 
 | 		b = append([]byte{}, b...) | 
 | 		b[len(b)-1]++ | 
 | 	} | 
 | 	return d.Conn.Write(b) | 
 | } |