11/* 
2- Copyright 2021 The Kubernetes Authors. 
2+     Copyright 2021 The Kubernetes Authors.
33
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 
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
77
8-      http://www.apache.org/licenses/LICENSE-2.0
8+ http://www.apache.org/licenses/LICENSE-2.0 
99
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. 
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.
1515*/ 
1616
1717package  handler3
@@ -23,13 +23,12 @@ import (
2323	"net/http/httptest" 
2424	"reflect" 
2525	"testing" 
26+ 	"time" 
2627
2728	"encoding/json" 
2829	"k8s.io/kube-openapi/pkg/spec3" 
2930)
3031
31- var  returnedGroupVersionListJSON  =  []byte (`{"Paths":["apis/apps/v1"]}` )
32- 
3332var  returnedOpenAPI  =  []byte (`{ 
3433  "openapi": "3.0", 
3534  "info": { 
@@ -45,6 +44,9 @@ func TestRegisterOpenAPIVersionedService(t *testing.T) {
4544		t .Errorf ("%v" , err )
4645	}
4746	compactOpenAPI  :=  buffer .Bytes ()
47+ 	var  hash  =  unquoteETag (computeETag (compactOpenAPI ))
48+ 
49+ 	var  returnedGroupVersionListJSON  =  []byte (`{"Paths":{"apis/apps/v1":"openapi/v3/apis/apps/v1?hash=`  +  hash  +  `"}}` )
4850
4951	json .Unmarshal (compactOpenAPI , & s )
5052
@@ -54,7 +56,6 @@ func TestRegisterOpenAPIVersionedService(t *testing.T) {
5456	}
5557
5658	returnedPb , err  :=  ToV3ProtoBinary (compactOpenAPI )
57- 	_  =  returnedPb 
5859
5960	if  err  !=  nil  {
6061		t .Fatalf ("Unexpected error in preparing returnedPb: %v" , err )
@@ -120,3 +121,153 @@ func TestRegisterOpenAPIVersionedService(t *testing.T) {
120121		}
121122	}
122123}
124+ 
125+ func  TestCacheBusting (t  * testing.T ) {
126+ 	var  s  * spec3.OpenAPI 
127+ 	buffer  :=  new (bytes.Buffer )
128+ 	if  err  :=  json .Compact (buffer , returnedOpenAPI ); err  !=  nil  {
129+ 		t .Errorf ("%v" , err )
130+ 	}
131+ 	compactOpenAPI  :=  buffer .Bytes ()
132+ 	var  hash  =  unquoteETag (computeETag (compactOpenAPI ))
133+ 
134+ 	json .Unmarshal (compactOpenAPI , & s )
135+ 
136+ 	returnedJSON , err  :=  json .Marshal (s )
137+ 	if  err  !=  nil  {
138+ 		t .Fatalf ("Unexpected error in preparing returnedJSON: %v" , err )
139+ 	}
140+ 
141+ 	returnedPb , err  :=  ToV3ProtoBinary (compactOpenAPI )
142+ 
143+ 	if  err  !=  nil  {
144+ 		t .Fatalf ("Unexpected error in preparing returnedPb: %v" , err )
145+ 	}
146+ 
147+ 	mux  :=  http .NewServeMux ()
148+ 	o , err  :=  NewOpenAPIService (nil )
149+ 	if  err  !=  nil  {
150+ 		t .Fatal (err )
151+ 	}
152+ 
153+ 	mux .Handle ("/openapi/v3" , http .HandlerFunc (o .HandleDiscovery ))
154+ 	mux .Handle ("/openapi/v3/apis/apps/v1" , http .HandlerFunc (o .HandleGroupVersion ))
155+ 
156+ 	o .UpdateGroupVersion ("apis/apps/v1" , s )
157+ 
158+ 	server  :=  httptest .NewServer (mux )
159+ 	defer  server .Close ()
160+ 	client  :=  server .Client ()
161+ 
162+ 	tcs  :=  []struct  {
163+ 		acceptHeader  string 
164+ 		respStatus    int 
165+ 		urlPath       string 
166+ 		respBody      []byte 
167+ 		expectedHash  string 
168+ 		cacheControl  string 
169+ 	}{
170+ 		// Correct hash should yield the proper expiry and Cache Control headers 
171+ 		{"application/json" ,
172+ 			200 ,
173+ 			"openapi/v3/apis/apps/v1?hash="  +  hash ,
174+ 			returnedJSON ,
175+ 			hash ,
176+ 			"public, immutable" ,
177+ 		},
178+ 		{
"application/[email protected] +protobuf" ,
179+ 			200 ,
180+ 			"openapi/v3/apis/apps/v1?hash="  +  hash ,
181+ 			returnedPb ,
182+ 			hash ,
183+ 			"public, immutable" ,
184+ 		},
185+ 		// Incorrect hash should redirect to the page with the correct hash 
186+ 		{"application/json" ,
187+ 			200 ,
188+ 			"openapi/v3/apis/apps/v1?hash="  +  "OUTDATEDHASH" ,
189+ 			returnedJSON ,
190+ 			hash ,
191+ 			"public, immutable" ,
192+ 		},
193+ 		{
"application/[email protected] +protobuf" ,
194+ 			200 ,
195+ 			"openapi/v3/apis/apps/v1?hash="  +  "OUTDATEDHASH" ,
196+ 			returnedPb ,
197+ 			hash ,
198+ 			"public, immutable" ,
199+ 		},
200+ 
201+ 		// No hash should not return Cache Control information 
202+ 		{"application/json" ,
203+ 			200 ,
204+ 			"openapi/v3/apis/apps/v1" ,
205+ 			returnedJSON ,
206+ 			"" ,
207+ 			"" ,
208+ 		},
209+ 		{
"application/[email protected] +protobuf" ,
210+ 			200 ,
211+ 			"openapi/v3/apis/apps/v1" ,
212+ 			returnedPb ,
213+ 			"" ,
214+ 			"" ,
215+ 		},
216+ 	}
217+ 
218+ 	for  _ , tc  :=  range  tcs  {
219+ 		req , err  :=  http .NewRequest ("GET" , server .URL + "/" + tc .urlPath , nil )
220+ 		if  err  !=  nil  {
221+ 			t .Errorf ("Accept: %v: Unexpected error in creating new request: %v" , tc .acceptHeader , err )
222+ 		}
223+ 
224+ 		req .Header .Add ("Accept" , tc .acceptHeader )
225+ 		resp , err  :=  client .Do (req )
226+ 		if  err  !=  nil  {
227+ 			t .Errorf ("Accept: %v: Unexpected error in serving HTTP request: %v" , tc .acceptHeader , err )
228+ 		}
229+ 
230+ 		if  resp .StatusCode  !=  200  {
231+ 			t .Errorf ("Accept: Unexpected response status code, want: %v, got: %v" , 200 , resp .StatusCode )
232+ 		}
233+ 
234+ 		if  cacheControl  :=  resp .Header .Get ("Cache-Control" ); cacheControl  !=  tc .cacheControl  {
235+ 			t .Errorf ("Expected Cache Control %v, got %v" , tc .cacheControl , cacheControl )
236+ 		}
237+ 
238+ 		if  tc .expectedHash  !=  ""  {
239+ 			if  hash  :=  resp .Request .URL .Query ()["hash" ]; hash [0 ] !=  tc .expectedHash  {
240+ 				t .Errorf ("Expected Hash: %s, got %s" , tc .expectedHash , hash [0 ])
241+ 			}
242+ 
243+ 			expires  :=  resp .Header .Get ("Expires" )
244+ 			parsedTime , err  :=  time .Parse (time .RFC1123 , expires )
245+ 			if  err  !=  nil  {
246+ 				t .Errorf ("Could not parse cache expiry %v" , expires )
247+ 			}
248+ 
249+ 			difference  :=  parsedTime .Sub (time .Now ()).Hours ()
250+ 			if  difference  <=  0  {
251+ 				t .Errorf ("Expected cache expiry to be in the future" )
252+ 			}
253+ 		} else  {
254+ 			hash  :=  resp .Request .URL .Query ()["hash" ]
255+ 			if  len (hash ) !=  0  {
256+ 				t .Errorf ("Expect no redirect and empty hash if the hash is not provide" )
257+ 			}
258+ 			expires  :=  resp .Header .Get ("Expires" )
259+ 			if  expires  !=  ""  {
260+ 				t .Errorf ("Expected an empty Expiry if hash is not provided,  got %v" , expires )
261+ 			}
262+ 		}
263+ 
264+ 		defer  resp .Body .Close ()
265+ 		body , err  :=  ioutil .ReadAll (resp .Body )
266+ 		if  err  !=  nil  {
267+ 			t .Errorf ("Accept: %v: Unexpected error in reading response body: %v" , tc .acceptHeader , err )
268+ 		}
269+ 		if  ! reflect .DeepEqual (body , tc .respBody ) {
270+ 			t .Errorf ("Accept: %v: Response body mismatches, \n want: %s, \n got:  %s" , tc .acceptHeader , string (tc .respBody ), string (body ))
271+ 		}
272+ 	}
273+ }
0 commit comments