Problem/Motivation
Right now, upload progress only works if you're running in mod_php mode (with the PECL extesion uploadprogress). Sadly mod_php only works with Apache.
Steps to reproduce
Upload a file while not using mod_php (by disabling it or using something like Nginx)
Notice that there's no upload progress bar
Steps with MR changes:
1. Ensure you are running NGINX(I(@yash.rode) am using Laravel Valet)
2. You should be able to see Upload progress Enabled on the Status report page.
3. Go to structure/types/manage/article/form-display and click the gear icon in front of image
4. and change the Progress indicator to Bar with progress meter
5. When you try to upload an image to an article, you should see a progress bar.
I used PHP 8.1, NGINX using Laravel Valet.
Proposed resolution
PHP >=5.3 has a built-in upload progress http://php.net/manual/en/session.upload-progress.php that we should support.
Remaining tasks
N/A
User interface changes
N/A, only restoring existing functionality to all users
API changes
N/A
Data model changes
N/A
Release notes snippet
N/A
| Comment | File | Size | Author |
|---|---|---|---|
| #118 | 1561866-nr-bot.txt | 5.26 KB | needs-review-queue-bot |
| #115 | Screenshot 2023-08-10 at 2.50.44 PM.png | 168.65 KB | yash.rode |
| #112 | Screenshot 2023-08-09 at 6.19.57 PM.png | 188.45 KB | yash.rode |
| #110 | 1561866-nr-bot.txt | 3.27 KB | needs-review-queue-bot |
| #102 | 1561866-nr-bot.txt | 8.1 KB | needs-review-queue-bot |
Issue fork drupal-1561866
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #1
andypostSuppose this functionality should be pluggable to support http://wiki.nginx.org/HttpUploadProgressModule
There's a project http://drupal.org/project/filefield_nginx_progress
Comment #2
droplet commentedComment #3
gagarine commentedThis should work with nginx with php-fpm no?
Comment #4
gagarine commentedComment #5
mgiffordI understand that there are also problems with php-fpm & PECL’s uploadprogress.
As people keep looking for new ways to enhance performance, it would be good to be able to leverage tools built into PHP 5.4+
Comment #6
NaX commentedI played around with PHP Session upload using nginx module as a base. Very quickly I worked out that PHP Session Uploads Don't work at all for CGI/FastCGI installs.
I read that you can switch to mod_fastcgi and using the -flush command you can get it working.
I am not in control of my clients hosting making this something I cant use.
Attached is my D7 test module that started out as a copy of nginx module. I never got it working but somebody else may find it helpful as a starting point.
Comment #7
dillix commentedAs I can see on function page:
Note, this feature doesn't work, when your webserver is runnig PHP via FastCGI. There will be no progress informations in the session array.
Unfortunately PHP gets the data only after the upload is completed and can't show any progress.
Comment #8
driskell commentedHas anyone tried with Nginx >=1.7.11 and fastcgi_request_buffering set to "off"? This might be exactly what's needed. Of course it means a server level change for the locations where the POST is going to happen though so not ideal for everyone, but may at least allow the feature to be used by advanced users with server-level access.
http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_reque...
Comment #9
serg2 commentedNow that PHP 5.5 is the minimum required version all systems should have Session Upload Progress built in so should make the patch simpler.
Also, the status report:
is incorrect. Does that mean this issue should be classed as bug rather than a feature request?
Am I correct in thinking that this mis-reporting occurs on every system? As it is so widespread should this be a major?
Comment #10
andypostComment #11
cilefen commentedComment #13
Peter Swietoslawski commentedI believe there is a case to be made that Drupal 8 should get rid of APC rfc 1867 and Uploadprogress support altogether and replace it with PHP's native Session Upload Progress.
Reasons to support the case:
FileWidgetAjaxController::progressis using accordinglyapcu_fetchoruploadprogress_get_info()that break PHP parser causing script to stop working and thus rendering error like:for AJAX callback for
Drupal::ProgressBar::sendPing()$_SESSION["upload_progress_xxx"]array is never created by PHP.Bottom line going with PHP native solution will simplify code, and remove dependencies on PHP extensions.
Comment #14
jhedstromThis makes complete sense to me. Since D8 now requires php >= 5.5, this will not be a php-version issue at all.
Moving back to 'active' since there is no actual patch here yet.
Comment #15
jhedstromAn added bonus:
This is also a requirement for the Bigpipe module to function properly, so folks that have that working will immediately benefit from this change.
Comment #16
gappleIt was already mentioned the uploadprogress doesn't have a release in PECL that supports PHP7. It also appears that rfc1867 support was not included in APCu 5 (https://github.com/krakjoe/apcu/issues/212), so PHP's native upload progress handling looks like the only option available for PHP 7.
Comment #17
droplet commentedAFAIK, Drupal is using custom session handler and it breaks built-in PHP session upload progress. I read an article in the old day stated that it still possible to do it with extra PHP.ini config & code but I'm failed.
In Drupal, we using a jQuery.form plugin for all form ajaxing. It actually supports Client-side upload progress (XMLHttpRequest Level 2 [https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent]).
If anybody is interested, take a look and patch it. It won't be hard. (A new issue needs to be created.)
Comment #18
rjjakes commentedHi @droplet
Thanks for the pointers about jQuery.form. I've also tried to get the built in PHP session upload progress to work, with no success.
I've created a separate issue with a patch that implements the jQuery.form uploadProgress function.
https://www.drupal.org/node/2833968
Comment #19
andypostComment #20
andypostAlso there's another way to offload php https://www.nginx.com/resources/wiki/modules/upload_progress/
Comment #23
anavarreComment #25
darren ohAs mentioned in comment #17, using a custom session handler breaks the built-in PHP session upload progress extension. The attached patch temporarily reverts to the built-in PHP session handler and retrieves the upload progress data. It then restores Drupal’s custom session handler. This only works if PHP is configured to use the same cookie name as Drupal and if no other upload progress extensions are enabled. The fix for #2662932: Fix file upload progress bar must be applied before this can be tested.
Comment #27
darren ohFixed failed tests.
Comment #28
darren ohComment #30
darren ohFixed another failed test.
Comment #31
darren ohIn case anyone else wonders if switching between session handlers is necessary, I just tried setting the default session.save_handler to user. I thought PHP might hold on to the session data until Drupal started its session manager, but PHP just crashed.
Comment #32
zymbian commented+1 for patch #30.
Comment #33
borisson_This needs to have a docblock.
I agree that it's a good thing to document what this regex does. I would like to know the why of this regex though. Why are we doing this regex.
So something like this:
This comment doesn't really explain anything. It can be removed.
This comment doesn't explain the why. So either remove, or better, explain why we need to do this.
Comment #34
mohit1604 commentedFixed points 3 and 4 mentioned in #33.
Comment #35
darren ohHere is the patch with all documentation fixes added.
Comment #36
anavarreIn testing this locally I managed to get the upload progress widget indicator to show up just fine but it seems to be stalled until the upload completes. I did try with very large files but it didn't make any difference. Simplytest.me seems broken ATM so I couldn't test there to rule out any local setup oddity.
Has anyone been able to get this working correctly with the latest patch?
Comment #37
darren ohThe upload progress bar is broken by #2662932: Fix file upload progress bar. The patch for that issue has to be applied before testing this one.
Comment #38
darren ohAdded check for correct PHP session name to status report.
Comment #39
darren ohComment #40
smokrisThanks — the combination of #2662932-74 and #1561866-38 is working well for me so far.
While testing this patch, I had a
session_id()that happened to contain an underscore.php_session_valid_key()returns false if it contains an underscore, so PHP wasn't saving the session and upload progress wasn't being reported.So this issue also depends on #2238561: Use the default PHP session ID instead of generating a custom one or another fix for the invalid-underscore-in-session_id issue (such as doing a simple
str_replaceto convert the underscores into commas, as in the attached patch).Also, regarding the status report message:
It might be helpful to be more explicit about how to do this; e.g., change it to something like
…must be set to %session_name in php.ini (which is applied before Drupal starts) in order to store file upload progress…Comment #41
darren ohsession.name can be set in php.ini, .htaccess, httpd.conf or .user.ini. If the fix for #2455465: Add mod_php7 check to htaccess and remove php5 code were committed, I would recommend setting session.name in .htaccess. Until that happens, I recommend that we refer users to the PHP documentation so they can make the right decision for their situation.
Comment #43
darren ohComment #45
darren ohHere is a new patch that
Comment #47
darren ohAdded coding standards fixes.
Comment #49
darren ohComma encoding in session identifiers was throwing off tests. This patch prevents invalid characters in the session identifier by using base 32 encoding instead of base 64.
Comment #50
darren ohPlease make sure smokris is included in the commit credit for raising the problem of underscores in session IDs.
Comment #51
darren ohI’ve been reading further and it looks like the built-in session upload progress extension may have the same limitations as the others, so leaving the server checks alone makes more sense.
Comment #52
damienmckennaGiven that the upload progress doesn't touch PHP thus won't touch Drupal, is there any way to automatically test this? Otherwise, what criteria do we set in order to get this into core?
Comment #54
andypostApc no longer provide upload and core require php 7.0
Comment #57
andypostUploadprogress will be released soon with upcoming PHP 8.0 this week
https://github.com/php/pecl-php-uploadprogress/pull/5#issuecomment-73237...
Comment #58
xjm@andypost suggested there might be known issues with this for PHP 8 compatibility at present, so tagging for the release notes to go in the "Known issues" section with an explanation.
Comment #60
andypostThe uploadprogress got release 2.0 and only yaml PECL extension still not ready but works with patch
Comment #63
wim leersI was checking to make sure this wasn't one of the issues that needed release notes for the upcoming 9.5.0 release, and it isn't. Clarifying that.
Comment #64
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #68
yash.rode commentedComment #69
yash.rode commentedI have applied the patch #51 except one hunk where we Randomly generate a valid PHP session identifier and set it. because #2238561: Use the default PHP session ID instead of generating a custom one so I think we don't need that anymore?
Comment #70
yash.rode commentedI think, Drupal now uses the default session handler instead of creating a custom sessions handler, so removing that part of the code according to #25.
Comment #71
andypostYes, session handling mostly polished nowadays, except few kinds of tests #2484991: Add the session to the request in KernelTestBase, BrowserTestBase, and drush
Comment #72
yash.rode commentedCan someone tell me, how can I test this. I am seeing Enabled (PECL uploadprogress) on the status report page, but I am not able to see the progress bar while uploading the file and the documentation seems outdated to me.
Comment #73
yash.rode commentedIt is working for Apache testing for NGINX
Comment #74
yash.rode commentedThe MR is working for me with Apache as well as NGINX, following is the working snapshot with NGINX.
Comment #75
smustgrave commentedFor the IS update did not test.
Comment #76
yash.rode commentedSorry, I did not get the last comment.
Comment #77
smustgrave commentedtagged for issue summary update years ago but still has not happened.
Comment #78
tim.plunkettCleaning up tags and the IS. I think leaving an issue with a working patch at NR is fine, even if there are other "needs" tags. It still needs review.
Comment #79
smustgrave commentedWould slightly disagree with that. especially if one is for the issue summary to be updated. IS should be clear for reviewers, which is now thanks!
With the MR applied, cached cleared.
I'm on nginx though and when I upload a file I don't see any progress bar just the spinner icon.
Comment #80
andypost@smustgrave which backend are you using after Nginx?
Did you ensure that request buffering is disabled and Nginx has no own uploadprogress extension enabled?
I bet you also have PECL uploadprogress extension enabled if you're using docker image
@yash.rode please provide your configuration from comment #74
I'm sure it need proper docs because there's a lot of a-la-ddev environment exists in a wild and everyone has own build of PHP
Leaving for reviews to figure out who also can't make it work
Comment #81
yash.rode commentedAdded steps to reproduce.
Comment #82
yash.rode commentedComment #83
narendrarI tested it on both Apache and Nginx. Progress bar is visible after changing form display for image to Bar with progress meter.
Screen Recording 2023-07-14 at 11.32.13 AM.mov
Comment #84
smustgrave commentedBased on the video (thanks!) in #83 this appears to be working.
Comment #85
znerol commentedI suggest to try a different approach here. Instead of attempting to restart the session mid-request (that's dangerous for many reasons), let's try to collect the upload progress data before the session gets initialized.
$_SESSIONsuperglobal and add it to some request attribute. Immediately save and close the session before passing control to the underlying middleware.FileWidgetAjaxController.phpto collect the upload progress data from the request attribute.SessionManager.phpA note on the design of this PHP feature. File upload progress data is saved to the session store before the SAPI hands over control to the actual PHP script. It follows that any session store is used which happens to be configured in the execution environment (i.e., via ini settings in any of the configuration files within the search path). Hence, PHP will use whatever
session.*ini setting happens to be in effect prior to the initialization ofSessionManager(via theSessionstack middleware).Instead of instructing people to change their
session.nameini settings, I suggest to just work with the server defaults. This will result in two cookies being used. One for the upload progress (with the namePHPSESSIDin most cases) and a different one with the actual Drupal session data. This shouldn't be a problem with the design outlined above.N.b., calling session_module_name mid-request effectively removes the
session_handlerservice, and as a result any subsequent changes to the session will not be stored to the session record in the database anymore.Comment #86
yash.rode commentedImplementing #85
Comment #87
yash.rode commentedI was trying to implement #85. I have a doubt in that, in
\Drupal\Core\StackMiddleware\PreSessionHandler::handle, I am getting $_SESSION as NULL, so I am not able to access the data to set the attribute. @znerol do you have any idea why would this happen, or where am I going wrong?Comment #88
znerol commentedThanks for taking this on @yash.rode. I left a review comment.
Comment #89
yash.rode commentedWhile working on this I found out that without even the MR/patch the progress bar is already showing, you just need to make sure `Upload progress` is enabled on the status report page. Can someone please confirm this?
I used Apache and nginx (Laravel Valet). So I wonder if this issue is still relevant?
Comment #90
narendrarAgree with @yash.rode.
Tested it on ddev for both apache and nginx as it has uploadprogress enabled by default (https://github.com/ddev/ddev/issues/1249), and once we use Bar with progress meter in Manage form display, we can see the progress bar.
Comment #91
darren ohThis is should be tested with uploadprogress disabled.
Comment #92
andypostAlso testing should be done using built-in web-server which is started with
core/scripts/drupal quick-startComment #93
narendrarComment #94
yash.rode commentedAssigning it for testing, trying to figure out if we need the block of code we are trying to change(
\Drupal\file\Controller\FileWidgetAjaxController::progress's else if part). The progress bar exists without that part of code.Comment #95
yash.rode commentedBefore I go further with #85, I need some help with the existing approach from #51 which is right now part of MR, where we are stopping the Drupal session and starting a PHP session and then doing the opposite. While doing that it is failing at
\Drupal\file\Controller\FileWidgetAjaxController::progresson line 97 with the error ValueError: session_module_name(): Argument #1 ($module) cannot be "user". which is causing the progress bar to not make any progress, can someone help me with this? Thanks in advance.Comment #96
znerol commentedThanks @yash.rode for your work.
It looks like the code now tries to terminate and restart the session mid-request. Don't do that. Instead, start the session in the middleware, read out the upload progress data from the
$_SESSIONsuperglobal, save it in a request attribute and close the session before passing control to the underlying middleware.Comment #97
yash.rode commentedHi @znerol, I was trying #96. If I start the session and end it in the middleware with session_start() and session_write_close() I am getting access denied on the site because, I guess, the session is getting corrupted somehow.
Comment #98
znerol commentedThanks @yash.rode. I was able to reproduce the access-denied page when attempting to use an existing browser window for testing. It doesn't happen when starting with a clean browser (i.e., no cookies for the test domain). Hence, please always test in a clean browser. I recommend to use a new private window in order to investigate session related changes.
Regarding the architecture: Attempting to use two separate sessions with two separate session handlers in one request (which is necessary if we want to use session based upload progress) is not trivial. That said, our very best bet is to do that in a middleware which separates the session handler for uploads as much as possible from the rest of the system.
Comment #99
yash.rode commentedThanks, it is working when the cache is cleared, but $_SESSION is always empty. What am I missing? I tried putting a conditional break point it, We are never reaching it.
Comment #100
znerol commentedTried c0b4f0f7 (last commit in MR by @tim.plunkett). For sure it would be perfect if we could further isolate calls into the custom session by doing that on
/file/progress/only. However, it looks like this is provoking invalidation of the actual session again (i.e., you get logged out and the next request will be access-denied).Looking at the
FileWidgetAjaxControllerI wonder whether it would be possible to replace that with a very simple front controller script which doesn't do anything else than reporting progress. In the ideal case that script wouldn't have any dependency at all to other parts of Drupal. As a result we might be able to completely avoid interaction with the authenticated session.In order to that, it would be necessary to introduce dedicated front controller scripts for each upload method. The decision which implementation to use would happen whenever the upload element is rendered. I think the front controllers could be as simple as this:
core/modules/file/scripts/upload-progress-pecl.phpcore/modules/file/scripts/upload-progress-session.phpPlease note that these are only ideas. I haven't tested any of the implementations. We have custom front controllers for other cases as well (e.g.,
core/modules/statistics/statistics.php). But honestly I'm not sure whether it is wise to replicate that pattern.This could also be explored in contrib if there was a way to alter the front controller script where the upload widget is polling for the progress.
Comment #101
yash.rode commentedI tried the approach described in #100. I am facing the same problem we had with the previous approach-- $_SESSION is empty. Is there something I am missing, can someone have a look?
Comment #102
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #103
yash.rode commentedComment #104
nod_Comment #105
yash.rode commentedThanks for the review, but all I am trying to say is #101 , So basically this approach is not working.
Comment #106
nod_Ah yes sorry, I would try to make it an actual route instead of a single php file for now. If that works we can move on to making the call "lighter" by bypassing some of the Drupal initialization stuff. If that doesn't work we have another issue :)
Comment #107
nod_Which was probably already attempted earlier. I just keep tripping on myself there heh.
Comment #108
znerol commentedTook a look at the front controller approach (c093cd7c). I observed that the post request to
session-upload-progress.phpis empty. However, the PHP docs state that:I guess that the JavaScript part is incomplete in
c093cd7c.Comment #109
yash.rode commentedHi, I tried sending the data, still $_SESSION is empty.
Comment #110
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #111
znerol commentedThere should be a variable with the name set to the value of
session.upload_progress.name. E.g., in my case i find thatsession.upload_progress.namehas the valuePHP_SESSION_UPLOAD_PROGRESS.Hence, there should be a POST variable on every file related request with the name
PHP_SESSION_UPLOAD_PROGRESS.Comment #112
yash.rode commentedWe already have that variable

So in this case we are posting 1202650292.
Comment #113
znerol commentedIf I understand the PHP docs correctly, the POST variable needs to be sent by the initial post and in addition with every XHR request. Also the input highlighted in the screenshot actually shows that the POST variable doesn't have the correct name. Hint: You need to have
<input name="PHP_SESSION_UPLOAD_PROGRESS">somewhere in the source.Comment #114
tim.plunkett@yash.rode helped me get set up today and I'm also having the same issue where despite calling
session_start(), the superglobal$_SESSIONdoesn't exist.It definitely exists when putting a breakpoint in a random place like the node form, and it's full of Symfony variables...
Not sure what is missing here.
Comment #115
yash.rode commentedComment #116
znerol commentedI was trying to implement a minimal demo in pure PHP (i.e, no Drupal, no Frameworks) but haven't had any success yet. If somebody has a POC, please upload it somewhere and post a link here.
The SO post How to make PHP upload_progress SESSION work? has one good answer. It might serve as a starting point.
Comment #117
yash.rode commentedhttps://github.com/TychaK/Track-Upload-Progress-using-Session-Upload-Pro...
this is one of the implementation that I have been referring to. For them $_SESSION is available, and it is not available for us, that's the problem. You can try this implementation as a POC.
Comment #118
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #119
yash.rode commentedComment #120
smustgrave commentedWill stop the bot from running on this while you discuss
Comment #121
tim.plunkett@znerol would be great to get your thoughts on the PoC that Yash linked to in #117
Comment #122
smustgrave commented@znerol just following up if you had a chance to take a look?
Comment #123
smustgrave commentedSeems this one stalled some. The MR is no longer mergable though so moving to NW due to that.
Comment #124
klemendev commentedIs this related to https://www.drupal.org/project/drupal/issues/2833968?
Comment #125
yash.rode commentedYes, they are related. Once #2833968: Upload progress using jQuery.form plugin instead of 3rd party PHP libraries is fixed, this problem will also be resolved. The PHP session approach is not working, so we will have to go with the jQuery.form plugin approach discussed in the other issue.
Comment #126
smustgrave commented@yash.rode would you say this is duplicate now?
Can move over credit if so