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

Implement method HEAD in ApiListener #6062

Merged
merged 21 commits into from
Jan 24, 2024

Conversation

Yale96
Copy link
Contributor

@Yale96 Yale96 commented Dec 29, 2023

No description provided.

@Yale96 Yale96 added the 7.9 label Dec 29, 2023
@Yale96 Yale96 added this to the 7.9.1 milestone Dec 29, 2023
@Yale96 Yale96 self-assigned this Dec 29, 2023
@jkosternl
Copy link
Contributor

@Yale96, can you check (or perhaps add a check) that no real content data is received if the HEAD method is used? Maybe we need to add something to prevent that (or that it is used to by pass security etc).

@tnleeuw
Copy link
Contributor

tnleeuw commented Jan 8, 2024

@Yale96, can you check (or perhaps add a check) that no real content data is received if the HEAD method is used? Maybe we need to add something to prevent that (or that it is used to by pass security etc).

Indeed please add a test for this. I see nothing in code now to prevent data from being returned.

@jkosternl jkosternl removed the 7.9 label Jan 8, 2024
@jkosternl jkosternl removed this from the 7.9.1 milestone Jan 8, 2024
@jkosternl jkosternl linked an issue Jan 8, 2024 that may be closed by this pull request
@Yale96 Yale96 requested a review from jkosternl January 9, 2024 12:31
Copy link
Contributor

@jkosternl jkosternl left a comment

Choose a reason for hiding this comment

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

As discussed in daily.

@jkosternl jkosternl removed the request for review from nielsm5 January 10, 2024 16:31
Copy link
Contributor

@jkosternl jkosternl left a comment

Choose a reason for hiding this comment

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

Looking good. Just a few minor remarks to consider.

@Yale96 Yale96 requested a review from jkosternl January 11, 2024 06:13
Copy link
Contributor

@tnleeuw tnleeuw left a comment

Choose a reason for hiding this comment

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

In addition to the comments on the PR also on all affected files change the copyright-year to include the year 2024! :-)

Comment on lines 587 to 591
if (result == null && method == ApiListener.HttpMethod.HEAD) {
MimeType contentType = determineContentType(messageContext, listener, null);
response.setContentType(contentType.toString());
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Two comments on this:

  1. result == null is not the same as Message.isEmpty(result)
  2. I think that since both blocks have same content you can merge the if-conditions:
    if (!Message.isEmpty(result) || method == ApiListener.HttpMethod.HEAD))

@@ -1198,8 +1225,11 @@ public void testRequestWithAccept(Methods method) throws Exception {

// Assert
assertEquals(200, result.getStatus());
Message input = requestMessage;
assertEquals("application/xml", input.getContext().get("Header.accept"));
if (method != Methods.HEAD) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is the exception on method HEAD needed here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's not needed anymore indeed. I will remove it

@@ -579,9 +579,11 @@ else if(segment.startsWith("{") && segment.endsWith("}")) {
*/
response.addHeader("Allow", (String) messageContext.get("allowedMethods"));

if (!Message.isEmpty(result)) {
if (!Message.isEmpty(result) && result.size() != -1) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not the correct change because you now make setting content-type dependent on result-size being known.
What I had suggested earlier was to combine (!Message.isEmpty(result) || method == ApiListener.HttpMethod.HEAD)

Checking for size can be done in a nested if. And when setting content-length on the response, you should not need to also add a header content-length because the HttpResponse implementation should do this for you.

Comment on lines 615 to 617
} else {
response.setContentLength(0);
response.addHeader("content-length", "0");
Copy link
Contributor

Choose a reason for hiding this comment

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

Now you always set content-length to 0 when method = HEAD?
I thought that that goes against the intention?

Response result = service(createRequest(uri, Methods.HEAD, null, headers));

// Assert
assertEquals(200, result.getStatus());
Copy link
Contributor

Choose a reason for hiding this comment

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

I do not see a check here that the content of the response is empty. That check is perhaps the most important check to validate if HEAD did what it is supposed to do.

assertEquals("OPTIONS, HEAD", result.getHeader("Allow"));
assertNull(result.getErrorMessage());
assertFalse(result.containsHeader("etag"));
assertEquals("0", result.getHeader("content-length"));
Copy link
Contributor

Choose a reason for hiding this comment

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

The length of the response should not have been 0 as the response-content that would have been returned by GET would have been {"tralalalallala"}.

assertNull(result.getErrorMessage());
assertFalse(result.containsHeader("etag"));
assertEquals("0", result.getHeader("content-length"));
assertEquals("*/*", result.getHeader("content-type"));
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm surprised that the content-type header is */* and not JSON. Can you please set the mimetype on the repeatableMessage to JSON? Or otherwise the produces on the ApiListenerBuilder?

Comment on lines +930 to +933
public void apiListenerWithHeadMethodCall() throws Exception {
// Arrange
String uri = "/apiListenerWithHeadMethodCall";
Message repeatableMessage = Message.asMessage(new Message("{\"tralalalallala\":true}").asByteArray());
Copy link
Contributor

Choose a reason for hiding this comment

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

After applying other comments to this test, I would like a copy of this test where this repeatableMessage is constructed with an empty string, but the size set to something like 20 by setting the corresponding key in the MessageContext. Then the content-length returned by HEAD should also be 20.

@Yale96 Yale96 requested a review from tnleeuw January 19, 2024 10:40
@tnleeuw tnleeuw requested a review from nielsm5 January 22, 2024 11:22
nielsm5 and others added 2 commits January 23, 2024 11:07
Co-authored-by: J. Koster <j.koster@gmx.com>
/*
* Finalize the pipeline and write the result to the response
*/
final boolean outputWritten = writeToResponseStream(response, result);
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Sluiten we deze wel?

Copy link
Sponsor Member

@nielsm5 nielsm5 left a comment

Choose a reason for hiding this comment

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

I've made some changes in #6148 so one of us needs to merge those :)

…442-Implement-method-head-in-ApiListener

# Conflicts:
#	core/src/main/java/org/frankframework/http/rest/ApiListener.java
@nielsm5 nielsm5 changed the title 5442 - implement method HEAD in ApiListener Implement method HEAD in ApiListener Jan 24, 2024
@@ -240,6 +226,7 @@ public boolean accepts(@Nullable String acceptHeader) {
* HTTP method to listen to
* @ff.default GET
*/
@Deprecated
Copy link
Sponsor Member

@nielsm5 nielsm5 Jan 24, 2024

Choose a reason for hiding this comment

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

Suggested change
@Deprecated

Deze moet hier denk ik niet?

@nielsm5 nielsm5 requested a review from tnleeuw January 24, 2024 14:36
Copy link

sonarcloud bot commented Jan 24, 2024

@@ -577,15 +577,18 @@
/*
* Add headers
*/
response.addHeader("Allow", (String) messageContext.get("allowedMethods"));
response.addHeader("Allow", (String) pipelineSession.get("allowedMethods"));

Check warning

Code scanning / CodeQL

HTTP response splitting Medium

This header depends on a
user-provided value
, which may cause a response-splitting vulnerability.
@nielsm5 nielsm5 merged commit 9bd72ea into master Jan 24, 2024
15 checks passed
@nielsm5 nielsm5 deleted the 5442-Implement-method-head-in-ApiListener branch January 24, 2024 15:54
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.

Implement http method HEAD
4 participants