...

Source file src/github.com/letsencrypt/boulder/akamai/cache-client_test.go

Documentation: github.com/letsencrypt/boulder/akamai

     1  package akamai
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/jmhodges/clock"
    14  	blog "github.com/letsencrypt/boulder/log"
    15  	"github.com/letsencrypt/boulder/metrics"
    16  	"github.com/letsencrypt/boulder/test"
    17  )
    18  
    19  func TestMakeAuthHeader(t *testing.T) {
    20  	log := blog.NewMock()
    21  	stats := metrics.NoopRegisterer
    22  	cpc, err := NewCachePurgeClient(
    23  		"https://akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net",
    24  		"akab-client-token-xxx-xxxxxxxxxxxxxxxx",
    25  		"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=",
    26  		"akab-access-token-xxx-xxxxxxxxxxxxxxxx",
    27  		"production",
    28  		2,
    29  		time.Second,
    30  		log,
    31  		stats,
    32  	)
    33  	test.AssertNotError(t, err, "Failed to create cache purge client")
    34  	fc := clock.NewFake()
    35  	cpc.clk = fc
    36  	wantedTimestamp, err := time.Parse(timestampFormat, "20140321T19:34:21+0000")
    37  	test.AssertNotError(t, err, "Failed to parse timestamp")
    38  	fc.Set(wantedTimestamp)
    39  
    40  	expectedHeader := "EG1-HMAC-SHA256 client_token=akab-client-token-xxx-xxxxxxxxxxxxxxxx;access_token=akab-access-token-xxx-xxxxxxxxxxxxxxxx;timestamp=20140321T19:34:21+0000;nonce=nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx;signature=hXm4iCxtpN22m4cbZb4lVLW5rhX8Ca82vCFqXzSTPe4="
    41  	authHeader := cpc.makeAuthHeader(
    42  		[]byte("datadatadatadatadatadatadatadata"),
    43  		"/testapi/v1/t3",
    44  		"nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    45  	)
    46  	test.AssertEquals(t, authHeader, expectedHeader)
    47  }
    48  
    49  type akamaiServer struct {
    50  	responseCode int
    51  	*httptest.Server
    52  }
    53  
    54  func (as *akamaiServer) sendResponse(w http.ResponseWriter, resp purgeResponse) {
    55  	respBytes, err := json.Marshal(resp)
    56  	if err != nil {
    57  		fmt.Printf("Failed to marshal response body: %s\n", err)
    58  		w.WriteHeader(http.StatusInternalServerError)
    59  		return
    60  	}
    61  	w.WriteHeader(as.responseCode)
    62  	w.Write(respBytes)
    63  }
    64  
    65  func (as *akamaiServer) purgeHandler(w http.ResponseWriter, r *http.Request) {
    66  	var req struct {
    67  		Objects []string
    68  	}
    69  	body, err := io.ReadAll(r.Body)
    70  	if err != nil {
    71  		fmt.Printf("Failed to read request body: %s\n", err)
    72  		w.WriteHeader(http.StatusInternalServerError)
    73  		return
    74  	}
    75  
    76  	err = CheckSignature("secret", as.URL, r, body)
    77  	if err != nil {
    78  		fmt.Printf("Error checking signature: %s\n", err)
    79  		w.WriteHeader(http.StatusInternalServerError)
    80  		return
    81  	}
    82  
    83  	err = json.Unmarshal(body, &req)
    84  	if err != nil {
    85  		fmt.Printf("Failed to unmarshal request body: %s\n", err)
    86  		w.WriteHeader(http.StatusInternalServerError)
    87  		return
    88  	}
    89  
    90  	resp := purgeResponse{
    91  		HTTPStatus:       as.responseCode,
    92  		Detail:           "?",
    93  		EstimatedSeconds: 10,
    94  		PurgeID:          "?",
    95  	}
    96  
    97  	fmt.Println(r.URL.Path, v3PurgePath)
    98  	if strings.HasPrefix(r.URL.Path, v3PurgePath) {
    99  		for _, testURL := range req.Objects {
   100  			if !strings.HasPrefix(testURL, "http://") {
   101  				resp.HTTPStatus = http.StatusForbidden
   102  				break
   103  			}
   104  		}
   105  	}
   106  	as.sendResponse(w, resp)
   107  }
   108  func newAkamaiServer(code int) *akamaiServer {
   109  	m := http.NewServeMux()
   110  	as := akamaiServer{
   111  		responseCode: code,
   112  		Server:       httptest.NewServer(m),
   113  	}
   114  	m.HandleFunc(v3PurgePath, as.purgeHandler)
   115  	m.HandleFunc(v3PurgeTagPath, as.purgeHandler)
   116  	return &as
   117  }
   118  
   119  // TestV3Purge tests the Akamai CCU v3 purge API
   120  func TestV3Purge(t *testing.T) {
   121  	as := newAkamaiServer(http.StatusCreated)
   122  	defer as.Close()
   123  
   124  	// Client is a purge client with a "production" v3Network parameter
   125  	client, err := NewCachePurgeClient(
   126  		as.URL,
   127  		"token",
   128  		"secret",
   129  		"accessToken",
   130  		"production",
   131  		3,
   132  		time.Second,
   133  		blog.NewMock(),
   134  		metrics.NoopRegisterer,
   135  	)
   136  	test.AssertNotError(t, err, "Failed to create CachePurgeClient")
   137  	client.clk = clock.NewFake()
   138  
   139  	err = client.Purge([]string{"http://test.com"})
   140  	test.AssertNotError(t, err, "Purge failed; expected 201 response")
   141  
   142  	started := client.clk.Now()
   143  	as.responseCode = http.StatusInternalServerError
   144  	err = client.Purge([]string{"http://test.com"})
   145  	test.AssertError(t, err, "Purge succeeded; expected 500 response")
   146  	t.Log(client.clk.Since(started))
   147  	// Given 3 retries, with a retry interval of 1 second, a growth factor of 1.3,
   148  	// and a jitter of 0.2, the minimum amount of elapsed time is:
   149  	// (1 * 0.8) + (1 * 1.3 * 0.8) + (1 * 1.3 * 1.3 * 0.8) = 3.192s
   150  	test.Assert(t, client.clk.Since(started) > (time.Second*3), "Retries should've taken at least 3.192 seconds")
   151  
   152  	started = client.clk.Now()
   153  	as.responseCode = http.StatusCreated
   154  	err = client.Purge([]string{"http:/test.com"})
   155  	test.AssertError(t, err, "Purge succeeded; expected a 403 response from malformed URL")
   156  	test.Assert(t, client.clk.Since(started) < time.Second, "Purge should've failed out immediately")
   157  }
   158  
   159  func TestPurgeTags(t *testing.T) {
   160  	as := newAkamaiServer(http.StatusCreated)
   161  	defer as.Close()
   162  
   163  	// Client is a purge client with a "production" v3Network parameter
   164  	client, err := NewCachePurgeClient(
   165  		as.URL,
   166  		"token",
   167  		"secret",
   168  		"accessToken",
   169  		"production",
   170  		3,
   171  		time.Second,
   172  		blog.NewMock(),
   173  		metrics.NoopRegisterer,
   174  	)
   175  	test.AssertNotError(t, err, "Failed to create CachePurgeClient")
   176  	fc := clock.NewFake()
   177  	client.clk = fc
   178  
   179  	err = client.PurgeTags([]string{"ff"})
   180  	test.AssertNotError(t, err, "Purge failed; expected response 201")
   181  
   182  	as.responseCode = http.StatusForbidden
   183  	err = client.PurgeTags([]string{"http://test.com"})
   184  	test.AssertError(t, err, "Purge succeeded; expected Forbidden response")
   185  }
   186  
   187  func TestNewCachePurgeClient(t *testing.T) {
   188  	// Creating a new cache purge client with an invalid "network" parameter should error
   189  	_, err := NewCachePurgeClient(
   190  		"http://127.0.0.1:9000/",
   191  		"token",
   192  		"secret",
   193  		"accessToken",
   194  		"fake",
   195  		3,
   196  		time.Second,
   197  		blog.NewMock(),
   198  		metrics.NoopRegisterer,
   199  	)
   200  	test.AssertError(t, err, "NewCachePurgeClient with invalid network parameter didn't error")
   201  
   202  	// Creating a new cache purge client with a valid "network" parameter shouldn't error
   203  	_, err = NewCachePurgeClient(
   204  		"http://127.0.0.1:9000/",
   205  		"token",
   206  		"secret",
   207  		"accessToken",
   208  		"staging",
   209  		3,
   210  		time.Second,
   211  		blog.NewMock(),
   212  		metrics.NoopRegisterer,
   213  	)
   214  	test.AssertNotError(t, err, "NewCachePurgeClient with valid network parameter errored")
   215  
   216  	// Creating a new cache purge client with an invalid server URL parameter should error
   217  	_, err = NewCachePurgeClient(
   218  		"h&amp;ttp://whatever",
   219  		"token",
   220  		"secret",
   221  		"accessToken",
   222  		"staging",
   223  		3,
   224  		time.Second,
   225  		blog.NewMock(),
   226  		metrics.NoopRegisterer,
   227  	)
   228  	test.AssertError(t, err, "NewCachePurgeClient with invalid server url parameter didn't error")
   229  }
   230  
   231  func TestBigBatchPurge(t *testing.T) {
   232  	log := blog.NewMock()
   233  
   234  	as := newAkamaiServer(http.StatusCreated)
   235  
   236  	client, err := NewCachePurgeClient(
   237  		as.URL,
   238  		"token",
   239  		"secret",
   240  		"accessToken",
   241  		"production",
   242  		3,
   243  		time.Second,
   244  		log,
   245  		metrics.NoopRegisterer,
   246  	)
   247  	test.AssertNotError(t, err, "Failed to create CachePurgeClient")
   248  
   249  	var urls []string
   250  	for i := 0; i < 250; i++ {
   251  		urls = append(urls, fmt.Sprintf("http://test.com/%d", i))
   252  	}
   253  
   254  	err = client.Purge(urls)
   255  	test.AssertNotError(t, err, "Purge failed.")
   256  }
   257  
   258  func TestReverseBytes(t *testing.T) {
   259  	a := []byte{0, 1, 2, 3}
   260  	test.AssertDeepEquals(t, reverseBytes(a), []byte{3, 2, 1, 0})
   261  }
   262  
   263  func TestGenerateOCSPCacheKeys(t *testing.T) {
   264  	der := []byte{105, 239, 255}
   265  	test.AssertDeepEquals(
   266  		t,
   267  		makeOCSPCacheURLs(der, "ocsp.invalid/"),
   268  		[]string{
   269  			"ocsp.invalid/?body-md5=d6101198a9d9f1f6",
   270  			"ocsp.invalid/ae/",
   271  			"ocsp.invalid/ae%2F%2F",
   272  		},
   273  	)
   274  }
   275  

View as plain text