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

Fixed reading in streamed body using fastcgi #7509

Closed
wants to merge 3 commits into from

Conversation

SiebelsTim
Copy link
Contributor

@SiebelsTim SiebelsTim commented Sep 23, 2021

@SiebelsTim
Copy link
Contributor Author

How do I resolve the merge conflicts? I was branching off of PHP-7.4.

The merge conflict is very easy to solve, as it just adds a void parameter to a function.

@cmb69
Copy link
Member

cmb69 commented Sep 23, 2021

How do I resolve the merge conflicts? I was branching off of PHP-7.4.

The question is which branch you want to target. If you want to target PHP-7.4 what is generally okay for a bug fix, just change the branch in the GH UI (Edit button for the title). If you want to target master, you should rebase onto that locally and then force push.

@cmb69
Copy link
Member

cmb69 commented Sep 23, 2021

After having a closer look, it might be better not to target any of the stable versions, so PHP-8.1 might be most appropriate (unless RM's would disagree). And since this is not solely a FPM issue, it would be good to support this for the cgi-fcgi SAPI as well.

@SiebelsTim
Copy link
Contributor Author

Thanks for the quick answer.

I am branching off of master now. That should be 8.1, correct?

Yep, cgi-fcgi has very similar code. I'll look into that later.

@cmb69
Copy link
Member

cmb69 commented Sep 23, 2021

I am branching off of master now. That should be 8.1, correct?

No. PHP-8.1 has already been branched. master is supposed to become 8.2.

@SiebelsTim
Copy link
Contributor Author

Okay. I've used PHP-8.1 as a base then and applied the patch to cgi-fcgi as well.

@SiebelsTim
Copy link
Contributor Author

I've rebased onto PHP-8.1 to resolve merge conflicts.

@SiebelsTim
Copy link
Contributor Author

And again, after tests failed. This was fixed in 69514e6

@SiebelsTim SiebelsTim changed the base branch from master to PHP-8.1 October 15, 2021 08:40
@SiebelsTim
Copy link
Contributor Author

@bukka Anything further I need to do to?

sapi/fpm/tests/tester.inc Outdated Show resolved Hide resolved
@SiebelsTim SiebelsTim closed this Nov 25, 2021
@SiebelsTim SiebelsTim reopened this Nov 25, 2021
@cmb69
Copy link
Member

cmb69 commented Nov 25, 2021

Regarding the test failures see https://externals.io/message/116496 (Travis should be resolved, but Azure isn't yet).

@bukka
Copy link
Member

bukka commented Nov 27, 2021

I just updated the https://bugs.php.net/bug.php?id=51191 . I don't think this is really acceptable as it goes against the spec (especially it is not acceptable for plain CGI that this PR changes as well). The fact is that fastcgi is not a maintained spec but it's the only thing that we currently have. Even though we don't implement everything from it (e.g. multiplexing), I'm not aware that we would deviate from it.

I think the only option would be to rewrite the FastCGI spec (we can't probably use the current wording because of the copyright) and update it accordingly. This is something that I have been thinking about for some time as I would like to introduce optional TLS support (with client cert for securing connection) at some point which would require some spec extension. So this would be a good candidate to add into it as well. It's quite a bit of work though.

@SiebelsTim
Copy link
Contributor Author

Hi,

thanks a lot for looking into it.

I raised similar concerns at the nginx mailinglist: https://forum.nginx.org/read.php?2,292380,292383

When I looked into the spec for this ticket, I haven't found a reference to CONTENT_LENGTH being a required parameter. You could argue that it is implied in that section or required transitively by CGI, though.

That being said, do you think it would be possible to differentiate between missing and and available CONTENT_LENGTH parameter? i.e. when there is no CONTENT_LENGTH parameter, read in the entire STDIN. When there is a CONTENT_LENGTH paramter, read in at most CONTENT_LENGTH bytes.

By the way, regarding the spec:

A Responder performing an update, e.g. implementing a POST method, should compare the number of bytes received on FCGI_STDIN with CONTENT_LENGTH and abort the update if the two numbers are not equal.

I might be wrong, but I think we do not implement this, do we? The issue we are facing in our product is, that the body is simply empty. I would expect the responder (i.e. php-fcgi?) to answer with an error. However, I do not think that this would be a good experience for users to not be able handle such cases in their application. We could argue that the responder is not php-fcgi alone, but rather the php application in combination with php-fcgi, and it is the applications responsibility to enforce this constraint.

Regarding extending the spec: After all, we still need web servers, such as nginx or apache, to implement these feature we would be trying to implement on our end, right? In any way, I don't think I could contribute in such dimensions. However, I'd be willing to help.

@bukka
Copy link
Member

bukka commented Nov 29, 2021

When I looked into the spec for this ticket, I haven't found a reference to CONTENT_LENGTH being a required parameter. You could argue that it is implied in that section or required transitively by CGI, though.

FCGI doesn't define any env vars but specifically points to CGI/1.1 which is quite explicit about it so it looks quite clear to me.

That being said, do you think it would be possible to differentiate between missing and and available CONTENT_LENGTH parameter? i.e. when there is no CONTENT_LENGTH parameter, read in the entire STDIN. When there is a CONTENT_LENGTH parameter, read in at most CONTENT_LENGTH bytes.

But then it breaks that requirement that CONTENT_LENGTH must be present so we are again talking about not following the spec. I understand why this can be useful - just not sure if it's right way to do without the spec. It's more kind of like we would decide to hack this way... :)

By the way, regarding the spec:

A Responder performing an update, e.g. implementing a POST method, should compare the number of bytes received on FCGI_STDIN with CONTENT_LENGTH and abort the update if the two numbers are not equal.

I might be wrong, but I think we do not implement this, do we? The issue we are facing in our product is, that the body is simply empty. I would expect the responder (i.e. php-fcgi?) to answer with an error. However, I do not think that this would be a good experience for users to not be able handle such cases in their application. We could argue that the responder is not php-fcgi alone, but rather the php application in combination with php-fcgi, and it is the applications responsibility to enforce this constraint.

Yeah from the quick look it doesn't look that we explicitly abort the connection. I also don't see how it could be useful for the user as there are better ways how to handle it on the application level. I guess it was supposed to be probably more protection about buggy clients but not sure really. In any way it is should and not MUST so it doesn't seem like a hard requirement.

Regarding extending the spec: After all, we still need web servers, such as nginx or apache, to implement these feature we would be trying to implement on our end, right? In any way, I don't think I could contribute in such dimensions. However, I'd be willing to help.

Yeah this would require integration in web servers. But it's not just this as there are more things that could be improved on the protocol but it will surely take time so not an immediate solution but think it's a proper solution.

@SiebelsTim
Copy link
Contributor Author

Okay. I do understand your concerns.

Right now, streaming bodies without a Content-Length header in nginx simply does not work. At least not, when we disable request buffering in nginx. Apache had the same problem in the past, which they fixed by buffering all requests with chunked encoding. I think nginx is supposed to implement the same handling then. I will take this to their bug tracker.

Thanks a lot for the discussion. How do we proceed with this MR? I guess we would just close it? We can always reopen if there's more to it :)

@SiebelsTim
Copy link
Contributor Author

@SiebelsTim
Copy link
Contributor Author

The nginx folks dismissed my report immediately, telling me to enable request buffering for all requests. I will continue to try to push the issue forward at that end.

In the meantime, I'd like to suggest the following:

I understand that nginx does not want to start buffering all chunked requests for the sake of having (non spec-compliant) FastCGI responders that can deal with those requests.
Since we are the responder, and we already decided to let the application handle mismatches of the content length, could we move the responsibility entirely to the application?
Right now, we cannot read in the request body, even if we tried. With the proposed change in this MR (regarding FastCGI, not CGI), the application can read in the request and is able to read in the CONTENT_LENGTH parameter.

But then it breaks that requirement that CONTENT_LENGTH must be present so we are again talking about not following the spec.

So my proposition is to handle the case, that the webserver did not pass a CONTENT_LENGTH parameter (and therefore issued an invalid request) at application level. PHP could provide the body and let the application deal with a mismatch in CONTENT_LENGTH.

@bukka
Copy link
Member

bukka commented Dec 1, 2021

I understand that nginx does not want to start buffering all chunked requests for the sake of having (non spec-compliant) FastCGI responders that can deal with those requests. Since we are the responder, and we already decided to let the application handle mismatches of the content length, could we move the responsibility entirely to the application? Right now, we cannot read in the request body, even if we tried. With the proposed change in this MR (regarding FastCGI, not CGI), the application can read in the request and is able to read in the CONTENT_LENGTH parameter.

I'm sorry but I don't think we should be breaking the spec because nginx is unwilling to follow the spec. Maybe you should choose a web server that actually follow the spec and switch to httpd in that case. They obviously made sacrifice to be compatible and we shouldn't put them into the disadvantage because nginx wants to support some minority of responders that are not complaint - it would be interesting to know what their share of fastcgi responders is but my guess is that well over 99% will be php-fpm but might be wrong ofc.

But then it breaks that requirement that CONTENT_LENGTH must be present so we are again talking about not following the spec.

So my proposition is to handle the case, that the webserver did not pass a CONTENT_LENGTH parameter (and therefore issued an invalid request) at application level. PHP could provide the body and let the application deal with a mismatch in CONTENT_LENGTH.

I don't think the application should decide about this. Definitely not be default. We could potentially introduce a configuration flag that would allow it but still not sure if even that is a good idea. This won't go to 8.1 for sure - it's more likely 8.2 but need to move to some other things so get it reviewed might take some time as I feel that there are higher priorities than getting in a feature that breaks the spec.

@SiebelsTim
Copy link
Contributor Author

I'm sorry but I don't think we should be breaking the spec because nginx is unwilling to follow the spec.

I agree completely. I was wondering if that would be in line with the specification. Or at least be in the zone of undefined behaviour. Having no option in the application to read the body that was passed into it doesn't seem right either.

it would be interesting to know what their share of fastcgi responders is but my guess is that well over 99% will be php-fpm but might be wrong ofc.

Yes. I was wondering that as well.

I don't think the application should decide about this. Definitely not be default.

Would you mind elaborating why you don't think the application should be able to decide this? I thought this was similar to having fewer bytes than CONTENT_LENGTH specifies.

We could potentially introduce a configuration flag that would allow it but still not sure if even that is a good idea. This won't go to 8.1 for sure - it's more likely 8.2 but need to move to some other things so get it reviewed might take some time as I feel that there are higher priorities than getting in a feature that breaks the spec.

While that would solve our issue, I don't think this is a good idea for php itself.

In the end, I'm tackling this on both ends and will push this further on the nginx side as well.

@bukka
Copy link
Member

bukka commented Dec 1, 2021

I'm sorry but I don't think we should be breaking the spec because nginx is unwilling to follow the spec.

I agree completely. I was wondering if that would be in line with the specification. Or at least be in the zone of undefined behaviour. Having no option in the application to read the body that was passed into it doesn't seem right either.

I understand that it's a bit an artificial limitation but it's defined like this in the spec so the default behavior should respect it. The correct fix is to fix the spec and not pass the decision to application.

I don't think the application should decide about this. Definitely not be default.

Would you mind elaborating why you don't think the application should be able to decide this?

I don't think that application should be aware of the protocol specifics. This should be all handled by the SAPI ideally. It seems quite messy to me if the application should care about some FastCGI specific bits. We both know that the result of that would be basically breaking the spec because application would never care...

Btw how would you actually terminate fcgi connection in the application?

I thought this was similar to having fewer bytes than CONTENT_LENGTH specifies.

After thinking about it I think we should actually rather fix this case and abort the connection for cases when the input body length doesn't match the CONTENT_LENGTH. I'd consider this as a bug though.

@SiebelsTim
Copy link
Contributor Author

Btw how would you actually terminate fcgi connection in the application?

Oh, right. We don't have an API for that. Incidentally, that makes it also impossible to terminate the connection when receiving fewer bytes than specified. I won't even suggest to add an API to userland for this, as I agree that the application shouldn't need to worry about FastCGI.

I understand your rationale. Thanks a lot for the discussion!

If you ever decide to tackle changing the specification and I can help, feel free to contact me.

@Nottt
Copy link

Nottt commented Apr 24, 2024

Yars later and this is still a use. I just want to stream ffmpeg progress data to php using php-fpm and nginx with fastcgi, and since they do chunked posts, I am not able to make this work at all.

This has been a issue for at least 8 years :(

https://trac.ffmpeg.org/ticket/5288

https://ffmpeg-user.ffmpeg.narkive.com/M68TeljY/chunked-request-when-using-progress

With a live input, the request would never "finish" at all

@Nottt
Copy link

Nottt commented Apr 24, 2024

This is also causing issues for lots of use cases and projects around the internet such as:

nextcloud/documentation#9574

caddyserver/caddy#5236

golang/go#5613

and probably many others. In 2024 there is many use cases of uploading data with post and chunked encoding and php just can't handle that

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants