I am facing a problem when a remote web client with slow connection fails to send complete POST request with multipart/form-data
content but PHP still uses partially received data to populate $_POST
array. As a result one value in $_POST
array can be incomplete and more values can be missing. I tried to ask same question in Apache list first and got an answer that Apache doesn't buffer the request body and passes it to PHP module as a
giant blob.
Here is my sample POST request:
POST /test.php HTTP/1.0
Connection: close
Content-Length: 10000
Content-Type: multipart/form-data; boundary=ABCDEF
--ABCDEF
Content-Disposition: form-data; name="a"
A
--ABCDEF
You can see that Content-Length
is 10000
bytes, but I send just one var a=A
.
The PHP script is:
<?php print_r($_REQUEST); ?>
Web server waits for about 10 seconds for the rest of my request (but I don't send anything) and then returns this response:
HTTP/1.1 200 OK
Date: Wed, 27 Nov 2013 19:42:20 GMT
Server: Apache/2.2.22 (Debian)
X-Powered-By: PHP/5.4.4-14+deb7u3
Vary: Accept-Encoding
Content-Length: 23
Connection: close
Content-Type: text/html
Array
(
[a] => A
)
So here is my question: How can I verify in PHP that the post request was received completely? $_SERVER['CONTENT_LENGTH']
would show 10000
from the request header, but is there a way to check the real content length received?
I guess that the remote client is actually a browser with HTML page. otherwise, let me know and i'll try to adapt my solution.
You can add field <input type="hidden" name="complete">
(for example) as the last parameter. in PHP
check firstly whether this parameter was sent from client. if this parameter sent - you can be sure that you got the entire data.
Now, i'm not sure that the order of parameters must be preserved according the RFC (of both, HTML and HTTP). but i've tried some variations and i saw that the order kept indeed.
Better solution will be, calculate (on client side) hash of the parameters and send him as another parameter. so you can be absolutely sure that you got the entire data. But this is starting to sound complicated...
As far as I know there is no way to check if the size of received content matches the value of the Content-Length
header when using multipart/form-data
as Content-Type
, because you cannot get hold of the raw content.
1) If you can change Content-Type
(to application/x-www-form-urlencoded
for example) you can read php://input
, which will contain the raw content of the request. The size of php://input
should match Content-Length
(assuming the value of Content-Length
is correct). If there's a match, you can still use $_POST
to get the processed content (regular post data). Read about php://input
here.
2) Or you can serialize the data on the client and send it as text/plain
. The server can check the size the same way as described above. The server will need to unserialize the received content to be able to work with it. And if the client generates a hash of the serialized data and send it along in a header (X-Content-Hash
for example), the server can also generate a hash and check if it matches the one in the header. You won't need to check the hash, and can be a 100% sure the content is correct.
3) If you cannot change Content-Type
, you'll need something different from size to verify the content. The client could use an extra header (something like X-Form-Data-Fields
) to sum up the fields/keys/names of the content you're sending. The server could then check if all fields mentioned in the header are present in the content.
4) Another solution would be for the client to have a predefined key/value as last entry in the content. Something like:
--boundary
Content-Disposition: form-data; name="_final_field_"
TRUE
--boundary--
The server can check if that field is present in the content, if so the content must be complete.
update
When you need to pass binary data, you can't use option 1, but can still use option 2:
The client can base64
encode the binary entries, serialize the data (with any technique you like), generate a hash of the serialized data, send the hash as header and data as body.
The server can generate a hash of the received content, check the hash with the one in the header (and report a mismatch), unserialize the content, base64
decode the binary entries.
This is a bit more work then plainly using multipart/form-data
, but the server can verify with a 100% guarantee the content is the same as what the client sent.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With