I’ve got a perl script backing up our TeamCity server via the REST API as follows:
use strict;
use LWP::UserAgent;
use HTTP::Request::Common qw{ POST GET }
# ... code ommitted for brevity ... #
my $url = 'http://teamcity:8080/httpAuth/app/rest/server/backup';
my $req = POST( $url . '?includeConfigs=true&includeDatabase=true&includeBuildLogs=true&fileName=' . $filename);
$req->authorization_basic($username, $password);
my $resp = $ua->request($req);
I tried posting the content more in line with the documentation for HTTP:Request, but for some reason it fails, complaining that I haven’t specified a file name:
# This fails
my $req= POST( $url, [ 'includeConfigs' => 'true',
'includeDatabase' => 'true',
'includeBuildLogs' => 'true',
'fileName' => $filename,
] );
Yet, when I look at the backend REST log for TeamCity, the full request seems to have made it intact, and is identical to the one that passes above.
Log of successful command:
[2012-12-13 15:02:38,574] DEBUG [www-perl/5.805 ] - rver.server.rest.APIController - REST API request received: POST '/httpAuth/app/rest/server/backup?includeConfigs=true&includeDatabase=true&includeBuildLogs=true&fileName=foo', from client 10.126.31.219, authenticated as jsmith
Log of failed command:
[2012-12-13 14:57:00,649] DEBUG [www-perl/5.805 ] - rver.server.rest.APIController - REST API request received: POST '/httpAuth/app/rest/server/backup?includeConfigs=true&includeDatabase=true&includeBuildLogs=true&fileName=foo', from client 10.126.31.219, authenticated as jsmith
Is there any other hidden difference between the two methods of making a POST request that could be causing the failure?
UPDATE: Here is the result of each request when printed via Data::Dumper
Successful POST:
$VAR1 = bless( {
'_content' => '',
'_uri' => bless( do{\(my $o = 'http://teamcity:8080/httpAuth/app/rest/server/backup?includeConfigs=true&includeDatabase=true&includeBuildLogs=true&fileName=foo')}, 'URI::http' ),
'_headers' => bless( {
'content-type' => 'application/x-www-form-urlencoded',
'content-length' => 0,
'authorization' => 'Basic c3lzQnVpbGRTeXN0ZW1JOnBhaWQuZmFpdGg='
}, 'HTTP::Headers' ),
'_method' => 'POST'
}, 'HTTP::Request' );
Unsuccessful POST:
$VAR1 = bless( {
'_content' => 'includeConfigs=true&includeDatabase=true&includeBuildLogs=true&fileName=foo',
'_uri' => bless( do{\(my $o = 'http://teamcity:8080/httpAuth/app/rest/server/backup')}, 'URI::http' ),
'_headers' => bless( {
'content-type' => 'application/x-www-form-urlencoded',
'content-length' => 75,
'authorization' => 'Basic c3lzQnVpbGRTeXN0ZW1JOnBhaWQuZmFpdGg='
}, 'HTTP::Headers' ),
'_method' => 'POST'
}, 'HTTP::Request' );
I think your server-side script can only handle
GETparameters encoded in the URL, notPOSTdata transmitted via standard intput. Note that there are several different methods described by HTTP, these areGET,POST,HEAD,DELETEetc. And then there are two ways of passing data to an application on the server. Most often one of those ways is also calledGET parametersand the other one is calledPOST databecause theGET parametersare usually used with the HTTPGETmethod andPOST datais usually used for the HTTPPOSTmethod. However, they don’t have to. And I think you’re mixing up the HTTPPOSTmethod withGET parametersin the successful case and withPOST datain the unsuccessful case.GET parametersare passed via the URL by, most often by appending?to the URL followed by the actual key/value pais. Those are available via certain environment varialbes to the script running on the server. It’s up to the script to split the variables at the&, split key/value pairs on=and undo the escaping.For
POST datathe environment variableCONTENT_LENGTHtells the script how many bytes to read from its standard input. The actual key/value pairs are transmitted via a different encoding, usually as multipart encoded content. Yes,POSTHTTP requests (mostly from HTML<form>s) can also be sent URL-encoded likeGET parameters, but there’s a length limit imposed by the HTTP standard on the URLs including all parameters. Hence the method of transferring the data via standard input, and not via the URL.Now it looks like your server-side script can evaluate URL-encoded parameters (aka.
GET parameters) parameters but not data posted to it via standard input. Even though you use thePOSTHTTP method/verb you don’t actually transmit the values asPOST datavia standard input in your successful case. You could simply swapPOST(...)forGET(...)in that case and it should still work.In your unsuccessful case you use the
POSTHTTP method and thePOST dataway of transmitting the values.My verbiage here may be wrong in cases, but the fundamentals should still be OK.