Skip to content
This repository was archived by the owner on Jun 11, 2024. It is now read-only.

Commit bf5666b

Browse files
committed
tests for unbalanced entries
1 parent 6033e15 commit bf5666b

File tree

3 files changed

+317
-2
lines changed

3 files changed

+317
-2
lines changed

browserup-proxy-core/src/main/java/com/browserup/harreader/model/HarResponse.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class HarResponse {
1919

2020
private HttpStatus status;
2121
private String statusText = "";
22-
private String httpVersion = "HTTP/1.1";
22+
private String httpVersion = "unknown";
2323
private List<HarCookie> cookies;
2424
private List<HarHeader> headers;
2525
private HarContent content;

browserup-proxy-core/src/main/resources/mitmproxy/har_dump.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ def on_get_har(self, req, resp):
9999

100100
har_file = self.harDumpAddOn.save_har(filtered_har)
101101

102-
self.harDumpAddOn.mark_har_entries_submitted(har)
102+
if clean_har:
103+
self.harDumpAddOn.mark_har_entries_submitted(har)
103104

104105
resp.status = falcon.HTTP_200
105106
resp.content_type = falcon.MEDIA_JSON
Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
/*
2+
* Modifications Copyright (c) 2019 BrowserUp, Inc.
3+
*/
4+
5+
package com.browserup.bup.mitmproxy
6+
7+
import com.browserup.bup.MitmProxyServer
8+
import com.browserup.bup.proxy.test.util.MockServerTest
9+
import com.browserup.harreader.model.HarRequest
10+
import com.browserup.harreader.model.HarResponse
11+
import org.apache.http.client.methods.CloseableHttpResponse
12+
import org.apache.http.client.methods.HttpGet
13+
import org.awaitility.Awaitility
14+
import org.junit.After
15+
import org.junit.Test
16+
17+
import java.util.concurrent.TimeUnit
18+
import java.util.concurrent.atomic.AtomicBoolean
19+
20+
import static com.browserup.bup.proxy.test.util.NewProxyServerTestUtil.getNewHttpClient
21+
import static com.github.tomakehurst.wiremock.client.WireMock.*
22+
import static java.util.concurrent.Executors.newSingleThreadExecutor
23+
import static org.awaitility.Awaitility.await
24+
import static org.hamcrest.MatcherAssert.assertThat
25+
import static org.hamcrest.Matchers.empty
26+
import static org.hamcrest.Matchers.not
27+
import static org.junit.Assert.assertEquals
28+
import static org.junit.Assert.assertNotEquals
29+
/**
30+
* These tests use mocked server with long delay to verify behavior of 'unbalanced' har entries, where there might be request-only, response-only or HAR entry with both request and response, depending on when 'clean har' is called during request/response/reporting process.
31+
*/
32+
class UnbalancedHarEntriesTest extends MockServerTest {
33+
private static final def DEFAULT_HAR_RESPONSE = new HarResponse()
34+
private static final def DEFAULT_HAR_REQUEST = new HarRequest()
35+
36+
private MitmProxyServer proxy
37+
38+
@After
39+
void tearDown() {
40+
if (proxy?.started) {
41+
proxy.abort()
42+
}
43+
}
44+
45+
@Test
46+
void testResponseOnlyHarEntryReceivedIfNoResponseYet() {
47+
//GIVEN
48+
def stubUrl = "/testResponseTimeoutCapturedInHar"
49+
def targetServerDelaySec = 5
50+
def targetServiceResponseCode = 200
51+
def idleConnectionTimeout = targetServerDelaySec + 1
52+
53+
configureMockServer(stubUrl, targetServerDelaySec, targetServiceResponseCode)
54+
55+
proxy = startProxyAndCreateHar(idleConnectionTimeout)
56+
57+
def requestUrl = "http://localhost:${mockServerPort}$stubUrl".toString()
58+
59+
//WHEN
60+
sendRequestToMockServer(requestUrl, targetServiceResponseCode)
61+
62+
// Let request to be sent and captured
63+
sleep(500)
64+
65+
def har = proxy.getHar()
66+
67+
//THEN
68+
assertThat("Expected to find entries in the HAR", har.getLog().getEntries(), not(empty()))
69+
70+
String capturedUrl = har.log.entries[0].request.url
71+
assertEquals("URL captured in HAR did not match request URL", requestUrl, capturedUrl)
72+
assertEquals("Expected response to be default", DEFAULT_HAR_RESPONSE, har.log.entries.first().response)
73+
}
74+
75+
@Test
76+
void testGetRequestOnlyEntryAndVerifyPopulatedEntry() {
77+
//GIVEN
78+
def stubUrl = "/testResponseTimeoutCapturedInHar"
79+
def targetServerDelaySec = 5
80+
def targetServiceResponseCode = 200
81+
def idleConnectionTimeout = targetServerDelaySec + 1
82+
83+
configureMockServer(stubUrl, targetServerDelaySec, 200)
84+
85+
proxy = startProxyAndCreateHar(idleConnectionTimeout)
86+
87+
def requestUrl = "http://localhost:${mockServerPort}$stubUrl".toString()
88+
89+
//WHEN
90+
def responseReceived = sendRequestToMockServer(requestUrl, targetServiceResponseCode)
91+
92+
// Let request to be sent and captured
93+
sleep(500)
94+
95+
//THEN
96+
// Verify we got request-only har entry
97+
def har = proxy.getHar()
98+
def harEntry = har.log.entries.first()
99+
def capturedUrl = harEntry.request.url
100+
assertEquals("URL captured in HAR did not match request URL", requestUrl, capturedUrl)
101+
assertEquals("Expected response to be default", DEFAULT_HAR_RESPONSE, harEntry.response)
102+
103+
// Wait until response it received
104+
await().atMost(targetServerDelaySec + 1, TimeUnit.SECONDS).until({ responseReceived.get() })
105+
106+
// Verify we got response-only har entry
107+
har = proxy.getHar()
108+
harEntry = har.log.entries.first()
109+
assertNotEquals("Expected request to be not default", DEFAULT_HAR_REQUEST, harEntry.request)
110+
assertEquals("URL captured in HAR did not match request URL", requestUrl, capturedUrl)
111+
assertNotEquals("Expected response to be not defualt", DEFAULT_HAR_RESPONSE, harEntry.response)
112+
assertEquals("Expected response status to be $targetServiceResponseCode",
113+
harEntry.response.status, targetServiceResponseCode)
114+
assertNotEquals("Expected response http version to be populated",
115+
harEntry.response.httpVersion, DEFAULT_HAR_RESPONSE.httpVersion)
116+
}
117+
118+
@Test
119+
void testMultipleRequestsAndForSlowOneWeGetOnlyResponseOnlyEntry() {
120+
//GIVEN
121+
def stubUrl = "/testResponseTimeoutCapturedInHar"
122+
def targetServerDelaySec = 5
123+
def targetServiceResponseCode = 200
124+
def idleConnectionTimeoutSec = targetServerDelaySec + 1
125+
126+
configureMockServer(stubUrl, targetServerDelaySec, targetServiceResponseCode)
127+
128+
proxy = startProxyAndCreateHar(idleConnectionTimeoutSec)
129+
130+
def requestUrl = "http://localhost:${mockServerPort}$stubUrl".toString()
131+
132+
def otherRequests = [
133+
'https://browserup.com/wp-content/themes/browserup/images/logo-text-475x93.png?1',
134+
'https://browserup.com/wp-content/themes/browserup/images/logo-text-475x93.png?2',
135+
'https://browserup.com/wp-content/themes/browserup/images/logo-text-475x93.png?3'
136+
]
137+
138+
//WHEN
139+
def responseReceived = sendRequestToMockServer(requestUrl, targetServiceResponseCode)
140+
def responsesReceived = new ArrayList<AtomicBoolean>()
141+
otherRequests.each {
142+
responsesReceived.add(sendRequestToMockServer(it, null))
143+
}
144+
def totalNumberOfRequests = otherRequests.size() + 1
145+
146+
// Wait for 'other' requests
147+
await().atMost(7, TimeUnit.SECONDS).until {
148+
!responsesReceived.any { !it.get()}
149+
}
150+
151+
//THEN
152+
// Verify we got request-only har entry for mocked server
153+
def har = proxy.getHar()
154+
assertEquals("Expected to get correct number of entries", totalNumberOfRequests, har.log.entries.size())
155+
156+
def harEntryForMockedServer = har.log.entries.find { it.request.url.contains("localhost") }
157+
def capturedUrl = harEntryForMockedServer.request.url
158+
assertEquals("URL captured in HAR did not match request URL", requestUrl, capturedUrl)
159+
assertEquals("Expected response to be default", DEFAULT_HAR_RESPONSE, harEntryForMockedServer.response)
160+
161+
// Wait until response it received
162+
await().atMost(targetServerDelaySec + 1, TimeUnit.SECONDS).until({ responseReceived.get() })
163+
164+
// Verify this time har entry for mocked server contains both request and response
165+
har = proxy.getHar()
166+
assertEquals("Expected to get correct number of entries", totalNumberOfRequests, har.log.entries.size())
167+
168+
harEntryForMockedServer = har.log.entries.find { it.request.url.contains("localhost") }
169+
assertNotEquals("Expected request to be not default", DEFAULT_HAR_REQUEST, harEntryForMockedServer.request)
170+
assertEquals("URL captured in HAR did not match request URL", requestUrl, harEntryForMockedServer.request.url)
171+
assertNotEquals("Expected response to be not defualt", DEFAULT_HAR_RESPONSE, harEntryForMockedServer.response)
172+
assertEquals("Got unexpected response status",
173+
harEntryForMockedServer.response.status, targetServiceResponseCode)
174+
assertNotEquals("Expected response http version to be populated",
175+
harEntryForMockedServer.response.httpVersion, DEFAULT_HAR_RESPONSE.httpVersion)
176+
}
177+
178+
@Test
179+
void testMultipleRequestsAndAfterCleanHarWeGetOnlyOneResponseOnlyEntry() {
180+
//GIVEN
181+
def stubUrl = "/testResponseTimeoutCapturedInHar"
182+
def targetServerDelaySec = 5
183+
def targetServiceResponseCode = 200
184+
def idleConnectionTimeoutSec = targetServerDelaySec + 1
185+
186+
configureMockServer(stubUrl, targetServerDelaySec, targetServiceResponseCode)
187+
188+
proxy = startProxyAndCreateHar(idleConnectionTimeoutSec)
189+
190+
def requestUrl = "http://localhost:${mockServerPort}$stubUrl".toString()
191+
192+
def otherRequests = [
193+
'https://browserup.com/wp-content/themes/browserup/images/logo-text-475x93.png?1',
194+
'https://browserup.com/wp-content/themes/browserup/images/logo-text-475x93.png?2',
195+
'https://browserup.com/wp-content/themes/browserup/images/logo-text-475x93.png?3'
196+
]
197+
198+
//WHEN
199+
def responseReceived = sendRequestToMockServer(requestUrl, targetServiceResponseCode)
200+
def responsesReceived = new ArrayList<AtomicBoolean>()
201+
otherRequests.each {
202+
responsesReceived.add(sendRequestToMockServer(it, null))
203+
}
204+
def totalNumberOfRequests = otherRequests.size() + 1
205+
206+
// Wait for 'other' requests
207+
await().atMost(7, TimeUnit.SECONDS).until {
208+
!responsesReceived.any { !it.get()}
209+
}
210+
211+
//THEN
212+
// Verify we got request-only har entry for mocked server and clean har
213+
def har = proxy.getHar(true)
214+
assertEquals("Expected to get correct number of entries", totalNumberOfRequests, har.log.entries.size())
215+
216+
def harEntryForMockedServer = har.log.entries.find { it.request.url.contains("localhost") }
217+
def capturedUrl = harEntryForMockedServer.request.url
218+
assertEquals("URL captured in HAR did not match request URL", requestUrl, capturedUrl)
219+
assertEquals("Expected response to be default", DEFAULT_HAR_RESPONSE, harEntryForMockedServer.response)
220+
221+
// Wait until response it received
222+
await().atMost(targetServerDelaySec + 1, TimeUnit.SECONDS).until({ responseReceived.get() })
223+
224+
// Verify this time har entry for mocked server contains both request and response
225+
har = proxy.getHar()
226+
assertEquals("Expected to get only one entry for slow request after clean har", 1, har.log.entries.size())
227+
228+
harEntryForMockedServer = har.log.entries.first()
229+
assertEquals("Expected request to be default", DEFAULT_HAR_REQUEST, harEntryForMockedServer.request)
230+
assertNotEquals("Expected response to be not defualt", DEFAULT_HAR_RESPONSE, harEntryForMockedServer.response)
231+
assertEquals("Got unexpected response status",
232+
harEntryForMockedServer.response.status, targetServiceResponseCode)
233+
assertNotEquals("Expected response http version to be populated",
234+
harEntryForMockedServer.response.httpVersion, DEFAULT_HAR_RESPONSE.httpVersion)
235+
}
236+
237+
@Test
238+
void testSlowEndpointGetRequestOnlyEntryAndCleanHarAndVerifyResponseOnlyEntry() {
239+
//GIVEN
240+
def stubUrl = "/testResponseTimeoutCapturedInHar"
241+
def targetServerDelaySec = 5
242+
def idleConnectionTimeoutSec = targetServerDelaySec + 1
243+
def targetServiceResponseCode = 200
244+
stubFor(get(urlEqualTo(stubUrl))
245+
.willReturn(aResponse().withStatus(targetServiceResponseCode)
246+
.withFixedDelay(TimeUnit.SECONDS.toMillis(targetServerDelaySec) as Integer)
247+
.withBody("success"))
248+
)
249+
250+
proxy = startProxyAndCreateHar(idleConnectionTimeoutSec)
251+
252+
def requestUrl = "http://localhost:${mockServerPort}$stubUrl".toString()
253+
254+
def responseReceived = sendRequestToMockServer(requestUrl, targetServiceResponseCode)
255+
256+
257+
// Let request to be sent and captured
258+
sleep(500)
259+
260+
// Verify we got request-only har entry
261+
def har = proxy.getHar()
262+
def harEntry = har.log.entries.first()
263+
def capturedUrl = harEntry.request.url
264+
assertEquals("URL captured in HAR did not match request URL", requestUrl, capturedUrl)
265+
assertEquals("Expected response to be default", DEFAULT_HAR_RESPONSE, harEntry.response)
266+
267+
// Clean HAR
268+
proxy.getHar(true)
269+
270+
// Wait until response it received
271+
await().atMost(targetServerDelaySec + 1, TimeUnit.SECONDS).until({ responseReceived.get() })
272+
273+
// Verify we got response-only har entry
274+
har = proxy.getHar()
275+
harEntry = har.log.entries.first()
276+
assertEquals("Expected request to be default", DEFAULT_HAR_REQUEST, harEntry.request)
277+
assertNotEquals("Expected response to be not defualt", DEFAULT_HAR_RESPONSE, harEntry.response)
278+
assertEquals("Expected response status to be $targetServiceResponseCode",
279+
harEntry.response.status, targetServiceResponseCode)
280+
assertNotEquals("Expected response http version to be populated",
281+
harEntry.response.httpVersion, DEFAULT_HAR_RESPONSE.httpVersion)
282+
}
283+
284+
private void configureMockServer(String url, int delaySec, int responseCode) {
285+
stubFor(get(urlEqualTo(url))
286+
.willReturn(aResponse().withStatus(responseCode)
287+
.withFixedDelay(TimeUnit.SECONDS.toMillis(delaySec) as Integer)
288+
.withBody("success"))
289+
)
290+
}
291+
292+
private MitmProxyServer startProxyAndCreateHar(int idleConnectionTimeout) {
293+
proxy = new MitmProxyServer()
294+
proxy.setIdleConnectionTimeout(idleConnectionTimeout, TimeUnit.SECONDS)
295+
proxy.start()
296+
proxy.newHar()
297+
proxy
298+
}
299+
300+
private AtomicBoolean sendRequestToMockServer(requestUrl, targetServiceResponseCode) {
301+
def responseReceived = new AtomicBoolean(false)
302+
newSingleThreadExecutor().submit {
303+
getNewHttpClient(proxy.port).withCloseable {
304+
CloseableHttpResponse response = it.execute(new HttpGet(requestUrl))
305+
responseReceived.set(true)
306+
if (targetServiceResponseCode != null) {
307+
assertEquals("Did not receive HTTP $targetServiceResponseCode from proxy",
308+
targetServiceResponseCode, response.getStatusLine().getStatusCode())
309+
}
310+
}
311+
}
312+
responseReceived
313+
}
314+
}

0 commit comments

Comments
 (0)