gopkg.in/rethinkdb/rethinkdb-go.v6@v6.2.2/cluster_test.go (about)

     1  package rethinkdb
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"encoding/json"
     7  	"fmt"
     8  	"github.com/stretchr/testify/mock"
     9  	test "gopkg.in/check.v1"
    10  	"gopkg.in/rethinkdb/rethinkdb-go.v6/encoding"
    11  	p "gopkg.in/rethinkdb/rethinkdb-go.v6/ql2"
    12  	"io"
    13  	"net"
    14  	"time"
    15  )
    16  
    17  type ClusterSuite struct{}
    18  
    19  var _ = test.Suite(&ClusterSuite{})
    20  
    21  func (s *ClusterSuite) TestCluster_NewSingle_NoDiscover_Ok(c *test.C) {
    22  	host1 := Host{Name: "host1", Port: 28015}
    23  	node1 := "node1"
    24  
    25  	conn1 := &connMock{}
    26  	expectServerQuery(conn1, 1, node1)
    27  	conn1.onCloseReturn(nil)
    28  	conn2 := &connMock{}
    29  	conn2.onCloseReturn(nil)
    30  
    31  	dialMock := &mockDial{}
    32  	dialMock.On("Dial", host1.String()).Return(conn1, nil).Once()
    33  	dialMock.On("Dial", host1.String()).Return(conn2, nil).Once()
    34  
    35  	opts := &ConnectOpts{}
    36  	seeds := []Host{host1}
    37  	cluster := &Cluster{
    38  		hp:          newHostPool(opts),
    39  		seeds:       seeds,
    40  		opts:        opts,
    41  		closed:      clusterWorking,
    42  		connFactory: mockedConnectionFactory(dialMock),
    43  	}
    44  
    45  	err := cluster.run()
    46  	c.Assert(err, test.IsNil)
    47  	conn1.waitDial()
    48  	conn2.waitDial()
    49  	err = cluster.Close()
    50  	c.Assert(err, test.IsNil)
    51  	conn1.waitDone()
    52  	conn2.waitDone()
    53  	mock.AssertExpectationsForObjects(c, dialMock, conn1, conn2)
    54  }
    55  
    56  func (s *ClusterSuite) TestCluster_NewMultiple_NoDiscover_Ok(c *test.C) {
    57  	host1 := Host{Name: "host1", Port: 28015}
    58  	host2 := Host{Name: "host2", Port: 28015}
    59  	node1 := "node1"
    60  	node2 := "node2"
    61  
    62  	conn1 := &connMock{}
    63  	expectServerQuery(conn1, 1, node1)
    64  	conn1.onCloseReturn(nil)
    65  	conn2 := &connMock{}
    66  	conn2.onCloseReturn(nil)
    67  	conn3 := &connMock{}
    68  	expectServerQuery(conn3, 1, node2)
    69  	conn3.onCloseReturn(nil)
    70  	conn4 := &connMock{}
    71  	conn4.onCloseReturn(nil)
    72  
    73  	dialMock := &mockDial{}
    74  	dialMock.On("Dial", host1.String()).Return(conn1, nil).Once()
    75  	dialMock.On("Dial", host1.String()).Return(conn2, nil).Once()
    76  	dialMock.On("Dial", host2.String()).Return(conn3, nil).Once()
    77  	dialMock.On("Dial", host2.String()).Return(conn4, nil).Once()
    78  
    79  	opts := &ConnectOpts{}
    80  	seeds := []Host{host1, host2}
    81  	cluster := &Cluster{
    82  		hp:          newHostPool(opts),
    83  		seeds:       seeds,
    84  		opts:        opts,
    85  		closed:      clusterWorking,
    86  		connFactory: mockedConnectionFactory(dialMock),
    87  	}
    88  
    89  	err := cluster.run()
    90  	c.Assert(err, test.IsNil)
    91  	conn1.waitDial()
    92  	conn2.waitDial()
    93  	conn3.waitDial()
    94  	conn4.waitDial()
    95  	err = cluster.Close()
    96  	c.Assert(err, test.IsNil)
    97  	conn1.waitDone()
    98  	conn2.waitDone()
    99  	conn3.waitDone()
   100  	conn4.waitDone()
   101  	mock.AssertExpectationsForObjects(c, dialMock, conn1, conn2, conn3, conn4)
   102  }
   103  
   104  func (s *ClusterSuite) TestCluster_NewSingle_NoDiscover_DialFail(c *test.C) {
   105  	host1 := Host{Name: "host1", Port: 28015}
   106  
   107  	dialMock := &mockDial{}
   108  	dialMock.On("Dial", host1.String()).Return(nil, io.EOF).Once()
   109  
   110  	opts := &ConnectOpts{}
   111  	seeds := []Host{host1}
   112  	cluster := &Cluster{
   113  		hp:          newHostPool(opts),
   114  		seeds:       seeds,
   115  		opts:        opts,
   116  		closed:      clusterWorking,
   117  		connFactory: mockedConnectionFactory(dialMock),
   118  	}
   119  
   120  	err := cluster.run()
   121  	c.Assert(err, test.Equals, io.EOF)
   122  	mock.AssertExpectationsForObjects(c, dialMock)
   123  }
   124  
   125  func (s *ClusterSuite) TestCluster_NewMultiple_NoDiscover_DialHalfFail(c *test.C) {
   126  	host1 := Host{Name: "host1", Port: 28015}
   127  	host2 := Host{Name: "host2", Port: 28015}
   128  	node1 := "node1"
   129  
   130  	conn1 := &connMock{}
   131  	expectServerQuery(conn1, 1, node1)
   132  	conn1.onCloseReturn(nil)
   133  	conn2 := &connMock{}
   134  	conn2.onCloseReturn(nil)
   135  
   136  	dialMock := &mockDial{}
   137  	dialMock.On("Dial", host1.String()).Return(conn1, nil).Once()
   138  	dialMock.On("Dial", host1.String()).Return(conn2, nil).Once()
   139  	dialMock.On("Dial", host2.String()).Return(nil, io.EOF).Once()
   140  
   141  	opts := &ConnectOpts{}
   142  	seeds := []Host{host1, host2}
   143  	cluster := &Cluster{
   144  		hp:          newHostPool(opts),
   145  		seeds:       seeds,
   146  		opts:        opts,
   147  		closed:      clusterWorking,
   148  		connFactory: mockedConnectionFactory(dialMock),
   149  	}
   150  
   151  	err := cluster.run()
   152  	c.Assert(err, test.IsNil)
   153  	conn1.waitDial()
   154  	conn2.waitDial()
   155  	err = cluster.Close()
   156  	c.Assert(err, test.IsNil)
   157  	conn1.waitDone()
   158  	conn2.waitDone()
   159  	mock.AssertExpectationsForObjects(c, dialMock, conn1, conn2)
   160  }
   161  
   162  func (s *ClusterSuite) TestCluster_NewMultiple_NoDiscover_DialFail(c *test.C) {
   163  	host1 := Host{Name: "host1", Port: 28015}
   164  	host2 := Host{Name: "host2", Port: 28015}
   165  
   166  	dialMock := &mockDial{}
   167  	dialMock.On("Dial", host1.String()).Return(nil, io.EOF).Once()
   168  	dialMock.On("Dial", host2.String()).Return(nil, io.EOF).Once()
   169  
   170  	opts := &ConnectOpts{}
   171  	seeds := []Host{host1, host2}
   172  	cluster := &Cluster{
   173  		hp:          newHostPool(opts),
   174  		seeds:       seeds,
   175  		opts:        opts,
   176  		closed:      clusterWorking,
   177  		connFactory: mockedConnectionFactory(dialMock),
   178  	}
   179  
   180  	err := cluster.run()
   181  	c.Assert(err, test.Equals, io.EOF)
   182  	mock.AssertExpectationsForObjects(c, dialMock)
   183  }
   184  
   185  func (s *ClusterSuite) TestCluster_NewSingle_NoDiscover_ServerFail(c *test.C) {
   186  	host1 := Host{Name: "host1", Port: 28015}
   187  
   188  	conn1 := &connMock{}
   189  	expectServerQueryFail(conn1, 1, io.EOF)
   190  	conn1.onCloseReturn(nil)
   191  
   192  	dialMock := &mockDial{}
   193  	dialMock.On("Dial", host1.String()).Return(conn1, nil).Once()
   194  
   195  	opts := &ConnectOpts{}
   196  	seeds := []Host{host1}
   197  	cluster := &Cluster{
   198  		hp:          newHostPool(opts),
   199  		seeds:       seeds,
   200  		opts:        opts,
   201  		closed:      clusterWorking,
   202  		connFactory: mockedConnectionFactory(dialMock),
   203  	}
   204  
   205  	err := cluster.run()
   206  	c.Assert(err, test.NotNil)
   207  	if _, ok := err.(RQLConnectionError); ok {
   208  		c.Assert(err, test.Equals, RQLConnectionError{rqlError(io.EOF.Error())})
   209  	} else {
   210  		c.Assert(err, test.Equals, ErrConnectionClosed)
   211  	}
   212  	conn1.waitDone()
   213  	mock.AssertExpectationsForObjects(c, dialMock, conn1)
   214  }
   215  
   216  func (s *ClusterSuite) TestCluster_NewSingle_NoDiscover_PingFail(c *test.C) {
   217  	host1 := Host{Name: "host1", Port: 28015}
   218  	node1 := "node1"
   219  
   220  	conn1 := &connMock{}
   221  	expectServerQuery(conn1, 1, node1)
   222  	conn1.onCloseReturn(nil)
   223  
   224  	dialMock := &mockDial{}
   225  	dialMock.On("Dial", host1.String()).Return(conn1, nil).Once()
   226  	dialMock.On("Dial", host1.String()).Return(nil, io.EOF).Once()
   227  
   228  	opts := &ConnectOpts{}
   229  	seeds := []Host{host1}
   230  	cluster := &Cluster{
   231  		hp:          newHostPool(opts),
   232  		seeds:       seeds,
   233  		opts:        opts,
   234  		closed:      clusterWorking,
   235  		connFactory: mockedConnectionFactory(dialMock),
   236  	}
   237  
   238  	err := cluster.run()
   239  	c.Assert(err, test.Equals, io.EOF)
   240  	conn1.waitDone()
   241  	mock.AssertExpectationsForObjects(c, dialMock, conn1)
   242  }
   243  
   244  func (s *ClusterSuite) TestCluster_NewSingle_Discover_Ok(c *test.C) {
   245  	host1 := Host{Name: "host1", Port: 28015}
   246  	host2 := Host{Name: "1.1.1.1", Port: 2222}
   247  	host3 := Host{Name: "2.2.2.2", Port: 3333}
   248  	node1 := "node1"
   249  	node2 := "node2"
   250  	node3 := "node3"
   251  
   252  	conn1 := &connMock{}
   253  	expectServerQuery(conn1, 1, node1)
   254  	conn1.onCloseReturn(nil)
   255  	conn2 := &connMock{}
   256  	expectServerStatus(conn2, 1, []string{node1, node2, node3}, []Host{host1, host2, host3})
   257  	conn2.onCloseReturn(nil)
   258  	conn3 := &connMock{}
   259  	conn3.onCloseReturn(nil)
   260  	conn4 := &connMock{} // doesn't need call Server() due to it's known through ServerStatus()
   261  	conn4.onCloseReturn(nil)
   262  
   263  	dialMock := &mockDial{}
   264  	dialMock.On("Dial", host1.String()).Return(conn1, nil).Once()
   265  	dialMock.On("Dial", host1.String()).Return(conn2, nil).Once()
   266  	dialMock.On("Dial", host2.String()).Return(conn3, nil).Once()
   267  	dialMock.On("Dial", host3.String()).Return(conn4, nil).Once()
   268  
   269  	opts := &ConnectOpts{DiscoverHosts: true}
   270  	seeds := []Host{host1}
   271  	cluster := &Cluster{
   272  		hp:               newHostPool(opts),
   273  		seeds:            seeds,
   274  		opts:             opts,
   275  		closed:           clusterWorking,
   276  		connFactory:      mockedConnectionFactory(dialMock),
   277  		discoverInterval: 10 * time.Second,
   278  	}
   279  
   280  	err := cluster.run()
   281  	c.Assert(err, test.IsNil)
   282  	conn1.waitDial()
   283  	conn2.waitDial()
   284  	conn3.waitDial()
   285  	conn4.waitDial()
   286  	for !cluster.nodeExists(node2) || !cluster.nodeExists(node3) { // wait node to be added to list to be closed with cluster
   287  		time.Sleep(time.Millisecond)
   288  	}
   289  	err = cluster.Close()
   290  	c.Assert(err, test.IsNil)
   291  	conn1.waitDone()
   292  	conn2.waitDone()
   293  	conn3.waitDone()
   294  	conn4.waitDone()
   295  	mock.AssertExpectationsForObjects(c, dialMock, conn1, conn2, conn3, conn4)
   296  }
   297  
   298  func (s *ClusterSuite) TestCluster_NewMultiple_Discover_Ok(c *test.C) {
   299  	host1 := Host{Name: "host1", Port: 28015}
   300  	host2 := Host{Name: "host2", Port: 28016}
   301  	host3 := Host{Name: "2.2.2.2", Port: 3333}
   302  	node1 := "node1"
   303  	node2 := "node2"
   304  	node3 := "node3"
   305  
   306  	conn1 := &connMock{}
   307  	expectServerQuery(conn1, 1, node1)
   308  	conn1.onCloseReturn(nil)
   309  	conn2 := &connMock{}
   310  	expectServerStatus(conn2, 1, []string{node1, node2, node3}, []Host{host1, host2, host3})
   311  	conn2.onCloseReturn(nil)
   312  	conn3 := &connMock{}
   313  	expectServerQuery(conn3, 1, node2)
   314  	conn3.onCloseReturn(nil)
   315  	conn4 := &connMock{}
   316  	conn4.onCloseReturn(nil)
   317  	conn5 := &connMock{} // doesn't need call Server() due to it's known through ServerStatus()
   318  	conn5.onCloseReturn(nil)
   319  
   320  	dialMock := &mockDial{}
   321  	dialMock.On("Dial", host1.String()).Return(conn1, nil).Once()
   322  	dialMock.On("Dial", host1.String()).Return(conn2, nil).Once()
   323  	dialMock.On("Dial", host2.String()).Return(conn3, nil).Once()
   324  	dialMock.On("Dial", host2.String()).Return(conn4, nil).Once()
   325  	dialMock.On("Dial", host3.String()).Return(conn5, nil).Once()
   326  
   327  	opts := &ConnectOpts{DiscoverHosts: true}
   328  	seeds := []Host{host1, host2}
   329  	cluster := &Cluster{
   330  		hp:               newHostPool(opts),
   331  		seeds:            seeds,
   332  		opts:             opts,
   333  		closed:           clusterWorking,
   334  		connFactory:      mockedConnectionFactory(dialMock),
   335  		discoverInterval: 10 * time.Second,
   336  	}
   337  
   338  	err := cluster.run()
   339  	c.Assert(err, test.IsNil)
   340  	conn1.waitDial()
   341  	conn2.waitDial()
   342  	conn3.waitDial()
   343  	conn4.waitDial()
   344  	conn5.waitDial()
   345  	for !cluster.nodeExists(node3) { // wait node to be added to list to be closed with cluster
   346  		time.Sleep(time.Millisecond)
   347  	}
   348  	err = cluster.Close()
   349  	c.Assert(err, test.IsNil)
   350  	conn1.waitDone()
   351  	conn2.waitDone()
   352  	conn3.waitDone()
   353  	conn4.waitDone()
   354  	conn5.waitDone()
   355  	mock.AssertExpectationsForObjects(c, dialMock, conn1, conn2, conn3, conn4, conn5)
   356  }
   357  
   358  type mockDial struct {
   359  	mock.Mock
   360  }
   361  
   362  func mockedConnectionFactory(dial *mockDial) connFactory {
   363  	return func(host string, opts *ConnectOpts) (connection *Connection, err error) {
   364  		args := dial.MethodCalled("Dial", host)
   365  		err = args.Error(1)
   366  		if err != nil {
   367  			return nil, err
   368  		}
   369  
   370  		connection = newConnection(args.Get(0).(net.Conn), host, opts)
   371  		done := runConnection(connection)
   372  
   373  		m := args.Get(0).(*connMock)
   374  		m.setDone(done)
   375  
   376  		return connection, nil
   377  	}
   378  }
   379  
   380  func expectServerQuery(conn *connMock, token int64, nodeID string) {
   381  	writeChan := make(chan struct{})
   382  	readChan := make(chan struct{})
   383  
   384  	rawQ := makeServerQueryRaw(token)
   385  	conn.On("Write", rawQ).Return(0, nil, nil).Once().Run(func(args mock.Arguments) {
   386  		close(writeChan)
   387  	})
   388  
   389  	rawR := makeServerResponseRaw(token, nodeID)
   390  	rawH := makeResponseHeaderRaw(token, len(rawR))
   391  
   392  	conn.On("Read", respHeaderLen).Return(rawH, len(rawH), nil, nil).Once().Run(func(args mock.Arguments) {
   393  		<-writeChan
   394  		close(readChan)
   395  	})
   396  	conn.On("Read", len(rawR)).Return(rawR, len(rawR), nil, nil).Once().Run(func(args mock.Arguments) {
   397  		<-readChan
   398  	})
   399  }
   400  
   401  func expectServerQueryFail(conn *connMock, token int64, err error) {
   402  	writeChan := make(chan struct{})
   403  
   404  	rawQ := makeServerQueryRaw(token)
   405  	conn.On("Write", rawQ).Return(0, nil, nil).Once().Run(func(args mock.Arguments) {
   406  		close(writeChan)
   407  	})
   408  
   409  	conn.On("Read", respHeaderLen).Return(nil, 0, err, nil).Once().Run(func(args mock.Arguments) {
   410  		<-writeChan
   411  	})
   412  }
   413  
   414  func makeServerQueryRaw(token int64) []byte {
   415  	buf := &bytes.Buffer{}
   416  	buf.Grow(respHeaderLen)
   417  	buf.Write(buf.Bytes()[:respHeaderLen])
   418  	enc := json.NewEncoder(buf)
   419  
   420  	q := Query{
   421  		Token: token,
   422  		Type:  p.Query_SERVER_INFO,
   423  	}
   424  
   425  	err := enc.Encode(q.Build())
   426  	if err != nil {
   427  		panic(fmt.Sprintf("must encode failed: %v", err))
   428  	}
   429  	b := buf.Bytes()
   430  	binary.LittleEndian.PutUint64(b, uint64(q.Token))
   431  	binary.LittleEndian.PutUint32(b[8:], uint32(len(b)-respHeaderLen))
   432  	return b
   433  }
   434  
   435  func makeResponseHeaderRaw(token int64, respLen int) []byte {
   436  	buf1 := &bytes.Buffer{}
   437  	buf1.Grow(respHeaderLen)
   438  	buf1.Write(buf1.Bytes()[:respHeaderLen]) // reserve for header
   439  	b1 := buf1.Bytes()
   440  	binary.LittleEndian.PutUint64(b1, uint64(token))
   441  	binary.LittleEndian.PutUint32(b1[8:], uint32(respLen))
   442  	return b1
   443  }
   444  
   445  func makeServerResponseRaw(token int64, nodeID string) []byte {
   446  	buf2 := &bytes.Buffer{}
   447  	enc := json.NewEncoder(buf2)
   448  
   449  	coded, err := encoding.Encode(&ServerResponse{ID: nodeID})
   450  	if err != nil {
   451  		panic(fmt.Sprintf("must encode response failed: %v", err))
   452  	}
   453  	jresp, err := json.Marshal(coded)
   454  	if err != nil {
   455  		panic(fmt.Sprintf("must encode response failed: %v", err))
   456  	}
   457  
   458  	resp := Response{Token: token, Type: p.Response_SERVER_INFO, Responses: []json.RawMessage{jresp}}
   459  	err = enc.Encode(resp)
   460  	if err != nil {
   461  		panic(fmt.Sprintf("must encode failed: %v", err))
   462  	}
   463  
   464  	return buf2.Bytes()
   465  }
   466  
   467  func expectServerStatus(conn *connMock, token int64, nodeIDs []string, hosts []Host) {
   468  	writeChan := make(chan struct{})
   469  	readHChan := make(chan struct{})
   470  	readRChan := make(chan struct{})
   471  
   472  	rawQ := makeServerStatusQueryRaw(token)
   473  	conn.On("Write", rawQ).Return(0, nil, nil).Once().Run(func(args mock.Arguments) {
   474  		close(writeChan)
   475  	})
   476  
   477  	rawR := makeServerStatusResponseRaw(token, nodeIDs, hosts)
   478  	rawH := makeResponseHeaderRaw(token, len(rawR))
   479  
   480  	conn.On("Read", respHeaderLen).Return(rawH, len(rawH), nil, nil).Once().Run(func(args mock.Arguments) {
   481  		<-writeChan
   482  		close(readHChan)
   483  	})
   484  	conn.On("Read", len(rawR)).Return(rawR, len(rawR), nil, nil).Once().Run(func(args mock.Arguments) {
   485  		<-readHChan
   486  		close(readRChan)
   487  	})
   488  
   489  	rawQ2 := makeContinueQueryRaw(token)
   490  	// maybe - connection may be closed until cursor fetchs next batch
   491  	conn.On("Write", rawQ2).Return(0, nil, nil).Maybe().Run(func(args mock.Arguments) {
   492  		<-readRChan
   493  	})
   494  }
   495  
   496  func makeServerStatusQueryRaw(token int64) []byte {
   497  	buf := &bytes.Buffer{}
   498  	buf.Grow(respHeaderLen)
   499  	buf.Write(buf.Bytes()[:respHeaderLen]) // reserve for header
   500  	enc := json.NewEncoder(buf)
   501  
   502  	t := DB(SystemDatabase).Table(ServerStatusSystemTable).Changes(ChangesOpts{IncludeInitial: true})
   503  	q, err := newQuery(t, map[string]interface{}{}, &ConnectOpts{})
   504  	if err != nil {
   505  		panic(fmt.Sprintf("must newQuery failed: %v", err))
   506  	}
   507  	q.Token = token
   508  
   509  	err = enc.Encode(q.Build())
   510  	if err != nil {
   511  		panic(fmt.Sprintf("must encode failed: %v", err))
   512  	}
   513  
   514  	b := buf.Bytes()
   515  	binary.LittleEndian.PutUint64(b, uint64(q.Token))
   516  	binary.LittleEndian.PutUint32(b[8:], uint32(len(b)-respHeaderLen))
   517  	return b
   518  }
   519  
   520  func makeServerStatusResponseRaw(token int64, nodeIDs []string, hosts []Host) []byte {
   521  	buf2 := &bytes.Buffer{}
   522  	enc := json.NewEncoder(buf2)
   523  
   524  	type change struct {
   525  		NewVal *nodeStatus `rethinkdb:"new_val"`
   526  		OldVal *nodeStatus `rethinkdb:"old_val"`
   527  	}
   528  	jresps := make([]json.RawMessage, len(nodeIDs))
   529  	for i := range nodeIDs {
   530  		status := &nodeStatus{ID: nodeIDs[i], Network: nodeStatusNetwork{
   531  			ReqlPort: int64(hosts[i].Port),
   532  			CanonicalAddresses: []nodeStatusNetworkAddr{
   533  				{Host: hosts[i].Name},
   534  			},
   535  		}}
   536  
   537  		coded, err := encoding.Encode(&change{NewVal: status})
   538  		if err != nil {
   539  			panic(fmt.Sprintf("must encode response failed: %v", err))
   540  		}
   541  		jresps[i], err = json.Marshal(coded)
   542  		if err != nil {
   543  			panic(fmt.Sprintf("must encode response failed: %v", err))
   544  		}
   545  	}
   546  
   547  	resp := Response{Token: token, Type: p.Response_SUCCESS_PARTIAL, Responses: jresps}
   548  	err := enc.Encode(resp)
   549  	if err != nil {
   550  		panic(fmt.Sprintf("must encode failed: %v", err))
   551  	}
   552  	return buf2.Bytes()
   553  }
   554  
   555  func makeContinueQueryRaw(token int64) []byte {
   556  	buf := &bytes.Buffer{}
   557  	buf.Grow(respHeaderLen)
   558  	buf.Write(buf.Bytes()[:respHeaderLen]) // reserve for header
   559  	enc := json.NewEncoder(buf)
   560  
   561  	q := Query{Token: token, Type: p.Query_CONTINUE}
   562  	err := enc.Encode(q.Build())
   563  	if err != nil {
   564  		panic(fmt.Sprintf("must encode failed: %v", err))
   565  	}
   566  
   567  	b := buf.Bytes()
   568  	binary.LittleEndian.PutUint64(b, uint64(q.Token))
   569  	binary.LittleEndian.PutUint32(b[8:], uint32(len(b)-respHeaderLen))
   570  	return b
   571  }