github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/server/https/https_test.go (about)

     1  // Copyright 2017 Google Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://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,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package https
    16  
    17  import (
    18  	"bufio"
    19  	"bytes"
    20  	"context"
    21  	"crypto/ecdsa"
    22  	"crypto/elliptic"
    23  	"crypto/rand"
    24  	"crypto/sha256"
    25  	"crypto/tls"
    26  	"crypto/x509"
    27  	"encoding/base64"
    28  	"encoding/binary"
    29  	"encoding/hex"
    30  	"encoding/pem"
    31  	"fmt"
    32  	"io"
    33  	"math/big"
    34  	"net"
    35  	"net/http"
    36  	"net/url"
    37  	"strings"
    38  	"testing"
    39  	"time"
    40  
    41  	log "github.com/golang/glog"
    42  	"google.golang.org/protobuf/proto"
    43  
    44  	"github.com/google/fleetspeak/fleetspeak/src/common"
    45  	fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak"
    46  	"github.com/google/fleetspeak/fleetspeak/src/comtesting"
    47  	"github.com/google/fleetspeak/fleetspeak/src/server"
    48  	"github.com/google/fleetspeak/fleetspeak/src/server/comms"
    49  	"github.com/google/fleetspeak/fleetspeak/src/server/db"
    50  	"github.com/google/fleetspeak/fleetspeak/src/server/sqlite"
    51  	"github.com/google/fleetspeak/fleetspeak/src/server/testserver"
    52  
    53  	cpb "github.com/google/fleetspeak/fleetspeak/src/server/components/proto/fleetspeak_components"
    54  )
    55  
    56  var (
    57  	serverCert []byte
    58  )
    59  
    60  func makeServer(t *testing.T, caseName string, frontendConfig *cpb.FrontendConfig) (*server.Server, *sqlite.Datastore, string) {
    61  	cert, key, err := comtesting.ServerCert()
    62  	if err != nil {
    63  		t.Fatal(err)
    64  	}
    65  	serverCert = cert
    66  
    67  	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
    68  	if err != nil {
    69  		t.Fatal(err)
    70  	}
    71  	tl, err := net.ListenTCP("tcp", addr)
    72  	if err != nil {
    73  		t.Fatal(err)
    74  	}
    75  	com, err := NewCommunicator(Params{Listener: tl, Cert: cert, Key: key, Streaming: true, FrontendConfig: frontendConfig})
    76  	if err != nil {
    77  		t.Fatal(err)
    78  	}
    79  	time.Sleep(time.Second)
    80  	log.Infof("Communicator listening to: %v", tl.Addr())
    81  	ts := testserver.Make(t, "https", caseName, []comms.Communicator{com})
    82  
    83  	return ts.S, ts.DS, tl.Addr().String()
    84  }
    85  
    86  func clientCertFingerprint(derBytes []byte) string {
    87  	// Calculate the SHA-256 digest of the DER certificate
    88  	sha256Digest := sha256.Sum256(derBytes)
    89  
    90  	// Convert the SHA-256 digest to a hexadecimal string
    91  	sha256HexStr := fmt.Sprintf("%x", sha256Digest)
    92  
    93  	sha256Binary, err := hex.DecodeString(sha256HexStr)
    94  	if err != nil {
    95  		log.Errorf("error decoding hexdump: %v\n", err)
    96  		return ""
    97  	}
    98  
    99  	// Convert the hexadecimal string to a base64 encoded string
   100  	// It also removes trailing "=" padding characters
   101  	base64EncodedStr := strings.TrimRight(base64.StdEncoding.EncodeToString(sha256Binary), "=")
   102  
   103  	// Rreturn the base64 encoded string
   104  	return base64EncodedStr
   105  }
   106  
   107  func makeClient(t *testing.T, doTls bool) (common.ClientID, *http.Client, []byte, string) {
   108  	// Populate a CertPool with the server's certificate.
   109  	cp := x509.NewCertPool()
   110  	if !cp.AppendCertsFromPEM(serverCert) {
   111  		t.Fatal("Unable to parse server pem.")
   112  	}
   113  
   114  	// Create a key for the client.
   115  	privKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
   116  	if err != nil {
   117  		t.Fatal(err)
   118  	}
   119  	b, err := x509.MarshalECPrivateKey(privKey)
   120  	if err != nil {
   121  		t.Fatal(err)
   122  	}
   123  	bk := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b})
   124  
   125  	id, err := common.MakeClientID(privKey.Public())
   126  	if err != nil {
   127  		t.Fatal(err)
   128  	}
   129  
   130  	// Create a self signed cert for client key.
   131  	tmpl := x509.Certificate{
   132  		SerialNumber: big.NewInt(42),
   133  	}
   134  	b, err = x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, privKey.Public(), privKey)
   135  	if err != nil {
   136  		t.Fatal(err)
   137  	}
   138  	fp := clientCertFingerprint(b)
   139  	bc := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: b})
   140  
   141  	clientCert, err := tls.X509KeyPair(bc, bk)
   142  	if err != nil {
   143  		t.Fatal(err)
   144  	}
   145  	var cl http.Client
   146  	if doTls {
   147  		cl = http.Client{
   148  			Transport: &http.Transport{
   149  				TLSClientConfig: &tls.Config{
   150  					RootCAs:      cp,
   151  					Certificates: []tls.Certificate{clientCert},
   152  				},
   153  				Dial: (&net.Dialer{
   154  					Timeout:   30 * time.Second,
   155  					KeepAlive: 30 * time.Second,
   156  				}).Dial,
   157  				TLSHandshakeTimeout:   10 * time.Second,
   158  				ExpectContinueTimeout: 1 * time.Second,
   159  			},
   160  		}
   161  	} else {
   162  		cl = http.Client{
   163  			Transport: &http.Transport{
   164  				Dial: (&net.Dialer{
   165  					Timeout:   30 * time.Second,
   166  					KeepAlive: 30 * time.Second,
   167  				}).Dial,
   168  				ExpectContinueTimeout: 1 * time.Second,
   169  			},
   170  		}
   171  	}
   172  	return id, &cl, bc, fp
   173  }
   174  
   175  func TestNormalPoll(t *testing.T) {
   176  	ctx := context.Background()
   177  
   178  	s, ds, addr := makeServer(t, "Normal", nil)
   179  	id, cl, _, _ := makeClient(t, true)
   180  	defer s.Stop()
   181  
   182  	u := url.URL{Scheme: "https", Host: addr, Path: "/message"}
   183  
   184  	// An empty body is a valid, though atypical initial request.
   185  	resp, err := cl.Post(u.String(), "", nil)
   186  	if err != nil {
   187  		t.Fatal(err)
   188  	}
   189  
   190  	b, err := io.ReadAll(resp.Body)
   191  	if err != nil {
   192  		t.Error(err)
   193  	}
   194  	resp.Body.Close()
   195  
   196  	var cd fspb.ContactData
   197  	if err := proto.Unmarshal(b, &cd); err != nil {
   198  		t.Errorf("Unable to parse returned data as ContactData: %v", err)
   199  	}
   200  	if cd.SequencingNonce == 0 {
   201  		t.Error("Expected SequencingNonce in returned ContactData")
   202  	}
   203  
   204  	// The client should now exist in the datastore.
   205  	_, err = ds.GetClientData(ctx, id)
   206  	if err != nil {
   207  		t.Errorf("Error getting client data after poll: %v", err)
   208  	}
   209  }
   210  
   211  func TestFile(t *testing.T) {
   212  	ctx := context.Background()
   213  	s, ds, addr := makeServer(t, "File", nil)
   214  	_, cl, _, _ := makeClient(t, true)
   215  	defer s.Stop()
   216  
   217  	data := []byte("The quick sly fox jumped over the lazy dogs.")
   218  	if err := ds.StoreFile(ctx, "testService", "testFile", bytes.NewReader(data)); err != nil {
   219  		t.Errorf("Error from StoreFile(testService, testFile): %v", err)
   220  	}
   221  
   222  	u := url.URL{Scheme: "https", Host: addr, Path: "/files/testService/testFile"}
   223  	resp, err := cl.Get(u.String())
   224  	if err != nil {
   225  		t.Fatal(err)
   226  	}
   227  	if resp.StatusCode != http.StatusOK {
   228  		t.Errorf("Unexpected response code when reading file got [%v] want [%v].", resp.StatusCode, http.StatusOK)
   229  	}
   230  	b, err := io.ReadAll(resp.Body)
   231  	if err != nil {
   232  		t.Errorf("Unexpected error reading file body: %v", err)
   233  	}
   234  	if !bytes.Equal(data, b) {
   235  		t.Errorf("Unexpected file body, got [%v], want [%v]", string(b), string(data))
   236  	}
   237  	resp.Body.Close()
   238  }
   239  
   240  func makeWrapped() []byte {
   241  	cd := &fspb.ContactData{
   242  		ClientClock: db.NowProto(),
   243  	}
   244  	b, err := proto.Marshal(cd)
   245  	if err != nil {
   246  		log.Fatal(err)
   247  	}
   248  	wcd := &fspb.WrappedContactData{
   249  		ContactData:  b,
   250  		ClientLabels: []string{"linux", "test"},
   251  	}
   252  
   253  	buf, err := proto.Marshal(wcd)
   254  	if err != nil {
   255  		log.Fatal(err)
   256  	}
   257  	sizeBuf := make([]byte, 0, 16)
   258  	sizeBuf = binary.AppendUvarint(sizeBuf, uint64(len(buf)))
   259  
   260  	return append(sizeBuf, buf...)
   261  }
   262  
   263  func readContact(body *bufio.Reader) (*fspb.ContactData, error) {
   264  	size, err := binary.ReadUvarint(body)
   265  	if err != nil {
   266  		return nil, err
   267  	}
   268  	buf := make([]byte, size)
   269  	_, err = io.ReadFull(body, buf)
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  	var cd fspb.ContactData
   274  	if err := proto.Unmarshal(buf, &cd); err != nil {
   275  		return nil, err
   276  	}
   277  	return &cd, nil
   278  }
   279  
   280  func TestStreaming(t *testing.T) {
   281  	ctx := context.Background()
   282  	s, _, addr := makeServer(t, "Streaming", nil)
   283  	_, cl, _, _ := makeClient(t, true)
   284  	defer s.Stop()
   285  
   286  	br, bw := io.Pipe()
   287  	go func() {
   288  		// First exchange - these writes must happen during the http.Client.Do call
   289  		// below, because the server writes headers at the end of the first message
   290  		// exchange.
   291  
   292  		// Start with the magic number:
   293  		binary.Write(bw, binary.LittleEndian, magic)
   294  
   295  		if _, err := bw.Write(makeWrapped()); err != nil {
   296  			t.Error(err)
   297  		}
   298  	}()
   299  
   300  	u := url.URL{Scheme: "https", Host: addr, Path: "/streaming-message"}
   301  	req, err := http.NewRequest("POST", u.String(), br)
   302  	req.ContentLength = -1
   303  	req.Close = true
   304  	req.Header.Set("Expect", "100-continue")
   305  	if err != nil {
   306  		t.Fatal(err)
   307  	}
   308  	req = req.WithContext(ctx)
   309  	resp, err := cl.Do(req)
   310  	if err != nil {
   311  		t.Fatalf("Streaming post failed (%v): %v", resp, err)
   312  	}
   313  	// Read ContactData for first exchange.
   314  	body := bufio.NewReader(resp.Body)
   315  	cd, err := readContact(body)
   316  	if err != nil {
   317  		t.Error(err)
   318  	}
   319  	if cd.AckIndex != 0 {
   320  		t.Errorf("AckIndex of initial exchange should be unset, got %d", cd.AckIndex)
   321  	}
   322  
   323  	for i := uint64(1); i < 10; i++ {
   324  		// Write another WrappedContactData.
   325  		if _, err := bw.Write(makeWrapped()); err != nil {
   326  			t.Error(err)
   327  		}
   328  		cd, err := readContact(body)
   329  		if err != nil {
   330  			t.Error(err)
   331  		}
   332  		if cd.AckIndex != i {
   333  			t.Errorf("Received ack for contact %d, but expected %d", cd.AckIndex, i)
   334  		}
   335  	}
   336  
   337  	bw.Close()
   338  	resp.Body.Close()
   339  }
   340  
   341  func TestHeaderNormalPoll(t *testing.T) {
   342  	ctx := context.Background()
   343  	clientCertHeader := "ssl-client-cert"
   344  	frontendConfig := &cpb.FrontendConfig{
   345  		FrontendMode: &cpb.FrontendConfig_HttpsHeaderConfig{
   346  			HttpsHeaderConfig: &cpb.HttpsHeaderConfig{
   347  				ClientCertificateHeader: clientCertHeader,
   348  			},
   349  		},
   350  	}
   351  
   352  	s, ds, addr := makeServer(t, "Normal", frontendConfig)
   353  	id, cl, bc, _ := makeClient(t, true)
   354  	defer s.Stop()
   355  
   356  	u := url.URL{Scheme: "https", Host: addr, Path: "/message"}
   357  
   358  	req, err := http.NewRequest("POST", u.String(), nil)
   359  	req.Close = true
   360  	cc := url.PathEscape(string(bc))
   361  	req.Header.Set(clientCertHeader, cc)
   362  	if err != nil {
   363  		t.Fatal(err)
   364  	}
   365  
   366  	// An empty body is a valid, though atypical initial request.
   367  	req = req.WithContext(ctx)
   368  	resp, err := cl.Do(req)
   369  	if err != nil {
   370  		t.Fatal(err)
   371  	}
   372  
   373  	b, err := io.ReadAll(resp.Body)
   374  	if err != nil {
   375  		t.Error(err)
   376  	}
   377  	resp.Body.Close()
   378  
   379  	var cd fspb.ContactData
   380  	if err := proto.Unmarshal(b, &cd); err != nil {
   381  		t.Errorf("Unable to parse returned data as ContactData: %v", err)
   382  	}
   383  	if cd.SequencingNonce == 0 {
   384  		t.Error("Expected SequencingNonce in returned ContactData")
   385  	}
   386  
   387  	// The client should now exist in the datastore.
   388  	_, err = ds.GetClientData(ctx, id)
   389  	if err != nil {
   390  		t.Errorf("Error getting client data after poll: %v", err)
   391  	}
   392  }
   393  
   394  func TestHeaderStreaming(t *testing.T) {
   395  	ctx := context.Background()
   396  	clientCertHeader := "ssl-client-cert"
   397  	frontendConfig := &cpb.FrontendConfig{
   398  		FrontendMode: &cpb.FrontendConfig_HttpsHeaderConfig{
   399  			HttpsHeaderConfig: &cpb.HttpsHeaderConfig{
   400  				ClientCertificateHeader: clientCertHeader,
   401  			},
   402  		},
   403  	}
   404  
   405  	s, _, addr := makeServer(t, "Streaming", frontendConfig)
   406  	_, cl, bc, _ := makeClient(t, true)
   407  	defer s.Stop()
   408  
   409  	br, bw := io.Pipe()
   410  	go func() {
   411  		// First exchange - these writes must happen during the http.Client.Do call
   412  		// below, because the server writes headers at the end of the first message
   413  		// exchange.
   414  
   415  		// Start with the magic number:
   416  		binary.Write(bw, binary.LittleEndian, magic)
   417  
   418  		if _, err := bw.Write(makeWrapped()); err != nil {
   419  			t.Error(err)
   420  		}
   421  	}()
   422  
   423  	u := url.URL{Scheme: "https", Host: addr, Path: "/streaming-message"}
   424  	req, err := http.NewRequest("POST", u.String(), br)
   425  	req.ContentLength = -1
   426  	req.Close = true
   427  	req.Header.Set("Expect", "100-continue")
   428  
   429  	cc := url.PathEscape(string(bc))
   430  	req.Header.Set(clientCertHeader, cc)
   431  	if err != nil {
   432  		t.Fatal(err)
   433  	}
   434  	req = req.WithContext(ctx)
   435  	resp, err := cl.Do(req)
   436  	if err != nil {
   437  		t.Fatalf("Streaming post failed (%v): %v", resp, err)
   438  	}
   439  	// Read ContactData for first exchange.
   440  	body := bufio.NewReader(resp.Body)
   441  	cd, err := readContact(body)
   442  	if err != nil {
   443  		t.Error(err)
   444  	}
   445  	if cd.AckIndex != 0 {
   446  		t.Errorf("AckIndex of initial exchange should be unset, got %d", cd.AckIndex)
   447  	}
   448  
   449  	for i := uint64(1); i < 10; i++ {
   450  		// Write another WrappedContactData.
   451  		if _, err := bw.Write(makeWrapped()); err != nil {
   452  			t.Error(err)
   453  		}
   454  		cd, err := readContact(body)
   455  		if err != nil {
   456  			t.Error(err)
   457  		}
   458  		if cd.AckIndex != i {
   459  			t.Errorf("Received ack for contact %d, but expected %d", cd.AckIndex, i)
   460  		}
   461  	}
   462  
   463  	bw.Close()
   464  	resp.Body.Close()
   465  }
   466  
   467  func TestHeaderStreamingChecksum(t *testing.T) {
   468  	ctx := context.Background()
   469  	clientCertHeader := "ssl-client-cert"
   470  	clientCertChecksumHeader := "ssl-client-cert-checksum"
   471  	frontendConfig := &cpb.FrontendConfig{
   472  		FrontendMode: &cpb.FrontendConfig_HttpsHeaderChecksumConfig{
   473  			HttpsHeaderChecksumConfig: &cpb.HttpsHeaderChecksumConfig{
   474  				ClientCertificateHeader:         clientCertHeader,
   475  				ClientCertificateChecksumHeader: clientCertChecksumHeader,
   476  			},
   477  		},
   478  	}
   479  
   480  	s, _, addr := makeServer(t, "Streaming", frontendConfig)
   481  	_, cl, bc, fp := makeClient(t, true)
   482  	defer s.Stop()
   483  
   484  	br, bw := io.Pipe()
   485  	go func() {
   486  		// First exchange - these writes must happen during the http.Client.Do call
   487  		// below, because the server writes headers at the end of the first message
   488  		// exchange.
   489  
   490  		// Start with the magic number:
   491  		binary.Write(bw, binary.LittleEndian, magic)
   492  
   493  		if _, err := bw.Write(makeWrapped()); err != nil {
   494  			t.Error(err)
   495  		}
   496  	}()
   497  
   498  	u := url.URL{Scheme: "https", Host: addr, Path: "/streaming-message"}
   499  	req, err := http.NewRequest("POST", u.String(), br)
   500  	if err != nil {
   501  		t.Fatal(err)
   502  	}
   503  	req.ContentLength = -1
   504  	req.Close = true
   505  	req.Header.Set("Expect", "100-continue")
   506  
   507  	cc := url.PathEscape(string(bc))
   508  	req.Header.Set(clientCertHeader, cc)
   509  	req.Header.Set(clientCertChecksumHeader, fp)
   510  	req = req.WithContext(ctx)
   511  	resp, err := cl.Do(req)
   512  	if err != nil {
   513  		t.Fatalf("Streaming post failed (%v): %v", resp, err)
   514  	}
   515  	// Read ContactData for first exchange.
   516  	body := bufio.NewReader(resp.Body)
   517  	cd, err := readContact(body)
   518  	if cd == nil {
   519  		t.Fatalf("Read Contact returned nil: %v", err)
   520  	}
   521  	if err != nil {
   522  		t.Error(err)
   523  	}
   524  	if cd.AckIndex != 0 {
   525  		t.Errorf("AckIndex of initial exchange should be unset, got %d", cd.AckIndex)
   526  	}
   527  
   528  	for i := uint64(1); i < 10; i++ {
   529  		// Write another WrappedContactData.
   530  		if _, err := bw.Write(makeWrapped()); err != nil {
   531  			t.Error(err)
   532  		}
   533  		cd, err := readContact(body)
   534  		if err != nil {
   535  			t.Error(err)
   536  		}
   537  		if cd.AckIndex != i {
   538  			t.Errorf("Received ack for contact %d, but expected %d", cd.AckIndex, i)
   539  		}
   540  	}
   541  
   542  	bw.Close()
   543  	resp.Body.Close()
   544  }
   545  
   546  func TestCleartextHeaderStreaming(t *testing.T) {
   547  	ctx := context.Background()
   548  	clientCertHeader := "ssl-client-cert"
   549  	frontendConfig := &cpb.FrontendConfig{
   550  		FrontendMode: &cpb.FrontendConfig_CleartextHeaderConfig{
   551  			CleartextHeaderConfig: &cpb.CleartextHeaderConfig{
   552  				ClientCertificateHeader: clientCertHeader,
   553  			},
   554  		},
   555  	}
   556  
   557  	s, _, addr := makeServer(t, "Streaming", frontendConfig)
   558  	_, cl, bc, _ := makeClient(t, false)
   559  	defer s.Stop()
   560  
   561  	br, bw := io.Pipe()
   562  	go func() {
   563  		// First exchange - these writes must happen during the http.Client.Do call
   564  		// below, because the server writes headers at the end of the first message
   565  		// exchange.
   566  
   567  		// Start with the magic number:
   568  		binary.Write(bw, binary.LittleEndian, magic)
   569  
   570  		if _, err := bw.Write(makeWrapped()); err != nil {
   571  			t.Error(err)
   572  		}
   573  	}()
   574  
   575  	u := url.URL{Scheme: "http", Host: addr, Path: "/streaming-message"}
   576  	req, err := http.NewRequest("POST", u.String(), br)
   577  	req.ContentLength = -1
   578  	req.Close = true
   579  	req.Header.Set("Expect", "100-continue")
   580  
   581  	cc := url.PathEscape(string(bc))
   582  	req.Header.Set(clientCertHeader, cc)
   583  	if err != nil {
   584  		t.Fatal(err)
   585  	}
   586  	req = req.WithContext(ctx)
   587  	resp, err := cl.Do(req)
   588  	if err != nil {
   589  		t.Fatalf("Streaming post failed (%v): %v", resp, err)
   590  	}
   591  	// Read ContactData for first exchange.
   592  	body := bufio.NewReader(resp.Body)
   593  	cd, err := readContact(body)
   594  	if err != nil {
   595  		t.Error(err)
   596  	}
   597  	if cd.AckIndex != 0 {
   598  		t.Errorf("AckIndex of initial exchange should be unset, got %d", cd.AckIndex)
   599  	}
   600  
   601  	for i := uint64(1); i < 10; i++ {
   602  		// Write another WrappedContactData.
   603  		if _, err := bw.Write(makeWrapped()); err != nil {
   604  			t.Error(err)
   605  		}
   606  		cd, err := readContact(body)
   607  		if err != nil {
   608  			t.Error(err)
   609  		}
   610  		if cd.AckIndex != i {
   611  			t.Errorf("Received ack for contact %d, but expected %d", cd.AckIndex, i)
   612  		}
   613  	}
   614  
   615  	bw.Close()
   616  	resp.Body.Close()
   617  }
   618  
   619  func TestCleartextHeaderStreamingChecksum(t *testing.T) {
   620  	ctx := context.Background()
   621  	clientCertHeader := "ssl-client-cert"
   622  	clientCertChecksumHeader := "ssl-client-cert-checksum"
   623  	frontendConfig := &cpb.FrontendConfig{
   624  		FrontendMode: &cpb.FrontendConfig_CleartextHeaderChecksumConfig{
   625  			CleartextHeaderChecksumConfig: &cpb.CleartextHeaderChecksumConfig{
   626  				ClientCertificateHeader:         clientCertHeader,
   627  				ClientCertificateChecksumHeader: clientCertChecksumHeader,
   628  			},
   629  		},
   630  	}
   631  
   632  	s, _, addr := makeServer(t, "Streaming", frontendConfig)
   633  	_, cl, bc, fp := makeClient(t, false)
   634  	defer s.Stop()
   635  
   636  	br, bw := io.Pipe()
   637  	go func() {
   638  		// First exchange - these writes must happen during the http.Client.Do call
   639  		// below, because the server writes headers at the end of the first message
   640  		// exchange.
   641  
   642  		// Start with the magic number:
   643  		binary.Write(bw, binary.LittleEndian, magic)
   644  
   645  		if _, err := bw.Write(makeWrapped()); err != nil {
   646  			t.Error(err)
   647  		}
   648  	}()
   649  
   650  	u := url.URL{Scheme: "http", Host: addr, Path: "/streaming-message"}
   651  	req, err := http.NewRequest("POST", u.String(), br)
   652  	if err != nil {
   653  		t.Fatal(err)
   654  	}
   655  	req.ContentLength = -1
   656  	req.Close = true
   657  	req.Header.Set("Expect", "100-continue")
   658  
   659  	cc := url.PathEscape(string(bc))
   660  	req.Header.Set(clientCertHeader, cc)
   661  	req.Header.Set(clientCertChecksumHeader, fp)
   662  	req = req.WithContext(ctx)
   663  	resp, err := cl.Do(req)
   664  	if err != nil {
   665  		t.Fatalf("Streaming post failed (%v): %v", resp, err)
   666  	}
   667  	// Read ContactData for first exchange.
   668  	body := bufio.NewReader(resp.Body)
   669  	cd, err := readContact(body)
   670  	if cd == nil {
   671  		t.Fatalf("Read Contact returned nil: %v", err)
   672  	}
   673  	if err != nil {
   674  		t.Error(err)
   675  	}
   676  	if cd.AckIndex != 0 {
   677  		t.Errorf("AckIndex of initial exchange should be unset, got %d", cd.AckIndex)
   678  	}
   679  
   680  	for i := uint64(1); i < 10; i++ {
   681  		// Write another WrappedContactData.
   682  		if _, err := bw.Write(makeWrapped()); err != nil {
   683  			t.Error(err)
   684  		}
   685  		cd, err := readContact(body)
   686  		if err != nil {
   687  			t.Error(err)
   688  		}
   689  		if cd.AckIndex != i {
   690  			t.Errorf("Received ack for contact %d, but expected %d", cd.AckIndex, i)
   691  		}
   692  	}
   693  
   694  	bw.Close()
   695  	resp.Body.Close()
   696  }
   697  
   698  func TestCleartextXfccStreaming(t *testing.T) {
   699  	ctx := context.Background()
   700  	clientCertHeader := "ssl-client-cert"
   701  	frontendConfig := &cpb.FrontendConfig{
   702  		FrontendMode: &cpb.FrontendConfig_CleartextXfccConfig{
   703  			CleartextXfccConfig: &cpb.CleartextXfccConfig{
   704  				ClientCertificateHeader: clientCertHeader,
   705  			},
   706  		},
   707  	}
   708  
   709  	s, _, addr := makeServer(t, "Streaming", frontendConfig)
   710  	_, cl, bc, _ := makeClient(t, false)
   711  	defer s.Stop()
   712  
   713  	br, bw := io.Pipe()
   714  	go func() {
   715  		// First exchange - these writes must happen during the http.Client.Do call
   716  		// below, because the server writes headers at the end of the first message
   717  		// exchange.
   718  
   719  		// Start with the magic number:
   720  		binary.Write(bw, binary.LittleEndian, magic)
   721  
   722  		if _, err := bw.Write(makeWrapped()); err != nil {
   723  			t.Error(err)
   724  		}
   725  	}()
   726  
   727  	u := url.URL{Scheme: "http", Host: addr, Path: "/streaming-message"}
   728  	req, err := http.NewRequest("POST", u.String(), br)
   729  	if err != nil {
   730  		t.Fatal(err)
   731  	}
   732  	req.ContentLength = -1
   733  	req.Close = true
   734  	req.Header.Set("Expect", "100-continue")
   735  
   736  	cc := url.PathEscape(string(bc))
   737  	req.Header.Set(clientCertHeader, `Cert=`+cc)
   738  	req = req.WithContext(ctx)
   739  	resp, err := cl.Do(req)
   740  	if err != nil {
   741  		t.Fatalf("Streaming post failed (%v): %v", resp, err)
   742  	}
   743  	// Read ContactData for first exchange.
   744  	body := bufio.NewReader(resp.Body)
   745  	cd, err := readContact(body)
   746  	if cd == nil {
   747  		t.Fatalf("Read Contact returned nil: %v", err)
   748  	}
   749  	if err != nil {
   750  		t.Error(err)
   751  	}
   752  	if cd.AckIndex != 0 {
   753  		t.Errorf("AckIndex of initial exchange should be unset, got %d", cd.AckIndex)
   754  	}
   755  
   756  	for i := uint64(1); i < 10; i++ {
   757  		// Write another WrappedContactData.
   758  		if _, err := bw.Write(makeWrapped()); err != nil {
   759  			t.Error(err)
   760  		}
   761  		cd, err := readContact(body)
   762  		if err != nil {
   763  			t.Error(err)
   764  		}
   765  		if cd.AckIndex != i {
   766  			t.Errorf("Received ack for contact %d, but expected %d", cd.AckIndex, i)
   767  		}
   768  	}
   769  
   770  	bw.Close()
   771  	resp.Body.Close()
   772  }