Skip to content

Commit b9e276b

Browse files
authored
fix: adding HAR validation exceptions for when postData is an empty object (readmeio#110)
1 parent 9800042 commit b9e276b

39 files changed

Lines changed: 483 additions & 5 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
module.exports = {
2+
log: {
3+
version: '1.2',
4+
creator: {
5+
name: 'HTTPSnippet',
6+
version: '1.0.0',
7+
},
8+
entries: [
9+
{
10+
request: {
11+
method: 'POST',
12+
url: 'https://httpbin.org/anything',
13+
headers: [
14+
{
15+
name: 'content-type',
16+
value: 'application/json',
17+
},
18+
],
19+
postData: {
20+
// Per the HAR spec `postData` should always have `mimeType` but this fixture is
21+
// testing when that isn't the case.
22+
},
23+
},
24+
response: {
25+
status: 200,
26+
statusText: 'OK',
27+
httpVersion: 'HTTP/1.1',
28+
headers: [
29+
{
30+
name: 'Content-Type',
31+
value: 'application/json',
32+
},
33+
],
34+
content: {
35+
size: -1,
36+
mimeType: 'application/json',
37+
text: JSON.stringify({
38+
args: {},
39+
data: '',
40+
files: {},
41+
form: {},
42+
headers: {
43+
'Content-Type': 'application/json',
44+
},
45+
json: null,
46+
method: 'POST',
47+
url: 'https://httpbin.org/anything',
48+
}),
49+
},
50+
headersSize: -1,
51+
bodySize: -1,
52+
},
53+
},
54+
],
55+
},
56+
};

src/helpers/har-validator.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,25 @@ export const validateHarRequest = (request: any): request is Request => {
2727
if (!validate) {
2828
throw new Error('failed to find HAR request schema');
2929
}
30+
3031
const valid = validate(request);
3132
if (!valid && validate.errors) {
33+
if (validate.errors.length === 1) {
34+
// While not ideal, or compliant with the HAR spec, if we have an empty `postData` object in
35+
// our HAR and no other errors we should let this through because it's fine and our client
36+
// targets are able to handle it okay.
37+
const error = validate.errors[0];
38+
if (
39+
error.dataPath === '.postData' &&
40+
error.message === "should have required property 'mimeType'" &&
41+
JSON.stringify(request.postData) === '{}'
42+
) {
43+
return true;
44+
}
45+
}
46+
3247
throw new HARError(validate.errors);
3348
}
49+
3450
return true;
3551
};

src/integration.test.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,9 +232,17 @@ availableTargets()
232232
// check target agnostic we need to parse and re-stringify our expectations so that
233233
// this test can universally match them all.
234234
if (expected.headers?.['Content-Type']?.includes('application/json')) {
235-
expect(JSON.stringify(JSON.parse(response.data))).toStrictEqual(
236-
JSON.stringify(JSON.parse(expected.data))
237-
);
235+
// In our postdata-malformed fixture we're sending a POST payload without any
236+
// content so what HTTPBin sends back to us is a `json: null` and `data: ''`, which
237+
// we need to specially assert here as running `JSON.parse()` on an empty string
238+
// will throw an exception.
239+
if (fixture === 'postdata-malformed' && response.data === '') {
240+
expect(expected.data).toBe('');
241+
} else {
242+
expect(JSON.stringify(JSON.parse(response.data))).toStrictEqual(
243+
JSON.stringify(JSON.parse(expected.data))
244+
);
245+
}
238246
} else {
239247
expect(response.data).toStrictEqual(expected.data);
240248
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CURL *hnd = curl_easy_init();
2+
3+
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
4+
curl_easy_setopt(hnd, CURLOPT_URL, "https://httpbin.org/anything");
5+
6+
struct curl_slist *headers = NULL;
7+
headers = curl_slist_append(headers, "content-type: application/json");
8+
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);
9+
10+
CURLcode ret = curl_easy_perform(hnd);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
(require '[clj-http.client :as client])
2+
3+
(client/post "https://httpbin.org/anything" {:headers {:content-type "application/json"}})
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.Net.Http.Headers;
2+
var client = new HttpClient();
3+
var request = new HttpRequestMessage
4+
{
5+
Method = HttpMethod.Post,
6+
RequestUri = new Uri("https://httpbin.org/anything"),
7+
};
8+
using (var response = await client.SendAsync(request))
9+
{
10+
response.EnsureSuccessStatusCode();
11+
var body = await response.Content.ReadAsStringAsync();
12+
Console.WriteLine(body);
13+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
var client = new RestClient("https://httpbin.org/anything");
2+
var request = new RestRequest(Method.POST);
3+
request.AddHeader("content-type", "application/json");
4+
IRestResponse response = client.Execute(request);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"io/ioutil"
7+
)
8+
9+
func main() {
10+
11+
url := "https://httpbin.org/anything"
12+
13+
req, _ := http.NewRequest("POST", url, nil)
14+
15+
req.Header.Add("content-type", "application/json")
16+
17+
res, _ := http.DefaultClient.Do(req)
18+
19+
defer res.Body.Close()
20+
body, _ := ioutil.ReadAll(res.Body)
21+
22+
fmt.Println(res)
23+
fmt.Println(string(body))
24+
25+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
POST /anything HTTP/1.1
2+
Content-Type: application/json
3+
Host: httpbin.org
4+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
AsyncHttpClient client = new DefaultAsyncHttpClient();
2+
client.prepare("POST", "https://httpbin.org/anything")
3+
.setHeader("content-type", "application/json")
4+
.execute()
5+
.toCompletableFuture()
6+
.thenAccept(System.out::println)
7+
.join();
8+
9+
client.close();

0 commit comments

Comments
 (0)