...

Source file src/github.com/gomodule/redigo/redis/conn_test.go

Documentation: github.com/gomodule/redigo/redis

     1  // Copyright 2012 Gary Burd
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"): you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    11  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    12  // License for the specific language governing permissions and limitations
    13  // under the License.
    14  
    15  package redis_test
    16  
    17  import (
    18  	"bytes"
    19  	"crypto/tls"
    20  	"crypto/x509"
    21  	"fmt"
    22  	"io"
    23  	"math"
    24  	"net"
    25  	"os"
    26  	"reflect"
    27  	"strings"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/gomodule/redigo/redis"
    32  )
    33  
    34  type testConn struct {
    35  	io.Reader
    36  	io.Writer
    37  	readDeadline  time.Time
    38  	writeDeadline time.Time
    39  }
    40  
    41  func (*testConn) Close() error                         { return nil }
    42  func (*testConn) LocalAddr() net.Addr                  { return nil }
    43  func (*testConn) RemoteAddr() net.Addr                 { return nil }
    44  func (c *testConn) SetDeadline(t time.Time) error      { c.readDeadline = t; c.writeDeadline = t; return nil }
    45  func (c *testConn) SetReadDeadline(t time.Time) error  { c.readDeadline = t; return nil }
    46  func (c *testConn) SetWriteDeadline(t time.Time) error { c.writeDeadline = t; return nil }
    47  
    48  func dialTestConn(r string, w io.Writer) redis.DialOption {
    49  	return redis.DialNetDial(func(network, addr string) (net.Conn, error) {
    50  		return &testConn{Reader: strings.NewReader(r), Writer: w}, nil
    51  	})
    52  }
    53  
    54  type tlsTestConn struct {
    55  	net.Conn
    56  	done chan struct{}
    57  }
    58  
    59  func (c *tlsTestConn) Close() error {
    60  	c.Conn.Close()
    61  	<-c.done
    62  	return nil
    63  }
    64  
    65  func dialTestConnTLS(r string, w io.Writer) redis.DialOption {
    66  	return redis.DialNetDial(func(network, addr string) (net.Conn, error) {
    67  		client, server := net.Pipe()
    68  		tlsServer := tls.Server(server, &serverTLSConfig)
    69  		go io.Copy(tlsServer, strings.NewReader(r))
    70  		done := make(chan struct{})
    71  		go func() {
    72  			io.Copy(w, tlsServer)
    73  			close(done)
    74  		}()
    75  		return &tlsTestConn{Conn: client, done: done}, nil
    76  	})
    77  }
    78  
    79  type durationArg struct {
    80  	time.Duration
    81  }
    82  
    83  func (t durationArg) RedisArg() interface{} {
    84  	return t.Seconds()
    85  }
    86  
    87  type recursiveArg int
    88  
    89  func (v recursiveArg) RedisArg() interface{} { return v }
    90  
    91  var writeTests = []struct {
    92  	args     []interface{}
    93  	expected string
    94  }{
    95  	{
    96  		[]interface{}{"SET", "key", "value"},
    97  		"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n",
    98  	},
    99  	{
   100  		[]interface{}{"SET", "key", "value"},
   101  		"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n",
   102  	},
   103  	{
   104  		[]interface{}{"SET", "key", byte(100)},
   105  		"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n100\r\n",
   106  	},
   107  	{
   108  		[]interface{}{"SET", "key", 100},
   109  		"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n100\r\n",
   110  	},
   111  	{
   112  		[]interface{}{"SET", "key", int64(math.MinInt64)},
   113  		"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$20\r\n-9223372036854775808\r\n",
   114  	},
   115  	{
   116  		[]interface{}{"SET", "key", float64(1349673917.939762)},
   117  		"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$21\r\n1.349673917939762e+09\r\n",
   118  	},
   119  	{
   120  		[]interface{}{"SET", "key", ""},
   121  		"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$0\r\n\r\n",
   122  	},
   123  	{
   124  		[]interface{}{"SET", "key", nil},
   125  		"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$0\r\n\r\n",
   126  	},
   127  	{
   128  		[]interface{}{"SET", "key", durationArg{time.Minute}},
   129  		"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$2\r\n60\r\n",
   130  	},
   131  	{
   132  		[]interface{}{"SET", "key", recursiveArg(123)},
   133  		"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n123\r\n",
   134  	},
   135  	{
   136  		[]interface{}{"ECHO", true, false},
   137  		"*3\r\n$4\r\nECHO\r\n$1\r\n1\r\n$1\r\n0\r\n",
   138  	},
   139  }
   140  
   141  func TestWrite(t *testing.T) {
   142  	for _, tt := range writeTests {
   143  		var buf bytes.Buffer
   144  		c, _ := redis.Dial("", "", dialTestConn("", &buf))
   145  		err := c.Send(tt.args[0].(string), tt.args[1:]...)
   146  		if err != nil {
   147  			t.Errorf("Send(%v) returned error %v", tt.args, err)
   148  			continue
   149  		}
   150  		c.Flush()
   151  		actual := buf.String()
   152  		if actual != tt.expected {
   153  			t.Errorf("Send(%v) = %q, want %q", tt.args, actual, tt.expected)
   154  		}
   155  	}
   156  }
   157  
   158  var errorSentinel = &struct{}{}
   159  
   160  var readTests = []struct {
   161  	reply    string
   162  	expected interface{}
   163  }{
   164  	{
   165  		"+OK\r\n",
   166  		"OK",
   167  	},
   168  	{
   169  		"+PONG\r\n",
   170  		"PONG",
   171  	},
   172  	{
   173  		"@OK\r\n",
   174  		errorSentinel,
   175  	},
   176  	{
   177  		"$6\r\nfoobar\r\n",
   178  		[]byte("foobar"),
   179  	},
   180  	{
   181  		"$-1\r\n",
   182  		nil,
   183  	},
   184  	{
   185  		":1\r\n",
   186  		int64(1),
   187  	},
   188  	{
   189  		":-2\r\n",
   190  		int64(-2),
   191  	},
   192  	{
   193  		"*0\r\n",
   194  		[]interface{}{},
   195  	},
   196  	{
   197  		"*-1\r\n",
   198  		nil,
   199  	},
   200  	{
   201  		"*4\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$5\r\nHello\r\n$5\r\nWorld\r\n",
   202  		[]interface{}{[]byte("foo"), []byte("bar"), []byte("Hello"), []byte("World")},
   203  	},
   204  	{
   205  		"*3\r\n$3\r\nfoo\r\n$-1\r\n$3\r\nbar\r\n",
   206  		[]interface{}{[]byte("foo"), nil, []byte("bar")},
   207  	},
   208  
   209  	{
   210  		// "x" is not a valid length
   211  		"$x\r\nfoobar\r\n",
   212  		errorSentinel,
   213  	},
   214  	{
   215  		// -2 is not a valid length
   216  		"$-2\r\n",
   217  		errorSentinel,
   218  	},
   219  	{
   220  		// "x"  is not a valid integer
   221  		":x\r\n",
   222  		errorSentinel,
   223  	},
   224  	{
   225  		// missing \r\n following value
   226  		"$6\r\nfoobar",
   227  		errorSentinel,
   228  	},
   229  	{
   230  		// short value
   231  		"$6\r\nxx",
   232  		errorSentinel,
   233  	},
   234  	{
   235  		// long value
   236  		"$6\r\nfoobarx\r\n",
   237  		errorSentinel,
   238  	},
   239  }
   240  
   241  func TestRead(t *testing.T) {
   242  	for _, tt := range readTests {
   243  		c, _ := redis.Dial("", "", dialTestConn(tt.reply, nil))
   244  		actual, err := c.Receive()
   245  		if tt.expected == errorSentinel {
   246  			if err == nil {
   247  				t.Errorf("Receive(%q) did not return expected error", tt.reply)
   248  			}
   249  		} else {
   250  			if err != nil {
   251  				t.Errorf("Receive(%q) returned error %v", tt.reply, err)
   252  				continue
   253  			}
   254  			if !reflect.DeepEqual(actual, tt.expected) {
   255  				t.Errorf("Receive(%q) = %v, want %v", tt.reply, actual, tt.expected)
   256  			}
   257  		}
   258  	}
   259  }
   260  
   261  var testCommands = []struct {
   262  	args     []interface{}
   263  	expected interface{}
   264  }{
   265  	{
   266  		[]interface{}{"PING"},
   267  		"PONG",
   268  	},
   269  	{
   270  		[]interface{}{"SET", "foo", "bar"},
   271  		"OK",
   272  	},
   273  	{
   274  		[]interface{}{"GET", "foo"},
   275  		[]byte("bar"),
   276  	},
   277  	{
   278  		[]interface{}{"GET", "nokey"},
   279  		nil,
   280  	},
   281  	{
   282  		[]interface{}{"MGET", "nokey", "foo"},
   283  		[]interface{}{nil, []byte("bar")},
   284  	},
   285  	{
   286  		[]interface{}{"INCR", "mycounter"},
   287  		int64(1),
   288  	},
   289  	{
   290  		[]interface{}{"LPUSH", "mylist", "foo"},
   291  		int64(1),
   292  	},
   293  	{
   294  		[]interface{}{"LPUSH", "mylist", "bar"},
   295  		int64(2),
   296  	},
   297  	{
   298  		[]interface{}{"LRANGE", "mylist", 0, -1},
   299  		[]interface{}{[]byte("bar"), []byte("foo")},
   300  	},
   301  	{
   302  		[]interface{}{"MULTI"},
   303  		"OK",
   304  	},
   305  	{
   306  		[]interface{}{"LRANGE", "mylist", 0, -1},
   307  		"QUEUED",
   308  	},
   309  	{
   310  		[]interface{}{"PING"},
   311  		"QUEUED",
   312  	},
   313  	{
   314  		[]interface{}{"EXEC"},
   315  		[]interface{}{
   316  			[]interface{}{[]byte("bar"), []byte("foo")},
   317  			"PONG",
   318  		},
   319  	},
   320  }
   321  
   322  func TestDoCommands(t *testing.T) {
   323  	c, err := redis.DialDefaultServer()
   324  	if err != nil {
   325  		t.Fatalf("error connection to database, %v", err)
   326  	}
   327  	defer c.Close()
   328  
   329  	for _, cmd := range testCommands {
   330  		actual, err := c.Do(cmd.args[0].(string), cmd.args[1:]...)
   331  		if err != nil {
   332  			t.Errorf("Do(%v) returned error %v", cmd.args, err)
   333  			continue
   334  		}
   335  		if !reflect.DeepEqual(actual, cmd.expected) {
   336  			t.Errorf("Do(%v) = %v, want %v", cmd.args, actual, cmd.expected)
   337  		}
   338  	}
   339  }
   340  
   341  func TestPipelineCommands(t *testing.T) {
   342  	c, err := redis.DialDefaultServer()
   343  	if err != nil {
   344  		t.Fatalf("error connection to database, %v", err)
   345  	}
   346  	defer c.Close()
   347  
   348  	for _, cmd := range testCommands {
   349  		if err := c.Send(cmd.args[0].(string), cmd.args[1:]...); err != nil {
   350  			t.Fatalf("Send(%v) returned error %v", cmd.args, err)
   351  		}
   352  	}
   353  	if err := c.Flush(); err != nil {
   354  		t.Errorf("Flush() returned error %v", err)
   355  	}
   356  	for _, cmd := range testCommands {
   357  		actual, err := c.Receive()
   358  		if err != nil {
   359  			t.Fatalf("Receive(%v) returned error %v", cmd.args, err)
   360  		}
   361  		if !reflect.DeepEqual(actual, cmd.expected) {
   362  			t.Errorf("Receive(%v) = %v, want %v", cmd.args, actual, cmd.expected)
   363  		}
   364  	}
   365  }
   366  
   367  func TestBlankCommmand(t *testing.T) {
   368  	c, err := redis.DialDefaultServer()
   369  	if err != nil {
   370  		t.Fatalf("error connection to database, %v", err)
   371  	}
   372  	defer c.Close()
   373  
   374  	for _, cmd := range testCommands {
   375  		if err := c.Send(cmd.args[0].(string), cmd.args[1:]...); err != nil {
   376  			t.Fatalf("Send(%v) returned error %v", cmd.args, err)
   377  		}
   378  	}
   379  	reply, err := redis.Values(c.Do(""))
   380  	if err != nil {
   381  		t.Fatalf("Do() returned error %v", err)
   382  	}
   383  	if len(reply) != len(testCommands) {
   384  		t.Fatalf("len(reply)=%d, want %d", len(reply), len(testCommands))
   385  	}
   386  	for i, cmd := range testCommands {
   387  		actual := reply[i]
   388  		if !reflect.DeepEqual(actual, cmd.expected) {
   389  			t.Errorf("Receive(%v) = %v, want %v", cmd.args, actual, cmd.expected)
   390  		}
   391  	}
   392  }
   393  
   394  func TestRecvBeforeSend(t *testing.T) {
   395  	c, err := redis.DialDefaultServer()
   396  	if err != nil {
   397  		t.Fatalf("error connection to database, %v", err)
   398  	}
   399  	defer c.Close()
   400  	done := make(chan struct{})
   401  	go func() {
   402  		c.Receive()
   403  		close(done)
   404  	}()
   405  	time.Sleep(time.Millisecond)
   406  	c.Send("PING")
   407  	c.Flush()
   408  	<-done
   409  	_, err = c.Do("")
   410  	if err != nil {
   411  		t.Fatalf("error=%v", err)
   412  	}
   413  }
   414  
   415  func TestError(t *testing.T) {
   416  	c, err := redis.DialDefaultServer()
   417  	if err != nil {
   418  		t.Fatalf("error connection to database, %v", err)
   419  	}
   420  	defer c.Close()
   421  
   422  	c.Do("SET", "key", "val")
   423  	_, err = c.Do("HSET", "key", "fld", "val")
   424  	if err == nil {
   425  		t.Errorf("Expected err for HSET on string key.")
   426  	}
   427  	if c.Err() != nil {
   428  		t.Errorf("Conn has Err()=%v, expect nil", c.Err())
   429  	}
   430  	_, err = c.Do("SET", "key", "val")
   431  	if err != nil {
   432  		t.Errorf("Do(SET, key, val) returned error %v, expected nil.", err)
   433  	}
   434  }
   435  
   436  func TestReadTimeout(t *testing.T) {
   437  	l, err := net.Listen("tcp", "127.0.0.1:0")
   438  	if err != nil {
   439  		t.Fatalf("net.Listen returned %v", err)
   440  	}
   441  	defer l.Close()
   442  
   443  	go func() {
   444  		for {
   445  			c, err := l.Accept()
   446  			if err != nil {
   447  				return
   448  			}
   449  			go func() {
   450  				time.Sleep(time.Second)
   451  				c.Write([]byte("+OK\r\n"))
   452  				c.Close()
   453  			}()
   454  		}
   455  	}()
   456  
   457  	// Do
   458  
   459  	c1, err := redis.Dial(l.Addr().Network(), l.Addr().String(), redis.DialReadTimeout(time.Millisecond))
   460  	if err != nil {
   461  		t.Fatalf("redis.Dial returned %v", err)
   462  	}
   463  	defer c1.Close()
   464  
   465  	_, err = c1.Do("PING")
   466  	if err == nil {
   467  		t.Fatalf("c1.Do() returned nil, expect error")
   468  	}
   469  	if c1.Err() == nil {
   470  		t.Fatalf("c1.Err() = nil, expect error")
   471  	}
   472  
   473  	// Send/Flush/Receive
   474  
   475  	c2, err := redis.Dial(l.Addr().Network(), l.Addr().String(), redis.DialReadTimeout(time.Millisecond))
   476  	if err != nil {
   477  		t.Fatalf("redis.Dial returned %v", err)
   478  	}
   479  	defer c2.Close()
   480  
   481  	c2.Send("PING")
   482  	c2.Flush()
   483  	_, err = c2.Receive()
   484  	if err == nil {
   485  		t.Fatalf("c2.Receive() returned nil, expect error")
   486  	}
   487  	if c2.Err() == nil {
   488  		t.Fatalf("c2.Err() = nil, expect error")
   489  	}
   490  }
   491  
   492  var dialErrors = []struct {
   493  	rawurl        string
   494  	expectedError string
   495  }{
   496  	{
   497  		"localhost",
   498  		"invalid redis URL scheme",
   499  	},
   500  	// The error message for invalid hosts is different in different
   501  	// versions of Go, so just check that there is an error message.
   502  	{
   503  		"redis://weird url",
   504  		"",
   505  	},
   506  	{
   507  		"redis://foo:bar:baz",
   508  		"",
   509  	},
   510  	{
   511  		"http://www.google.com",
   512  		"invalid redis URL scheme: http",
   513  	},
   514  	{
   515  		"redis://localhost:6379/abc123",
   516  		"invalid database: abc123",
   517  	},
   518  }
   519  
   520  func TestDialURLErrors(t *testing.T) {
   521  	for _, d := range dialErrors {
   522  		_, err := redis.DialURL(d.rawurl)
   523  		if err == nil || !strings.Contains(err.Error(), d.expectedError) {
   524  			t.Errorf("DialURL did not return expected error (expected %v to contain %s)", err, d.expectedError)
   525  		}
   526  	}
   527  }
   528  
   529  func TestDialURLPort(t *testing.T) {
   530  	checkPort := func(network, address string) (net.Conn, error) {
   531  		if address != "localhost:6379" {
   532  			t.Errorf("DialURL did not set port to 6379 by default (got %v)", address)
   533  		}
   534  		return nil, nil
   535  	}
   536  	_, err := redis.DialURL("redis://localhost", redis.DialNetDial(checkPort))
   537  	if err != nil {
   538  		t.Error("dial error:", err)
   539  	}
   540  }
   541  
   542  func TestDialURLHost(t *testing.T) {
   543  	checkHost := func(network, address string) (net.Conn, error) {
   544  		if address != "localhost:6379" {
   545  			t.Errorf("DialURL did not set host to localhost by default (got %v)", address)
   546  		}
   547  		return nil, nil
   548  	}
   549  	_, err := redis.DialURL("redis://:6379", redis.DialNetDial(checkHost))
   550  	if err != nil {
   551  		t.Error("dial error:", err)
   552  	}
   553  }
   554  
   555  var dialURLTests = []struct {
   556  	description string
   557  	url         string
   558  	r           string
   559  	w           string
   560  }{
   561  	{"password", "redis://x:abc123@localhost", "+OK\r\n", "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n"},
   562  	{"database 3", "redis://localhost/3", "+OK\r\n", "*2\r\n$6\r\nSELECT\r\n$1\r\n3\r\n"},
   563  	{"database 99", "redis://localhost/99", "+OK\r\n", "*2\r\n$6\r\nSELECT\r\n$2\r\n99\r\n"},
   564  	{"no database", "redis://localhost/", "+OK\r\n", ""},
   565  }
   566  
   567  func TestDialURL(t *testing.T) {
   568  	for _, tt := range dialURLTests {
   569  		var buf bytes.Buffer
   570  		// UseTLS should be ignored in all of these tests.
   571  		_, err := redis.DialURL(tt.url, dialTestConn(tt.r, &buf), redis.DialUseTLS(true))
   572  		if err != nil {
   573  			t.Errorf("%s dial error: %v", tt.description, err)
   574  			continue
   575  		}
   576  		if w := buf.String(); w != tt.w {
   577  			t.Errorf("%s commands = %q, want %q", tt.description, w, tt.w)
   578  		}
   579  	}
   580  }
   581  
   582  func checkPingPong(t *testing.T, buf *bytes.Buffer, c redis.Conn) {
   583  	resp, err := c.Do("PING")
   584  	if err != nil {
   585  		t.Fatal("ping error:", err)
   586  	}
   587  	// Close connection to ensure that writes to buf are complete.
   588  	c.Close()
   589  	expected := "*1\r\n$4\r\nPING\r\n"
   590  	actual := buf.String()
   591  	if actual != expected {
   592  		t.Errorf("commands = %q, want %q", actual, expected)
   593  	}
   594  	if resp != "PONG" {
   595  		t.Errorf("resp = %v, want %v", resp, "PONG")
   596  	}
   597  }
   598  
   599  const pingResponse = "+PONG\r\n"
   600  
   601  func TestDialURLTLS(t *testing.T) {
   602  	var buf bytes.Buffer
   603  	c, err := redis.DialURL("rediss://example.com/",
   604  		redis.DialTLSConfig(&clientTLSConfig),
   605  		dialTestConnTLS(pingResponse, &buf))
   606  	if err != nil {
   607  		t.Fatal("dial error:", err)
   608  	}
   609  	checkPingPong(t, &buf, c)
   610  }
   611  
   612  func TestDialUseTLS(t *testing.T) {
   613  	var buf bytes.Buffer
   614  	c, err := redis.Dial("tcp", "example.com:6379",
   615  		redis.DialTLSConfig(&clientTLSConfig),
   616  		dialTestConnTLS(pingResponse, &buf),
   617  		redis.DialUseTLS(true))
   618  	if err != nil {
   619  		t.Fatal("dial error:", err)
   620  	}
   621  	checkPingPong(t, &buf, c)
   622  }
   623  
   624  func TestDialTLSSKipVerify(t *testing.T) {
   625  	var buf bytes.Buffer
   626  	c, err := redis.Dial("tcp", "example.com:6379",
   627  		dialTestConnTLS(pingResponse, &buf),
   628  		redis.DialTLSSkipVerify(true),
   629  		redis.DialUseTLS(true))
   630  	if err != nil {
   631  		t.Fatal("dial error:", err)
   632  	}
   633  	checkPingPong(t, &buf, c)
   634  }
   635  
   636  // Connect to local instance of Redis running on the default port.
   637  func ExampleDial() {
   638  	c, err := redis.Dial("tcp", ":6379")
   639  	if err != nil {
   640  		// handle error
   641  	}
   642  	defer c.Close()
   643  }
   644  
   645  // Connect to remote instance of Redis using a URL.
   646  func ExampleDialURL() {
   647  	c, err := redis.DialURL(os.Getenv("REDIS_URL"))
   648  	if err != nil {
   649  		// handle connection error
   650  	}
   651  	defer c.Close()
   652  }
   653  
   654  // TextExecError tests handling of errors in a transaction. See
   655  // http://redis.io/topics/transactions for information on how Redis handles
   656  // errors in a transaction.
   657  func TestExecError(t *testing.T) {
   658  	c, err := redis.DialDefaultServer()
   659  	if err != nil {
   660  		t.Fatalf("error connection to database, %v", err)
   661  	}
   662  	defer c.Close()
   663  
   664  	// Execute commands that fail before EXEC is called.
   665  
   666  	c.Do("DEL", "k0")
   667  	c.Do("ZADD", "k0", 0, 0)
   668  	c.Send("MULTI")
   669  	c.Send("NOTACOMMAND", "k0", 0, 0)
   670  	c.Send("ZINCRBY", "k0", 0, 0)
   671  	v, err := c.Do("EXEC")
   672  	if err == nil {
   673  		t.Fatalf("EXEC returned values %v, expected error", v)
   674  	}
   675  
   676  	// Execute commands that fail after EXEC is called. The first command
   677  	// returns an error.
   678  
   679  	c.Do("DEL", "k1")
   680  	c.Do("ZADD", "k1", 0, 0)
   681  	c.Send("MULTI")
   682  	c.Send("HSET", "k1", 0, 0)
   683  	c.Send("ZINCRBY", "k1", 0, 0)
   684  	v, err = c.Do("EXEC")
   685  	if err != nil {
   686  		t.Fatalf("EXEC returned error %v", err)
   687  	}
   688  
   689  	vs, err := redis.Values(v, nil)
   690  	if err != nil {
   691  		t.Fatalf("Values(v) returned error %v", err)
   692  	}
   693  
   694  	if len(vs) != 2 {
   695  		t.Fatalf("len(vs) == %d, want 2", len(vs))
   696  	}
   697  
   698  	if _, ok := vs[0].(error); !ok {
   699  		t.Fatalf("first result is type %T, expected error", vs[0])
   700  	}
   701  
   702  	if _, ok := vs[1].([]byte); !ok {
   703  		t.Fatalf("second result is type %T, expected []byte", vs[1])
   704  	}
   705  
   706  	// Execute commands that fail after EXEC is called. The second command
   707  	// returns an error.
   708  
   709  	c.Do("ZADD", "k2", 0, 0)
   710  	c.Send("MULTI")
   711  	c.Send("ZINCRBY", "k2", 0, 0)
   712  	c.Send("HSET", "k2", 0, 0)
   713  	v, err = c.Do("EXEC")
   714  	if err != nil {
   715  		t.Fatalf("EXEC returned error %v", err)
   716  	}
   717  
   718  	vs, err = redis.Values(v, nil)
   719  	if err != nil {
   720  		t.Fatalf("Values(v) returned error %v", err)
   721  	}
   722  
   723  	if len(vs) != 2 {
   724  		t.Fatalf("len(vs) == %d, want 2", len(vs))
   725  	}
   726  
   727  	if _, ok := vs[0].([]byte); !ok {
   728  		t.Fatalf("first result is type %T, expected []byte", vs[0])
   729  	}
   730  
   731  	if _, ok := vs[1].(error); !ok {
   732  		t.Fatalf("second result is type %T, expected error", vs[2])
   733  	}
   734  }
   735  
   736  func BenchmarkDoEmpty(b *testing.B) {
   737  	b.StopTimer()
   738  	c, err := redis.DialDefaultServer()
   739  	if err != nil {
   740  		b.Fatal(err)
   741  	}
   742  	defer c.Close()
   743  	b.StartTimer()
   744  	for i := 0; i < b.N; i++ {
   745  		if _, err := c.Do(""); err != nil {
   746  			b.Fatal(err)
   747  		}
   748  	}
   749  }
   750  
   751  func BenchmarkDoPing(b *testing.B) {
   752  	b.StopTimer()
   753  	c, err := redis.DialDefaultServer()
   754  	if err != nil {
   755  		b.Fatal(err)
   756  	}
   757  	defer c.Close()
   758  	b.StartTimer()
   759  	for i := 0; i < b.N; i++ {
   760  		if _, err := c.Do("PING"); err != nil {
   761  			b.Fatal(err)
   762  		}
   763  	}
   764  }
   765  
   766  var clientTLSConfig, serverTLSConfig tls.Config
   767  
   768  func init() {
   769  	// The certificate and key for testing TLS dial options was created
   770  	// using the command
   771  	//
   772  	//   go run GOROOT/src/crypto/tls/generate_cert.go  \
   773  	//      --rsa-bits 1024 \
   774  	//      --host 127.0.0.1,::1,example.com --ca \
   775  	//      --start-date "Jan 1 00:00:00 1970" \
   776  	//      --duration=1000000h
   777  	//
   778  	// where GOROOT is the value of GOROOT reported by go env.
   779  	localhostCert := []byte(`
   780  -----BEGIN CERTIFICATE-----
   781  MIICFDCCAX2gAwIBAgIRAJfBL4CUxkXcdlFurb3K+iowDQYJKoZIhvcNAQELBQAw
   782  EjEQMA4GA1UEChMHQWNtZSBDbzAgFw03MDAxMDEwMDAwMDBaGA8yMDg0MDEyOTE2
   783  MDAwMFowEjEQMA4GA1UEChMHQWNtZSBDbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
   784  gYkCgYEArizw8WxMUQ3bGHLeuJ4fDrEpy+L2pqrbYRlKk1DasJ/VkB8bImzIpe6+
   785  LGjiYIxvnDCOJ3f3QplcQuiuMyl6f2irJlJsbFT8Lo/3obnuTKAIaqUdJUqBg6y+
   786  JaL8Auk97FvunfKFv8U1AIhgiLzAfQ/3Eaq1yi87Ra6pMjGbTtcCAwEAAaNoMGYw
   787  DgYDVR0PAQH/BAQDAgKkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQF
   788  MAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAAAAAAAAAA
   789  AAAAAAEwDQYJKoZIhvcNAQELBQADgYEAdZ8daIVkyhVwflt5I19m0oq1TycbGO1+
   790  ach7T6cZiBQeNR/SJtxr/wKPEpmvUgbv2BfFrKJ8QoIHYsbNSURTWSEa02pfw4k9
   791  6RQhij3ZkG79Ituj5OYRORV6Z0HUW32r670BtcuHuAhq7YA6Nxy4FtSt7bAlVdRt
   792  rrKgNsltzMk=
   793  -----END CERTIFICATE-----`)
   794  
   795  	localhostKey := []byte(`
   796  -----BEGIN RSA PRIVATE KEY-----
   797  MIICXAIBAAKBgQCuLPDxbExRDdsYct64nh8OsSnL4vamqtthGUqTUNqwn9WQHxsi
   798  bMil7r4saOJgjG+cMI4nd/dCmVxC6K4zKXp/aKsmUmxsVPwuj/ehue5MoAhqpR0l
   799  SoGDrL4lovwC6T3sW+6d8oW/xTUAiGCIvMB9D/cRqrXKLztFrqkyMZtO1wIDAQAB
   800  AoGACrc5G6FOEK6JjDeE/Fa+EmlT6PdNtXNNi+vCas3Opo8u1G8VfEi1D4BgstrB
   801  Eq+RLkrOdB8tVyuYQYWPMhabMqF+hhKJN72j0OwfuPlVvTInwb/cKjo/zbH1IA+Y
   802  HenHNK4ywv7/p/9/MvQPJ3I32cQBCgGUW5chVSH5M1sj5gECQQDabQAI1X0uDqCm
   803  KbX9gXVkAgxkFddrt6LBHt57xujFcqEKFE7nwKhDh7DweVs/VEJ+kpid4z+UnLOw
   804  KjtP9JolAkEAzCNBphQ//IsbH5rNs10wIUw3Ks/Oepicvr6kUFbIv+neRzi1iJHa
   805  m6H7EayK3PWgax6BAsR/t0Jc9XV7r2muSwJAVzN09BHnK+ADGtNEKLTqXMbEk6B0
   806  pDhn7ZmZUOkUPN+Kky+QYM11X6Bob1jDqQDGmymDbGUxGO+GfSofC8inUQJAGfci
   807  Eo3g1a6b9JksMPRZeuLG4ZstGErxJRH6tH1Va5PDwitka8qhk8o2tTjNMO3NSdLH
   808  diKoXBcE2/Pll5pJoQJBAIMiiMIzXJhnN4mX8may44J/HvMlMf2xuVH2gNMwmZuc
   809  Bjqn3yoLHaoZVvbWOi0C2TCN4FjXjaLNZGifQPbIcaA=
   810  -----END RSA PRIVATE KEY-----`)
   811  
   812  	cert, err := tls.X509KeyPair(localhostCert, localhostKey)
   813  	if err != nil {
   814  		panic(fmt.Sprintf("error creating key pair: %v", err))
   815  	}
   816  	serverTLSConfig.Certificates = []tls.Certificate{cert}
   817  
   818  	certificate, err := x509.ParseCertificate(serverTLSConfig.Certificates[0].Certificate[0])
   819  	if err != nil {
   820  		panic(fmt.Sprintf("error parsing x509 certificate: %v", err))
   821  	}
   822  
   823  	clientTLSConfig.RootCAs = x509.NewCertPool()
   824  	clientTLSConfig.RootCAs.AddCert(certificate)
   825  }
   826  
   827  func TestWithTimeout(t *testing.T) {
   828  	for _, recv := range []bool{true, false} {
   829  		for _, defaultTimout := range []time.Duration{0, time.Minute} {
   830  			var buf bytes.Buffer
   831  			nc := &testConn{Reader: strings.NewReader("+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n"), Writer: &buf}
   832  			c, _ := redis.Dial("", "", redis.DialReadTimeout(defaultTimout), redis.DialNetDial(func(network, addr string) (net.Conn, error) { return nc, nil }))
   833  			for i := 0; i < 4; i++ {
   834  				var minDeadline, maxDeadline time.Time
   835  
   836  				// Alternate between default and specified timeout.
   837  				if i%2 == 0 {
   838  					if defaultTimout != 0 {
   839  						minDeadline = time.Now().Add(defaultTimout)
   840  					}
   841  					if recv {
   842  						c.Receive()
   843  					} else {
   844  						c.Do("PING")
   845  					}
   846  					if defaultTimout != 0 {
   847  						maxDeadline = time.Now().Add(defaultTimout)
   848  					}
   849  				} else {
   850  					timeout := 10 * time.Minute
   851  					minDeadline = time.Now().Add(timeout)
   852  					if recv {
   853  						redis.ReceiveWithTimeout(c, timeout)
   854  					} else {
   855  						redis.DoWithTimeout(c, timeout, "PING")
   856  					}
   857  					maxDeadline = time.Now().Add(timeout)
   858  				}
   859  
   860  				// Expect set deadline in expected range.
   861  				if nc.readDeadline.Before(minDeadline) || nc.readDeadline.After(maxDeadline) {
   862  					t.Errorf("recv %v, %d: do deadline error: %v, %v, %v", recv, i, minDeadline, nc.readDeadline, maxDeadline)
   863  				}
   864  			}
   865  		}
   866  	}
   867  }
   868  

View as plain text