code: func (c *Conn) Read(buffer []byte) (int, error) {
  	if _, connected := c.hState.(stateConnected); !connected {
  		// Clients can't call Read prior to handshake completion.
  		if c.isClient {
  			return 0, errors.New("Read called before the handshake completed")
  		}
  
  		// Neither can servers that don't allow early data.
  		if !c.config.AllowEarlyData {
  			return 0, errors.New("Read called before the handshake completed")
  		}
  
  		// If there's no early data, then return WouldBlock
  		if len(c.hsCtx.earlyData) == 0 {
  			return 0, AlertWouldBlock
  		}
  
  		return readPartial(&c.hsCtx.earlyData, buffer), nil
  	}
  
  	// The handshake is now connected.
  	logf(logTypeHandshake, "conn.Read with buffer = %d", len(buffer))
  	if alert := c.Handshake(); alert != AlertNoAlert {
  		return 0, alert
  	}
  
  	if len(buffer) == 0 {
  		return 0, nil
  	}
  
  	// Run our timers.
  	if c.config.UseDTLS {
  		if err := c.hsCtx.timers.check(time.Now()); err != nil {
  			return 0, AlertInternalError
  		}
  	}
  
  	// Lock the input channel
  	c.in.Lock()
  	defer c.in.Unlock()
  	for len(c.readBuffer) == 0 {
  		err := c.consumeRecord()
  
  		// err can be nil if consumeRecord processed a non app-data
  		// record.
  		if err != nil {
  			if c.config.NonBlocking || err != AlertWouldBlock {
  				logf(logTypeIO, "conn.Read returns err=%v", err)
  				return 0, err
  			}
  		}
  	}
  
  	return readPartial(&c.readBuffer, buffer), nil
  }
docs: // Read application data up to the size of buffer.  Handshake and alert records
  // are consumed by the Conn object directly.