Skip to content

Commit 2d43463

Browse files
committed
add TLS integration test, update Client.DialContext docs
Turns out we already have the necessary hook (DialContext), which was added in #158. Updates #1212 Signed-off-by: Brad Fitzpatrick <[email protected]>
1 parent 21f1bbe commit 2d43463

File tree

4 files changed

+163
-2
lines changed

4 files changed

+163
-2
lines changed

memcache/fakecerts_test.go

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package memcache
2+
3+
import "strings"
4+
5+
// Copied from Go's net/http/internal/testcert package.
6+
7+
// LocalhostCert is a PEM-encoded TLS cert with SAN IPs
8+
// "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT.
9+
// generated from src/crypto/tls:
10+
// go run generate_cert.go --rsa-bits 2048 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
11+
var LocalhostCert = []byte(`-----BEGIN CERTIFICATE-----
12+
MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS
13+
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
14+
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
15+
MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r
16+
bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U
17+
aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P
18+
YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk
19+
POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu
20+
h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE
21+
AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
22+
DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv
23+
bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI
24+
5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv
25+
cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2
26+
+tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B
27+
grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK
28+
5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/
29+
WkBKOclmOV2xlTVuPw==
30+
-----END CERTIFICATE-----`)
31+
32+
// LocalhostKey is the private key for LocalhostCert.
33+
var LocalhostKey = []byte(testingKey(`-----BEGIN RSA TESTING KEY-----
34+
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDoZtrm0dXV0Aqi
35+
4Bpc7f95sNRTiu/AJSD8I1onY9PnEsPg3VVxvytsVJbYdcqr4w99V3AgpH/UNzMS
36+
gAZ/8lZBNbsSDOVesJ3euVqMRfYPvd9pYl6QPRRpSDPm+2tNdn3QFAvta9EgJ3sW
37+
URnoU85w+W6aLI2bNSq3AaE771p3VbkGolpEjo9h+i42TBHo1rhPNKPkGupR8/QX
38+
AOLMpInRdeaHyDwb2a3DE5I3dG7VAVzrVfJ6W6Q84YoFX+rpEE2SVM17SAjy6xQy
39+
VjKgLvK2mk0xbtfa+h0B6VK7bmODHZqeP18NVm6HsBcXn7iclLgAC3SfWU1jucZK
40+
x1lqzw9tAgMBAAECggEABWzxS1Y2wckblnXY57Z+sl6YdmLV+gxj2r8Qib7g4ZIk
41+
lIlWR1OJNfw7kU4eryib4fc6nOh6O4AWZyYqAK6tqNQSS/eVG0LQTLTTEldHyVJL
42+
dvBe+MsUQOj4nTndZW+QvFzbcm2D8lY5n2nBSxU5ypVoKZ1EqQzytFcLZpTN7d89
43+
EPj0qDyrV4NZlWAwL1AygCwnlwhMQjXEalVF1ylXwU3QzyZ/6MgvF6d3SSUlh+sq
44+
XefuyigXw484cQQgbzopv6niMOmGP3of+yV4JQqUSb3IDmmT68XjGd2Dkxl4iPki
45+
6ZwXf3CCi+c+i/zVEcufgZ3SLf8D99kUGE7v7fZ6AQKBgQD1ZX3RAla9hIhxCf+O
46+
3D+I1j2LMrdjAh0ZKKqwMR4JnHX3mjQI6LwqIctPWTU8wYFECSh9klEclSdCa64s
47+
uI/GNpcqPXejd0cAAdqHEEeG5sHMDt0oFSurL4lyud0GtZvwlzLuwEweuDtvT9cJ
48+
Wfvl86uyO36IW8JdvUprYDctrQKBgQDycZ697qutBieZlGkHpnYWUAeImVA878sJ
49+
w44NuXHvMxBPz+lbJGAg8Cn8fcxNAPqHIraK+kx3po8cZGQywKHUWsxi23ozHoxo
50+
+bGqeQb9U661TnfdDspIXia+xilZt3mm5BPzOUuRqlh4Y9SOBpSWRmEhyw76w4ZP
51+
OPxjWYAgwQKBgA/FehSYxeJgRjSdo+MWnK66tjHgDJE8bYpUZsP0JC4R9DL5oiaA
52+
brd2fI6Y+SbyeNBallObt8LSgzdtnEAbjIH8uDJqyOmknNePRvAvR6mP4xyuR+Bv
53+
m+Lgp0DMWTw5J9CKpydZDItc49T/mJ5tPhdFVd+am0NAQnmr1MCZ6nHxAoGABS3Y
54+
LkaC9FdFUUqSU8+Chkd/YbOkuyiENdkvl6t2e52jo5DVc1T7mLiIrRQi4SI8N9bN
55+
/3oJWCT+uaSLX2ouCtNFunblzWHBrhxnZzTeqVq4SLc8aESAnbslKL4i8/+vYZlN
56+
s8xtiNcSvL+lMsOBORSXzpj/4Ot8WwTkn1qyGgECgYBKNTypzAHeLE6yVadFp3nQ
57+
Ckq9yzvP/ib05rvgbvrne00YeOxqJ9gtTrzgh7koqJyX1L4NwdkEza4ilDWpucn0
58+
xiUZS4SoaJq6ZvcBYS62Yr1t8n09iG47YL8ibgtmH3L+svaotvpVxVK+d7BLevA/
59+
ZboOWVe3icTy64BT3OQhmg==
60+
-----END RSA TESTING KEY-----`))
61+
62+
func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }

memcache/fakeserver_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/*
2+
Copyright 2023 The gomemcache AUTHORS
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
117
package memcache
218

319
import (

memcache/memcache.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,12 @@ func NewFromSelector(ss ServerSelector) *Client {
132132
// Client is a memcache client.
133133
// It is safe for unlocked use by multiple concurrent goroutines.
134134
type Client struct {
135-
// DialContext connects to the address on the named network
136-
// using the provided context
135+
// DialContext connects to the address on the named network using the
136+
// provided context.
137+
//
138+
// To connect to servers using TLS (memcached running with "--enable-ssl"),
139+
// use a DialContext func that uses tls.Dialer.DialContext. See this
140+
// package's tests as an example.
137141
DialContext func(ctx context.Context, network, address string) (net.Conn, error)
138142

139143
// Timeout specifies the socket read/write timeout.

memcache/memcache_test.go

+79
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,25 @@ package memcache
1919

2020
import (
2121
"bufio"
22+
"bytes"
23+
"context"
24+
"crypto/tls"
25+
"flag"
2226
"fmt"
2327
"io"
2428
"io/ioutil"
2529
"net"
2630
"os"
2731
"os/exec"
32+
"path/filepath"
33+
"strconv"
2834
"strings"
2935
"testing"
3036
"time"
3137
)
3238

39+
var debug = flag.Bool("debug", false, "be more verbose")
40+
3341
const localhostTCPAddr = "localhost:11211"
3442

3543
func TestLocalhost(t *testing.T) {
@@ -81,6 +89,77 @@ func TestFakeServer(t *testing.T) {
8189
testWithClient(t, New(ln.Addr().String()))
8290
}
8391

92+
func TestTLS(t *testing.T) {
93+
t.Parallel()
94+
td := t.TempDir()
95+
96+
// Test whether our memcached binary has TLS support. We --enable-ssl first,
97+
// before --version, as memcached evaluates the flags in the order provided
98+
// and we want it to fail if it's built without TLS support (as it is in
99+
// Debian, but not Ubuntu or Homebrew).
100+
out, err := exec.Command("memcached", "--enable-ssl", "--version").CombinedOutput()
101+
if err != nil {
102+
t.Skipf("skipping test; couldn't find memcached or no TLS support in binary: %v, %s", err, out)
103+
}
104+
t.Logf("version: %s", bytes.TrimSpace(out))
105+
106+
if err := os.WriteFile(filepath.Join(td, "/cert.pem"), LocalhostCert, 0644); err != nil {
107+
t.Fatal(err)
108+
}
109+
if err := os.WriteFile(filepath.Join(td, "/key.pem"), LocalhostKey, 0644); err != nil {
110+
t.Fatal(err)
111+
}
112+
113+
// Find some unused port. This is racy but we hope for the best and hope the kernel
114+
// doesn't reassign our ephemeral port to somebody in the tiny race window.
115+
ln, err := net.Listen("tcp", "localhost:0")
116+
if err != nil {
117+
t.Fatalf("failed to listen: %v", err)
118+
}
119+
port := ln.Addr().(*net.TCPAddr).Port
120+
ln.Close()
121+
122+
cmd := exec.Command("memcached",
123+
"--port="+strconv.Itoa(port),
124+
"--listen=127.0.0.1",
125+
"--enable-ssl",
126+
"-o", "ssl_chain_cert=cert.pem",
127+
"-o", "ssl_key=key.pem")
128+
cmd.Dir = td
129+
if *debug {
130+
cmd.Stdout = os.Stdout
131+
cmd.Stderr = os.Stderr
132+
}
133+
if err := cmd.Start(); err != nil {
134+
t.Fatalf("failed to start memcached: %v", err)
135+
}
136+
defer cmd.Wait()
137+
defer cmd.Process.Kill()
138+
139+
// Wait a bit for the server to be running.
140+
for i := 0; i < 10; i++ {
141+
nc, err := net.Dial("tcp", "localhost:"+strconv.Itoa(port))
142+
if err == nil {
143+
t.Logf("localhost:%d is up.", port)
144+
nc.Close()
145+
break
146+
}
147+
t.Logf("waiting for localhost:%d to be up...", port)
148+
time.Sleep(time.Duration(25*i) * time.Millisecond)
149+
}
150+
151+
c := New(net.JoinHostPort("127.0.0.1", strconv.Itoa(port)))
152+
c.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
153+
var td tls.Dialer
154+
td.Config = &tls.Config{
155+
InsecureSkipVerify: true,
156+
}
157+
return td.DialContext(ctx, network, addr)
158+
159+
}
160+
testWithClient(t, c)
161+
}
162+
84163
func mustSetF(t *testing.T, c *Client) func(*Item) {
85164
return func(it *Item) {
86165
if err := c.Set(it); err != nil {

0 commit comments

Comments
 (0)