Skip to content

Commit c4bcea2

Browse files
authored
Merge pull request #68 from databus23/readerfrom-writerto
Implement io.ReaderFrom/WriterTo for Conn
2 parents fff0abf + ce59419 commit c4bcea2

File tree

2 files changed

+200
-0
lines changed

2 files changed

+200
-0
lines changed

protocol.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package proxyproto
22

33
import (
44
"bufio"
5+
"io"
56
"net"
67
"sync"
78
"time"
@@ -237,3 +238,20 @@ func (p *Conn) readHeader() error {
237238

238239
return err
239240
}
241+
242+
// ReadFrom implements the io.ReaderFrom ReadFrom method
243+
func (p *Conn) ReadFrom(r io.Reader) (int64, error) {
244+
if rf, ok := p.conn.(io.ReaderFrom); ok {
245+
return rf.ReadFrom(r)
246+
}
247+
return io.Copy(p.conn, r)
248+
}
249+
250+
// WriteTo implements io.WriterTo
251+
func (p *Conn) WriteTo(w io.Writer) (int64, error) {
252+
p.once.Do(func() { p.readErr = p.readHeader() })
253+
if p.readErr != nil {
254+
return 0, p.readErr
255+
}
256+
return p.bufReader.WriteTo(w)
257+
}

protocol_test.go

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
"crypto/tls"
1010
"crypto/x509"
1111
"fmt"
12+
"io"
13+
"io/ioutil"
1214
"net"
1315
"testing"
1416
)
@@ -741,6 +743,186 @@ func Test_MisconfiguredTLSServerRespondsWithUnderlyingError(t *testing.T) {
741743
}
742744
}
743745

746+
type testConn struct {
747+
readFromCalledWith io.Reader
748+
reads int
749+
net.Conn // nil; crash on any unexpected use
750+
}
751+
752+
func (c *testConn) ReadFrom(r io.Reader) (int64, error) {
753+
c.readFromCalledWith = r
754+
b, err := ioutil.ReadAll(r)
755+
return int64(len(b)), err
756+
}
757+
func (c *testConn) Write(p []byte) (int, error) {
758+
return len(p), nil
759+
}
760+
func (c *testConn) Read(p []byte) (int, error) {
761+
if c.reads == 0 {
762+
return 0, io.EOF
763+
}
764+
c.reads--
765+
return 1, nil
766+
}
767+
768+
func TestCopyToWrappedConnection(t *testing.T) {
769+
innerConn := &testConn{}
770+
wrappedConn := NewConn(innerConn)
771+
dummySrc := &testConn{reads: 1}
772+
773+
io.Copy(wrappedConn, dummySrc)
774+
if innerConn.readFromCalledWith != dummySrc {
775+
t.Error("Expected io.Copy to delegate to ReadFrom function of inner destination connection")
776+
}
777+
}
778+
779+
func TestCopyFromWrappedConnection(t *testing.T) {
780+
wrappedConn := NewConn(&testConn{reads: 1})
781+
dummyDst := &testConn{}
782+
783+
io.Copy(dummyDst, wrappedConn)
784+
if dummyDst.readFromCalledWith != wrappedConn.conn {
785+
t.Errorf("Expected io.Copy to pass inner source connection to ReadFrom method of destination")
786+
}
787+
}
788+
789+
func TestCopyFromWrappedConnectionToWrappedConnection(t *testing.T) {
790+
innerConn1 := &testConn{reads: 1}
791+
wrappedConn1 := NewConn(innerConn1)
792+
innerConn2 := &testConn{}
793+
wrappedConn2 := NewConn(innerConn2)
794+
795+
io.Copy(wrappedConn1, wrappedConn2)
796+
if innerConn1.readFromCalledWith != innerConn2 {
797+
t.Errorf("Expected io.Copy to pass inner source connection to ReadFrom of inner destination connection")
798+
}
799+
}
800+
801+
func benchmarkTCPProxy(size int, b *testing.B) {
802+
//create and start the echo backend
803+
backend, err := net.Listen("tcp", "127.0.0.1:0")
804+
if err != nil {
805+
b.Fatalf("err: %v", err)
806+
}
807+
defer backend.Close()
808+
go func() {
809+
for {
810+
conn, err := backend.Accept()
811+
if err != nil {
812+
break
813+
}
814+
_, err = io.Copy(conn, conn)
815+
conn.Close()
816+
if err != nil {
817+
b.Fatalf("Failed to read entire payload: %v", err)
818+
}
819+
}
820+
}()
821+
822+
//start the proxyprotocol enabled tcp proxy
823+
l, err := net.Listen("tcp", "127.0.0.1:0")
824+
if err != nil {
825+
b.Fatalf("err: %v", err)
826+
}
827+
defer l.Close()
828+
pl := &Listener{Listener: l}
829+
go func() {
830+
for {
831+
conn, err := pl.Accept()
832+
if err != nil {
833+
break
834+
}
835+
bConn, err := net.Dial("tcp", backend.Addr().String())
836+
if err != nil {
837+
b.Fatalf("failed to dial backend: %v", err)
838+
}
839+
go func() {
840+
_, err = io.Copy(bConn, conn)
841+
if err != nil {
842+
b.Fatalf("Failed to proxy incoming data to backend: %v", err)
843+
}
844+
bConn.(*net.TCPConn).CloseWrite()
845+
}()
846+
_, err = io.Copy(conn, bConn)
847+
if err != nil {
848+
b.Fatalf("Failed to proxy data from backend: %v", err)
849+
}
850+
conn.Close()
851+
bConn.Close()
852+
}
853+
}()
854+
855+
data := make([]byte, size)
856+
857+
header := &Header{
858+
Version: 2,
859+
Command: PROXY,
860+
TransportProtocol: TCPv4,
861+
SourceAddr: &net.TCPAddr{
862+
IP: net.ParseIP("10.1.1.1"),
863+
Port: 1000,
864+
},
865+
DestinationAddr: &net.TCPAddr{
866+
IP: net.ParseIP("20.2.2.2"),
867+
Port: 2000,
868+
},
869+
}
870+
871+
//now for the actual benchmark
872+
b.ResetTimer()
873+
for n := 0; n < b.N; n++ {
874+
conn, err := net.Dial("tcp", pl.Addr().String())
875+
if err != nil {
876+
b.Fatalf("err: %v", err)
877+
}
878+
// Write out the header!
879+
header.WriteTo(conn)
880+
//send data
881+
go func() {
882+
_, err = conn.Write(data)
883+
if err != nil {
884+
b.Fatalf("Failed to write data: %v", err)
885+
}
886+
conn.(*net.TCPConn).CloseWrite()
887+
888+
}()
889+
//receive data
890+
n, err := io.Copy(ioutil.Discard, conn)
891+
if n != int64(len(data)) {
892+
b.Fatalf("Expected to receive %d bytes, got %d", len(data), n)
893+
}
894+
if err != nil {
895+
b.Fatalf("Failed to read data: %v", err)
896+
}
897+
conn.Close()
898+
}
899+
}
900+
901+
func BenchmarkTCPProxy16KB(b *testing.B) {
902+
benchmarkTCPProxy(16*1024, b)
903+
}
904+
func BenchmarkTCPProxy32KB(b *testing.B) {
905+
benchmarkTCPProxy(32*1024, b)
906+
}
907+
func BenchmarkTCPProxy64KB(b *testing.B) {
908+
benchmarkTCPProxy(64*1024, b)
909+
}
910+
func BenchmarkTCPProxy128KB(b *testing.B) {
911+
benchmarkTCPProxy(128*1024, b)
912+
}
913+
func BenchmarkTCPProxy256KB(b *testing.B) {
914+
benchmarkTCPProxy(256*1024, b)
915+
}
916+
func BenchmarkTCPProxy512KB(b *testing.B) {
917+
benchmarkTCPProxy(512*1024, b)
918+
}
919+
func BenchmarkTCPProxy1024KB(b *testing.B) {
920+
benchmarkTCPProxy(1024*1024, b)
921+
}
922+
func BenchmarkTCPProxy2048KB(b *testing.B) {
923+
benchmarkTCPProxy(2048*1024, b)
924+
}
925+
744926
// copied from src/net/http/internal/testcert.go
745927

746928
// Copyright 2015 The Go Authors. All rights reserved.

0 commit comments

Comments
 (0)