Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TEX-537] Refactor HTTP request and response representations #554

Merged
merged 3 commits into from
Dec 10, 2023

Conversation

timdawborn
Copy link
Contributor

@timdawborn timdawborn commented Dec 8, 2023

This PR does a bunch of initial refactoring and cleaning up of the representations of HTTP request and response objects throughout the codebase, ahead of making some larger changes to support gRPC-web request comparisons.

The changes in this PR are mostly code structure in nature rather than functional. There is one functionality change in the decision on how to compare two HTTP request bodies, which I've called out in comments.

@timdawborn timdawborn changed the title [TEX-537] Refactor HTTP request and response logic [TEX-537] Refactor HTTP request and response representations Dec 8, 2023
Comment on lines -5 to -17
request: {
method: string;
path: string;
headers: Headers;
body: Buffer;
};
response: {
status: {
code: number;
};
headers: Headers;
body: Buffer;
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to HttpRequest and HttpReponse in http.ts.

Comment on lines -54 to -60

/**
* Headers of a request or response.
*/
export interface Headers {
[headerName: string]: string | string[] | undefined;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved into http.ts and renamed to HttpHeaders.

Comment on lines -80 to -90
export interface Request {
host?: string;
method: string;
path: string;
headers: Headers;
body: Buffer;
}

export interface RequestWithHost extends Request {
host: string;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved into HttpRequest in http.ts.

Comment on lines -46 to -49
requestMethod: string,
requestPath: string,
requestHeaders: Headers,
requestBody: Buffer,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just pass around a HttpRequest object instead of all of the individual components of a request unencapsulated.

"deep-diff": "^1.0.2",
"fs-extra": "^11.1.1",
"js-yaml": "^4.1.0",
"query-string": "^6.13.6",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this super outdated library and replaced it with the builtin querystring library provided by NodeJS. https://nodejs.org/api/querystring.html

Comment on lines +42 to +71
// Compare the query parameters.
const parsedQueryParameters = parseQueryParameters(request.path);
const parsedCompareToQueryParameters = parseQueryParameters(
compareTo.request.path,
);
const differencesQueryParameters = countObjectDifferences(
parsedQueryParameters,
parsedCompareToQueryParameters,
rewriteBeforeDiffRules,
);

// Compare the cleaned headers.
const cleanedHeaders = stripExtraneousHeaders(request.headers);
const cleanedCompareToHeaders = stripExtraneousHeaders(
compareTo.request.headers,
);
return (
countObjectDifferences(
parsedQuery,
parsedCompareToQuery,
rewriteBeforeDiffRules,
) +
countObjectDifferences(headers, compareToHeaders, rewriteBeforeDiffRules) +
countBodyDifferences(
serialisedRequestBody,
serialisedCompareToRequestBody,
rewriteBeforeDiffRules,
)
const differencesHeaders = countObjectDifferences(
cleanedHeaders,
cleanedCompareToHeaders,
rewriteBeforeDiffRules,
);

// Compare the bodies.
const differencesBody = countBodyDifferences(
request,
compareTo.request,
rewriteBeforeDiffRules,
);

return differencesQueryParameters + differencesHeaders + differencesBody;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same logic as before, it's just been re-ordered slightly.

Comment on lines +90 to +110
const contentType = contentType1.type;
if (contentType === "application/json") {
return countBodyDifferencesApplicationJson(
request1,
contentType1,
request2,
contentType2,
rewriteBeforeDiffRules,
);
} else if (contentType.startsWith("text/")) {
return countBodyDifferencesText(
request1,
contentType1,
request2,
contentType2,
rewriteBeforeDiffRules,
);
} else {
// No more special cases to consider. Assume binary data for all other content types.
return countBodyDifferencesBinary(request1, request2);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is some slightly different logic to before.

Up until now, we just blindly attempted to decode it as JSON. If that succeeded, we did a JS object comparison of the resulting JSON decoded values. If it failed to decode, we did a base64 string comparison.

Now, we inspect the content type of the HTTP requests, and attempt to do JSON decoding if they have application/json as their content type, do raw string comparison if they have text/<anything> as their content type, and base64 comparison for any other content type.

const questionMarkPosition = path.indexOf("?");
if (questionMarkPosition !== -1) {
return path.substr(0, questionMarkPosition);
return path.substring(0, questionMarkPosition);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change fixes a deprecation warning. substr has been long deprecated in favour of substring.

const questionMarkPosition = path.indexOf("?");
if (questionMarkPosition !== -1) {
return queryString.parse(path.substr(questionMarkPosition));
return parseQueryString(path.substring(questionMarkPosition + 1));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The + 1 addition here fixes a long standing bug that I found while swapping the query string parsing functionality from the long deprecated library to the builtin NodeJS library. Before, the substring returned included in the ? at the start of the string as the slice was starting at the index of the ?. Apparently the old library accepted this without issue, but the new NodeJS one did not.

@@ -54,13 +54,14 @@
"brotli": "^1.3.3",
"chalk": "^4.1.0",
"commander": "^10.0.1",
"content-type": "^1.0.5",
Copy link
Contributor Author

@timdawborn timdawborn Dec 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a HTTP content-type parsing library. This is used in similarity.ts to start adding some smarts in how to compare HTTP request bodies of different content types.

@timdawborn timdawborn requested a review from njday December 8, 2023 03:06
@timdawborn timdawborn marked this pull request as ready for review December 8, 2023 03:06
@timdawborn timdawborn requested a review from a team as a code owner December 8, 2023 03:06
Comment on lines +5 to +10
/**
* Headers of a request or response.
*/
export interface HttpHeaders {
[headerName: string]: string | string[] | undefined;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been moved from tape.ts.

Comment on lines +12 to +25
/**
* The common fields of a HTTP request.
*/
export interface HttpRequest {
host?: string;
method: string;
path: string;
headers: HttpHeaders;
body: Buffer;
}

export interface HttpRequestWithHost extends HttpRequest {
host: string;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These have been moved from sender.ts.

Comment on lines +27 to +36
/**
* The common fields of a HTTP response.
*/
export interface HttpResponse {
status: {
code: number;
};
headers: HttpHeaders;
body: Buffer;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been moved from tape.ts.

Copy link

@njday njday left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@timdawborn timdawborn merged commit dc0743a into master Dec 10, 2023
6 checks passed
@timdawborn timdawborn deleted the TEX-537-refactor-http-request-response branch December 10, 2023 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants