@@ -223,6 +223,20 @@ func isValidReceivedCloseCode(code int) bool {
223
223
return validReceivedCloseCodes [code ] || (code >= 3000 && code <= 4999 )
224
224
}
225
225
226
+ // BufferPool represents a pool of buffers. The *sync.Pool type satisfies this
227
+ // interface. The type of the value stored in a pool is not specified.
228
+ type BufferPool interface {
229
+ // Get gets a value from the pool or returns nil if the pool is empty.
230
+ Get () interface {}
231
+ // Put adds a value to the pool.
232
+ Put (interface {})
233
+ }
234
+
235
+ // writePoolData is the type added to the write buffer pool. This wrapper is
236
+ // used to prevent applications from peeking at and depending on the values
237
+ // added to the pool.
238
+ type writePoolData struct { buf []byte }
239
+
226
240
// The Conn type represents a WebSocket connection.
227
241
type Conn struct {
228
242
conn net.Conn
@@ -232,6 +246,8 @@ type Conn struct {
232
246
// Write fields
233
247
mu chan bool // used as mutex to protect write to conn
234
248
writeBuf []byte // frame is constructed in this buffer.
249
+ writePool BufferPool
250
+ writeBufSize int
235
251
writeDeadline time.Time
236
252
writer io.WriteCloser // the current writer returned to the application
237
253
isWriting bool // for best-effort concurrent write detection
@@ -263,71 +279,38 @@ type Conn struct {
263
279
newDecompressionReader func (io.Reader ) io.ReadCloser
264
280
}
265
281
266
- func newConn (conn net.Conn , isServer bool , readBufferSize , writeBufferSize int ) * Conn {
267
- return newConnBRW (conn , isServer , readBufferSize , writeBufferSize , nil )
268
- }
269
-
270
- type writeHook struct {
271
- p []byte
272
- }
273
-
274
- func (wh * writeHook ) Write (p []byte ) (int , error ) {
275
- wh .p = p
276
- return len (p ), nil
277
- }
278
-
279
- func newConnBRW (conn net.Conn , isServer bool , readBufferSize , writeBufferSize int , brw * bufio.ReadWriter ) * Conn {
280
- mu := make (chan bool , 1 )
281
- mu <- true
282
+ func newConn (conn net.Conn , isServer bool , readBufferSize , writeBufferSize int , writeBufferPool BufferPool , br * bufio.Reader , writeBuf []byte ) * Conn {
282
283
283
- var br * bufio.Reader
284
- if readBufferSize == 0 && brw != nil && brw .Reader != nil {
285
- // Reuse the supplied bufio.Reader if the buffer has a useful size.
286
- // This code assumes that peek on a reader returns
287
- // bufio.Reader.buf[:0].
288
- brw .Reader .Reset (conn )
289
- if p , err := brw .Reader .Peek (0 ); err == nil && cap (p ) >= 256 {
290
- br = brw .Reader
291
- }
292
- }
293
284
if br == nil {
294
285
if readBufferSize == 0 {
295
286
readBufferSize = defaultReadBufferSize
296
- }
297
- if readBufferSize < maxControlFramePayloadSize {
287
+ } else if readBufferSize < maxControlFramePayloadSize {
288
+ // must be large enough for control frame
298
289
readBufferSize = maxControlFramePayloadSize
299
290
}
300
291
br = bufio .NewReaderSize (conn , readBufferSize )
301
292
}
302
293
303
- var writeBuf []byte
304
- if writeBufferSize == 0 && brw != nil && brw .Writer != nil {
305
- // Use the bufio.Writer's buffer if the buffer has a useful size. This
306
- // code assumes that bufio.Writer.buf[:1] is passed to the
307
- // bufio.Writer's underlying writer.
308
- var wh writeHook
309
- brw .Writer .Reset (& wh )
310
- brw .Writer .WriteByte (0 )
311
- brw .Flush ()
312
- if cap (wh .p ) >= maxFrameHeaderSize + 256 {
313
- writeBuf = wh .p [:cap (wh .p )]
314
- }
294
+ if writeBufferSize <= 0 {
295
+ writeBufferSize = defaultWriteBufferSize
315
296
}
297
+ writeBufferSize += maxFrameHeaderSize
316
298
317
- if writeBuf == nil {
318
- if writeBufferSize == 0 {
319
- writeBufferSize = defaultWriteBufferSize
320
- }
321
- writeBuf = make ([]byte , writeBufferSize + maxFrameHeaderSize )
299
+ if writeBuf == nil && writeBufferPool == nil {
300
+ writeBuf = make ([]byte , writeBufferSize )
322
301
}
323
302
303
+ mu := make (chan bool , 1 )
304
+ mu <- true
324
305
c := & Conn {
325
306
isServer : isServer ,
326
307
br : br ,
327
308
conn : conn ,
328
309
mu : mu ,
329
310
readFinal : true ,
330
311
writeBuf : writeBuf ,
312
+ writePool : writeBufferPool ,
313
+ writeBufSize : writeBufferSize ,
331
314
enableWriteCompression : true ,
332
315
compressionLevel : defaultCompressionLevel ,
333
316
}
@@ -484,7 +467,19 @@ func (c *Conn) prepWrite(messageType int) error {
484
467
c .writeErrMu .Lock ()
485
468
err := c .writeErr
486
469
c .writeErrMu .Unlock ()
487
- return err
470
+ if err != nil {
471
+ return err
472
+ }
473
+
474
+ if c .writeBuf == nil {
475
+ wpd , ok := c .writePool .Get ().(writePoolData )
476
+ if ok {
477
+ c .writeBuf = wpd .buf
478
+ } else {
479
+ c .writeBuf = make ([]byte , c .writeBufSize )
480
+ }
481
+ }
482
+ return nil
488
483
}
489
484
490
485
// NextWriter returns a writer for the next message to send. The writer's Close
@@ -610,6 +605,10 @@ func (w *messageWriter) flushFrame(final bool, extra []byte) error {
610
605
611
606
if final {
612
607
c .writer = nil
608
+ if c .writePool != nil {
609
+ c .writePool .Put (writePoolData {buf : c .writeBuf })
610
+ c .writeBuf = nil
611
+ }
613
612
return nil
614
613
}
615
614
0 commit comments