@@ -27,8 +27,9 @@ import (
2727 "net"
2828
2929 core "google.golang.org/grpc/credentials/alts/internal"
30- "google.golang.org/grpc/internal/mem"
30+ imem "google.golang.org/grpc/internal/mem"
3131 "google.golang.org/grpc/internal/transport/readyreader"
32+ "google.golang.org/grpc/mem"
3233)
3334
3435// ALTSRecordCrypto is the interface for gRPC ALTS record protocol.
@@ -75,16 +76,19 @@ const (
7576
7677var (
7778 protocols = make (map [string ]ALTSRecordFunc )
78- writeBufPool * mem .BinaryTieredBufferPool
79+ writeBufPool * imem .BinaryTieredBufferPool
7980 // readBufPool pools buffers of at least `altsReadBufferInitialSize` size.
8081 // Since the read buffer size is slightly larger than 32KB, using a regular
8182 // BinaryTieredBufferPool results in allocating buffers of almost double the
8283 // required length.
83- readBufPool = mem .NewDirtySimplePool ()
84+ readBufPool = imem .NewDirtySimplePool ()
85+
86+ // Compile-time check to ensure conn implements ReadyReader.
87+ _ readyreader.Reader = & conn {}
8488)
8589
8690func init () {
87- pool , err := mem .NewDirtyBinaryTieredBufferPool (
91+ pool , err := imem .NewDirtyBinaryTieredBufferPool (
8892 8 ,
8993 12 , // Go page size, 4KB
9094 14 , // 16KB (max HTTP/2 frame size used by gRPC)
@@ -126,7 +130,8 @@ type conn struct {
126130 // nextFrame stores the next frame (in protected buffer) info.
127131 nextFrame []byte
128132 // overhead is the calculated overhead of each frame.
129- overhead int
133+ overhead int
134+ constPool constBufferPool // stored as a field to avoid heap allocations.
130135}
131136
132137// NewConn creates a new secure channel instance given the other party role and
@@ -163,21 +168,38 @@ func NewConn(c net.Conn, side core.Side, recordProtocol string, key []byte, prot
163168 return altsConn , nil
164169}
165170
171+ type constBufferPool struct {
172+ buffer []byte
173+ }
174+
175+ func (p * constBufferPool ) Get (int ) * []byte {
176+ return & p .buffer
177+ }
178+
179+ func (p * constBufferPool ) Put (* []byte ) {}
180+
166181// Read reads and decrypts a frame from the underlying connection, and copies the
167182// decrypted payload into b. If the size of the payload is greater than len(b),
168183// Read retains the remaining bytes in an internal buffer, and subsequent calls
169184// to Read will read from this buffer until it is exhausted.
170185func (p * conn ) Read (b []byte ) (n int , err error ) {
186+ p .constPool .buffer = b
187+ _ , n , err = p .ReadOnReady (len (b ), & p .constPool )
188+ return n , err
189+ }
190+
191+ func (p * conn ) ReadOnReady (bufSize int , pool mem.BufferPool ) (* []byte , int , error ) {
171192 if len (p .buf ) == 0 {
172193 var framedMsg []byte
173194 var protected []byte
174195 if p .protectedHandle != nil {
175196 protected = * p .protectedHandle
176197 protected = protected [:cap (protected )]
177198 }
199+ var err error
178200 framedMsg , p .nextFrame , err = ParseFramedMsg (p .nextFrame , altsRecordLengthLimit )
179201 if err != nil {
180- return 0 , err
202+ return nil , 0 , err
181203 }
182204 // Check whether the next frame to be decrypted has been
183205 // completely received yet.
@@ -217,40 +239,42 @@ func (p *conn) Read(b []byte) (n int, err error) {
217239 // Connection was idle, need to re-allocate the read buffer.
218240 newBuf , nRead , err := p .reader .ReadOnReady (altsReadBufferInitialSize , readBufPool )
219241 if err != nil {
220- return 0 , err
242+ return nil , 0 , err
221243 }
222244 p .protectedHandle = newBuf
223245 protected = (* newBuf )[:nRead ]
224246 } else {
225247 nRead , err := p .Conn .Read (protected [len (protected ):cap (protected )])
226248 if err != nil {
227- return 0 , err
249+ return nil , 0 , err
228250 }
229251 protected = protected [:len (protected )+ nRead ]
230252 }
231253 framedMsg , p .nextFrame , err = ParseFramedMsg (protected , altsRecordLengthLimit )
232254 if err != nil {
233- return 0 , err
255+ return nil , 0 , err
234256 }
235257 }
236258 // Now we have a complete frame, decrypted it.
237259 msg := framedMsg [MsgLenFieldSize :]
238260 msgType := binary .LittleEndian .Uint32 (msg [:msgTypeFieldSize ])
239261 if msgType & 0xff != altsRecordMsgType {
240- return 0 , fmt .Errorf ("received frame with incorrect message type %v, expected lower byte %v" ,
262+ return nil , 0 , fmt .Errorf ("received frame with incorrect message type %v, expected lower byte %v" ,
241263 msgType , altsRecordMsgType )
242264 }
243265 ciphertext := msg [msgTypeFieldSize :]
244266
245267 // Decrypt directly into the buffer, avoiding a copy from p.buf if
246268 // possible.
247- if len (b ) >= len (ciphertext ) {
248- dec , err := p .crypto .Decrypt (b [:0 ], ciphertext )
269+ if bufSize >= len (ciphertext ) {
270+ allocatedBuf := pool .Get (bufSize )
271+ dec , err := p .crypto .Decrypt ((* allocatedBuf )[:0 ], ciphertext )
249272 if err != nil {
250- return 0 , err
273+ pool .Put (allocatedBuf )
274+ return nil , 0 , err
251275 }
252276 p .dropProtectedIfEmtpy ()
253- return len (dec ), nil
277+ return allocatedBuf , len (dec ), nil
254278 }
255279 // Decrypt requires that if the dst and ciphertext alias, they
256280 // must alias exactly. Code here used to use msg[:0], but msg
@@ -261,14 +285,15 @@ func (p *conn) Read(b []byte) (n int, err error) {
261285 // check: https://golang.org/pkg/crypto/cipher/#AEAD.
262286 p .buf , err = p .crypto .Decrypt (ciphertext [:0 ], ciphertext )
263287 if err != nil {
264- return 0 , err
288+ return nil , 0 , err
265289 }
266290 }
267291
268- n = copy (b , p .buf )
292+ allocatedBuf := pool .Get (bufSize )
293+ n := copy (* allocatedBuf , p .buf )
269294 p .buf = p .buf [n :]
270295 p .dropProtectedIfEmtpy ()
271- return n , nil
296+ return allocatedBuf , n , nil
272297}
273298
274299func (p * conn ) dropProtectedIfEmtpy () {
0 commit comments