No reply when using flush(), session cookies and long execution time - php

I could reduce my problem to the following script I put on my server:
<?php
session_start();
header('Content-type: text/plain', TRUE);
flush();
sleep(300);?>
When I connect to this script using:
GET /test.php HTTP/1.1
Host: localhost
I get the header back right away as expected:
HTTP/1.1 200 OK
Date: Sat, 03 Nov 2012 20:15:53 GMT
Server: Apache/2.2.22 (Fedora)
X-Powered-By: PHP/5.3.17
Set-Cookie: ZDEDebuggerPresent=php,phtml,php3; path=/
Set-Cookie: PHPSESSID=m7bmvblakkil96rqjq7j8f0f42; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Connection: close
Transfer-Encoding: chunked
Content-Type: text/plain; charset=UTF-8
If I then kill the connection and reconnect using:
GET /proxy/test.php HTTP/1.1
Host: localhost
Cookie: PHPSESSID=knkeaq2ao0cllpcci0rnassqj4
I don't get any header back anymore until I restart my webserver.
I would have expected to again retrieve the response header right away.
I have really no idea why I get this behavior. Maybe it's some kind of a bug? Or I'm misunderstanding some behavior.
It would be great if somebody could help me with this as I really don't have a clue what's going on.
Ps:
Im running Apache/2.2.22 with PHP 5.3.17 on fedora 17, but the server packages are out of the fedora 16 repos as I need zend debugger which does not work with the PHP coming with fedora 17.

Because your session is still open in the first request (for 300 seconds) you cannot start using it in the second. It will wait until the first session is closed and written to the file-system before it will attempt to read it for the second request. Because you cannot be sure the first request wants to modify the same session-variables which are already in use by the second request.
You can use session_write_close() before the sleep so that the session variables become available immediatly. But then you cannot change any session variables after that.

Related

How does HTTP max-age works and how to expire cache after some time?

I manage the HTTP caching in my applications. And it's not working as I think it should. Let's get to an actual example:
With the first serve of my PHP page I serve the following HTTP headers:
HTTP/1.1 200 OK
Date: Mon, 12 Dec 2016 16:39:33 GMT
Server: Apache/2.4.9 (Win64) PHP/5.5.12
Expires: Tue, 01 Jan 1980 19:53:00 GMT
Cache-Control: private, max-age=60, pre-check=60
Last-Modified: Mon, 12 Dec 2016 15:57:25 GMT
Etag: "a2883c859ce5c8153d65a4e904c40a79"
Content-Language: en
Content-Length: 326
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
My application manage the validation of Etags and send 304 if nothing has changed and when you refresh the page in the browser (F5) you get (if nothing has changed server side):
HTTP/1.1 304 Not Modified
Date: Mon, 12 Dec 2016 16:43:10 GMT
Server: Apache/2.4.9 (Win64) PHP/5.5.12
Connection: Keep-Alive
Keep-Alive: timeout=5, max=100
Since I serve Cache-Control: private with max-age=60 I would expect that after one minute the cache will be considered obsolete by the browser and it will request a fresh copy (equivalent of a Ctrl+F5 reload) but instead the cache is still valid several days after it's max-age.
Do I misunderstood these HTTP mechanism? Do I send something wrong or maybe miss something?
If a cached response is within the max-age, then it is considered fresh.
If it exceeds the max-age, then it is considered stale.
If a browser needs a resource and it has a fresh copy in the cache, then it will use that without checking back with the server.
If the browser has a stale copy then it will validate that against the server (in this case, using Etags) to see if it needs a new copy of it the cached copy is still OK.

Sessions in PHP 5.3.3 and kirby

Situation
I have implemented a modified version of the kirby Auth plugin on a client website, it basically works this way
User accounts are simple kirby content files with the title serving as the login and a password field containing a bcrypt encrypted version of the users password
On the website, visitors can click on "Customer Area", which goes to a template managed by the modified auth plugin (which redirects to the "login" form).
The website is multilingual. So the accounts file will look like content/extranet/login/login.fr.txt or as an example content/extranet/bastian/bastian.fr.txt, this is what the modified Auth plugin is looking for, it finds the file and reads it, checks the password, and the logins the users.
This system requires account pages title to be the same as their URL, and to always be invisible pages
User is logged in and can only see in the « secure » template the files present in his folder
This all works good here on my local MAMP server as you can see in the screenshots, but on the production server http://www.driving-evolution.com, this doesn’t work, and i don’t know why, i have looked in a lot of places and i don’t understand what is going on. (it doesn’t work on the staging server either)
On the production server, wether you enter a good or a bad login, the form doesn’t show any error message, and doesn’t log the user in either. At first i thought this is maybe because of my bcrypt install, but it is not, as i disabled it (and used plain passwords instead) and it still didn’t work.
The issue seems NOT to be from the plugin here but from a difference between my live PHP stack and local php stack (my guess is on the php session handling).
Remote PHP is 5.3.3
Here is a sample output of CURL on both installations :
WORKING (Local)
curl -d "username=test&password=test" -i devo.loc/fr/login
HTTP/1.1 302 Found
Date: Tue, 27 May 2014 12:59:49 GMT
Server: Apache/2.2.25 (Unix) mod_ssl/2.2.25 OpenSSL/0.9.8y DAV/2 PHP/5.5.3
X-Powered-By: PHP/5.5.3
Set-Cookie: PHPSESSID=9249a942248a2382d8eb10090bf5d825; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: authFrontend=mc06csp25; expires=Wed, 28-May-2014 12:59:49 GMT; Max-Age=86400; path=/
Set-Cookie: PHPSESSID=b38ec0b342356c2c38778e4a6925f085; path=/
Set-Cookie: authFrontend=8qyyn; expires=Wed, 28-May-2014 12:59:49 GMT; Max-Age=86400; path=/
Location: http://devo.loc/fr/extranet
X-UA-Compatible: IE=edge
Vary: Accept-Encoding
Content-Length: 0
Content-Type: text/html; charset=utf-8
FAILING (Remote)
curl -d "username=test&password=test" -i www.driving-evolution.com/fr/login
HTTP/1.1 200 OK
Date: Tue, 27 May 2014 13:01:32 GMT
Server: Apache
X-Powered-By: PHP/5.3.3
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: PHPSESSID=6j9o1i4djetojtbg444i51efm7; path=/
Vary: Accept-Encoding
X-UA-Compatible: IE=edge
Connection: Keep-Alive, close
Transfer-Encoding: chunked
Content-Type: text/html
USEFUL LINKS
REMOTE PHPVER : http://www.driving-evolution.com/phpver.php
SCREENSHOTS when it works on local
Hope someone will be able to help me on this ! Ask for any further info and i will give it.
Turns out it WAS a problem with the CMS, file caching was turned on and not ignoring the "login", "logout", and "extranet" files (all related to customer area), had to add :
c::set('cache.ignore', array('api', 'sitemap', 'extranet', 'account', 'login', 'logout'));
to my kirby config (api and sitemap are unrelated).
This fixes the problem as the login form was cached and thus not hitting anything on the server. Now form is not cached.

PHP file download with mod-xsendfile does not download entire file

I am having problem serving downloads from our website. Large files just won't download in full. Download will stop somewhere in between... Example this file (size cca 172MB) won't download in full size (there are other files also).
I switched from entirely PHP-base download script, the one included in Kohana framework:
return download::force($filePath);
to a mod-xsendfile solution. I was reading about the possible problems with PHP based download scripts and large file and cam over mod-xsendfile is the right solution... Well looks like not, I am getting the same result with both techniques. My current download implementation using mod-xsendfile headers like this:
header("X-Sendfile: $filePath");
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
What am I doing wrong?
UPDATE:
I used this HTTP sniffer to check response headers and this is the result if it helps solving this problem.
Status: HTTP/1.1 200 OK
Server: Apache
Set-Cookie: dewesoftsession=63ms5j67kc231pr4bpm8cmg1f7; expires=Sat, 30-Mar-2013 11:36:59 GMT; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: dewesoftsession=63ms5j67kc231pr4bpm8cmg1f7; expires=Sat, 30-Mar-2013 11:36:59 GMT; path=/
Content-Disposition: attachment; filename="DEWESoft_FULL_7_0_5.exe"
Last-Modified: Mon, 24 Sep 2012 12:50:12 GMT
ETag: "25814de-ac291e9-4ca7207c7fcd9"
Content-Type: application/octet-stream
Content-Length: 180523497
Date: Sat, 30 Mar 2013 09:37:01 GMT
X-Varnish: 294312007
Age: 2
Via: 1.1 varnish
Connection: close
X-Varnish-Cache: MISS
After couple of days we managed to find what cause the problem. Varnish has a start-up parameter called send_timeout which is set to 600s by default. With large file downloads you might run into this timeout which will cause your download to be interrupted.
So increasing Varnish's send_timeout parameter will help you solve this kind of issue.

HTTP Headers difference - load page incrementally

I have an HTML page that shows a progress bar as it steps through a process. It uses flush() to send the data to the browser. I'm trying to get this to work in a Zend process which I'm short circuiting by specifically sending a header, content, then ending the process with an exit command.
The HTML page displays correctly (progress bar steps through being done). The Zend/PHP page only shows the finished page (not the steps). I'm assuming this is a header problem since the method (flush()) is identical.
In Chrome, the header for the HTML page comes up as:
HTTP/1.1 200 OK
Date: Fri, 27 Jul 2012 14:38:07 GMT
Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/0.9.8r DAV/2 PHP/5.3.2
X-Powered-By: PHP/5.3.2
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html
And the header for the Zend/PHP page comes up as:
HTTP/1.1 200 OK
Date: Fri, 27 Jul 2012 14:44:13 GMT
Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/0.9.8r DAV/2 PHP/5.3.2
X-Powered-By: PHP/5.3.2
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-cache
Pragma: no-cache
Keep-Alive: timeout=5, max=98
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8
The only header information I'm specifying in the PHP is:
header('Content-Type: text/html; charset=utf-8');
I'm using this code from this page: http://w3shaman.com/article/php-progress-bar-script
Any help would be appreciated. Thanks.
Call ob_flush() before you call flush() as Zend could have output buffering activated.
Mathieu had the fix. Adding ob_flush() before flush() in the Zend/PHP page fixed the problem. I'm not sure if Zend is activating output buffering as suggested or not.

Why do I have two session id in my header?

I'm using PHP5.3, Apache 2.2 and Zend Framework 1.11 and when I do a request with curl, I got two Set-Cookie headers with two differents session id.
Any ideas?
> POST /api/register HTTP/1.1
> Authorization: Basic Ymd1ZXJ0eQ==
> Host: api.domain.tld
> Accept: */*
> Content-Length: 66
> Content-Type: application/x-www-form-urlencoded
>
< HTTP/1.1 200 OK
< Date: Wed, 07 Dec 2011 13:27:07 GMT
< Server: Apache
< X-Powered-By: PHP/5.3.8
< Set-Cookie: SESSID=vtvackh55bj1up3ouoq4bhk545; expires=Thu, 06-Dec-2012 19:15:53 GMT; path=/
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
< Pragma: no-cache
< Set-Cookie: SESSID=79a1g98e0pkqlq8fo7elpe9492; expires=Thu, 06-Dec-2012 19:15:53 GMT; path=/
< Vary: Accept-Encoding,User-Agent
< Content-Length: 128
< Content-Type: application/json
Your server application is emitting the Set-Cookie header twice. This can come from anywhere, impossible to tell without seeing all the code.
Look for things that deal with session handling. If there is third party code involved, disable it and see if the problem persists.
From Zend 1.11 documentation:
By default, Zend_Auth provides persistent storage of the identity from a successful authentication attempt using the PHP session.
Maybe you used somewhere AuthAdapter which start session and later on you are trying to manipulate it on your own starting another session with different session id.
I've experienced this issue in Zend 2.
I first used AuthenticationService to authenticate and after successful authentication I tried to use Zend Session Container to manipulate session.
I've used session container to explicitly regenerateId() and set some additional headers like secure, http_only and as a result I had two session headers like you.

Categories