I was running into an issue with using the firefox addon "Poster" to test the services module as suggested here: http://drupal.org/node/790416
The RESTServer.inc file contains at line #252
private function parseRequest($method, $controller) {
switch ($method) {
case 'POST':
if ($_SERVER['CONTENT_TYPE'] == 'application/x-www-form-urlencoded') {
return $_POST;
}
Through debugging I found that actual value in $_SERVER['CONTENT_TYPE'] when I am testing was "application/x-www-form-urlencoded; charset=UTF-8"
Of course the above code was assuming this was not a "POST" request. I did little more research to see if this was valid value for $_SERVER['CONTENT_TYPE']. Looking in the documentation for $_SERVER pointed me to theCGI/1.1 specification.
On this page you can find
4.1.3. CONTENT_TYPE
If the request includes a message-body, the CONTENT_TYPE variable is
set to the Internet Media Type [6] of the message-body.CONTENT_TYPE = "" | media-type
media-type = type "/" subtype *( ";" parameter )
type = token
subtype = token
parameter = attribute "=" value
attribute = token
value = token | quoted-stringThe type, subtype and parameter attribute names are not
case-sensitive. Parameter values may be case sensitive. Media types
and their use in HTTP are described section 3.7 of the HTTP/1.1
specification [4].There is no default value for this variable. If and only if it is
unset, then the script MAY attempt to determine the media type from
the data received. If the type remains unknown, then the script MAY
choose to assume a type of application/octet-stream or it may reject
the request with an error (as described in section 6.3.3).Each media-type defines a set of optional and mandatory parameters.
This may include a charset parameter with a case-insensitive value
defining the coded character set for the message-body.
My reading of this is that the value contains the media-type(divided into type and subtype) as the current code is expecting but also can contain other parameters after a semi colon. So "application/x-www-form-urlencoded; charset=UTF-8" appears to be a valid value that contains the extra parameter charset.
I am attaching a patches which changes the code to:
private function parseRequest($method, $controller) {
switch ($method) {
case 'POST':
$content_type_args = explode(';', $_SERVER['CONTENT_TYPE']);
$media_type = $content_type_args[0];
if ($media_type == 'application/x-www-form-urlencoded') {
return $_POST;
}
return array();
This checks against just media-type portion of the string and would ignore other paramters. It should work whether the parameters are there or not.
Also I have added a "return array();" so that logic doesn't continue to fall through to the "PUT" switch case. This was happening my case b/c it didn't have expected $_SERVER['CONTENT_TYPE'] value. The program was then assuming that this was "PUT". I think this "return array();" or a break should be added to the code whether my other changes are expected or not.
Comments
Comment #1
tedbowattaching patch for review
Comment #2
voxpelli commentedThis should already work. If not then it is the PUT part of parseRequest() that should be patched and not the POST part.
This parsing should use self::parseContentHeader() - but since the PUT-case already does that it is probably unnecessary to do it here as well. Only reason would be to avoid the performance impact of having to parse the request content twice. requestParsers() will otherwise make the request content be parsed by RESTServer::parseURLEncoded().
This makes it impossible to do POST:s with any content-type but application/x-www-form-urlencoded. POST-requests should be possible to make with any type of content body.
Powered by Dreditor.
Comment #3
tedbow@voxpelli thanks I am still getting to know the workings of this module.
Am I correct though in thinking that the "POST" case should not be testing the value of $_SERVER['CONTENT_TYPE'] directly?
I have new patch that does the "self::parseContentHeader" call before the switch statement. This way the "CASE" can test the $type['value'] returned.
I also removed the return statement that I added.
Comment #4
voxpelli commentedThat will probably result in a slight performance improvement, but doesn't fix any bug. If there's a bug then it should be fixed prior to doing this performance improvement. Can you explain further what doesn't work when you're using an unpatched version of Services? If it does work with an unpatched version then this should be changed into a feature request and retitled to reflect that it is about performance.
You need to check if $type have any value.
Powered by Dreditor.
Comment #5
tedbow@voxpelli, ok I figured out why my current situation is not working. I think it is combination of some special code I am running and Services code having the problem I specified in number #1.
I handling a service request from an outside app that is trying to create Users. The app is going to using a POST request but is not going to be sending the info exactly how the Services Rest Server is expecting. In a hook_init function I change the $_POST variable to what the Rest Server expects(rename the parameter "account") .
So after my hook_init the $_POST variable is exactly as the Rest Server wants it. The problem is that when it gets to "parseRequest" the current code doesn't recognise it as a POST request b/c of the reason I specified in #1.
It then falls through the switch case and gets the input by reading the raw input stream. Of course my hook_init method didn't affect the this input like it did the $_POST variable.
The problem I see with the current code is that it appears when first looking at the code that if a POST's content type is "application/x-www-form-urlencoded" then it will use the $_POST variable for $data. In fact though if $_SERVER['CONTENT_TYPE'] has any valid parameters it will not use the the $_POST variable but rather the raw input stream.
The core of the problem is when I rely on what I believe to be the intention of the code, i.e., If the post is a "form-urlencoded" then it will use the $_POST this doesn't work.
One way around this would be adding the ability to alter what is parsed from the request. Something like this:
Sorry to be so long winded.
Comment #6
voxpelli commentedI see - I agree that an alter ('rest_server_request_parsed'?) in that place would make sense and that it would be good if $_POST would be used more expectable. The reason it sometimes needs to be parsed separate is because PHP doesn't parse the form-data on PUT:s if I remember correctly.
Can you add an alter to your previous patch? Change the status to "need review" once you have and I'll check it.
Comment #7
tedbow@voxpelli et all,
I have attached the patch with change requested in #6.
Here is the example of how I am using the alter in my case
I don't like the fact that I have to check the callback to determine where I am at. It would nice if I could check for the resource, "user" and the operation, "create" but this not available in the controller object at that point, correct? I also don't think there is anything else that should be sent in the context but maybe there is.
Reading the docs for drupal_alter here: http://api.drupal.org/api/drupal/includes--module.inc/function/drupal_al...
It says:
I was wondering if I should clone the $controller variable so it cannot get altered but don't know if it is worth the performance hit that the clone call would add. Any thoughts?
Comment #8
tedbowSorry attached incomplete patch file. Please regard #7's patch.
Also this patch I changed the way the $controller is being passed so the implementation would be
Comment #9
kylebrowning commentedCommitted, thanks.
Comment #10
voxpelli commentedStill missing an isset() here?
Powered by Dreditor.
Comment #11
kylebrowning commentedthx voxpelli, amended to previous commit and fixed in 7.x
Comment #12
tedbow@voxpelli thanks for catching that
Comment #13
kylebrowning commented