I have been trying for weeks to properly format a REST request to the Amazon AWS S3 API using the available examples on the web but have been unable to even successfully connect.
I have found the code to generate a signature, found the proper method for formatting the "string to encode", and the http headers. I have worked my way through the signatureDoesNotMatch errors just to get a Anonymous users can not perform copy functions, Please authenticate message.
I have a working copy of an Adobe Flex application that successfully uploads files, but with their "original" file name. The point of using the REST with the Amazon API is to perform a PUT (copy) of the file, just so I can rename it to something my back end system can use.
If I could find a way to get this REST submission to work, or perhaps a way to specify a "new" filename within Flex while uploading I could avoid this whole REST situation all together.
If anyone has successfully performed a PUT/Copy command on the Amazon API via REST I would be very interested in how this was accomplished - OR - if anyone has been able to change the destination file name using the Flex fileReference.browse() method I would also be eternally grateful for any pointers.
PHP code for this is as follows:
$aws_key = 'removed_for_security';
$aws_secret = 'removed_for_security';
$source_file = $uploaded_s3_file; // file to upload to S3 (defined in above script)
$aws_bucket = 'bucket'; // AWS bucket
$aws_object = $event_file_name; // AWS object name (file name)
if (strlen($aws_secret) != 40) die("$aws_secret should be exactly 40 bytes long");
$file_data = file_get_contents($source_file);
if ($file_data == false) die("Failed to read file " . $source_file);
// opening HTTP connection to Amazon S3
$fp = fsockopen("s3.amazonaws.com", 80, $errno, $errstr, 30);
if (!$fp) die("$errstr ($errno)\n");
// Uploading object
$file_length = strlen($file_data); // for Content-Length HTTP field
$dt = gmdate('r'); // GMT based timestamp
// preparing String to Sign (see AWS S3 Developer Guide)
// preparing string to sign
$string2sign = "PUT
{$dt}
/{$aws_bucket}/{$aws_object}";
// preparing HTTP query
// $query = "PUT /".$aws_bucket."/".$event_file_name." HTTP/1.1
$query = "PUT /" . $event_file_name . " HTTP/1.1
Host: {$aws_bucket}.s3.amazonaws.com
Date: {$dt}
x-amz-copy-source: /{$aws_bucket}/{$current_s3_filename}
x-amz-acl: public-read
Authorization: AWS {$aws_key}:" . amazon_hmac($string2sign) . "\n\n";
$query .= $file_data;
$resp = sendREST($fp, $query);
if (strpos($resp, '') !== false) {
die($resp);
}
echo "FILE uploaded\n";
// done
echo "Your file's URL is: http://s3.amazonaws.com/{$aws_bucket}/{$aws_object}\n";
fclose($fp);
// Sending HTTP query and receiving, with trivial keep-alive support
function sendREST($fp, $q, $debug = true){
if ($debug) echo "\nQUERY<<{$q}>>\n";
fwrite($fp, $q);
$r = '';
$check_header = true;
while (!feof($fp)) {
$tr = fgets($fp, 256);
if ($debug) echo "\nRESPONSE<<{$tr}>>";
$r .= $tr;
if (($check_header) && (strpos($r, "\r\n\r\n") !== false)) {
// if content-length == 0, return query result
if (strpos($r, 'Content-Length: 0') !== false) {
return $r;
}
}
// Keep-alive responses does not return EOF
// they end with \r\n0\r\n\r\n string
if (substr($r, -7) == "\r\n0\r\n\r\n") {
return $r;
}
}
return $r;
}
// hmac-sha1 code START
// hmac-sha1 function: assuming key is global $aws_secret 40 bytes long
// read more at http://en.wikipedia.org/wiki/HMAC
// warning: key($aws_secret) is padded to 64 bytes with 0x0 after first function call
function amazon_hmac($stringToSign) {
// helper function binsha1 for amazon_hmac (returns binary value of sha1 hash)
if (!function_exists('binsha1')) {
if (version_compare(phpversion(), "5.0.0", ">=")) {
function binsha1($d) { return sha1($d, true); }
} else {
function binsha1($d) { return pack('H*', sha1($d)); }
}
}
global $aws_secret;
if (strlen($aws_secret) == 40) {
$aws_secret = $aws_secret . str_repeat(chr(0), 24);
}
$ipad = str_repeat(chr(0x36), 64);
$opad = str_repeat(chr(0x5c), 64);
$hmac = binsha1(($aws_secret ^ $opad) . binsha1(($aws_secret ^ $ipad) . $stringToSign));
return base64_encode($hmac);
}
// hmac-sha1 code END
When I submit a malformed or incorrect header I get the corresponding error message as expected:
Query:
PUT /bucket/1-132-1301047200-1.jpg HTTP/1.1 Host: s3.amazonaws.com x-amz-acl: public-read Connection: keep-alive Content-Length: 34102 Date: Sat, 26 Mar 2011 00:43:36 +0000 Authorization: AWS -removed for security-:GmgRObHEFuirWPwaqRgdKiQK/EQ=
HTTP/1.1 403 Forbidden
x-amz-request-id: A7CB0311812CD721
x-amz-id-2: ZUY0mH4Q20Izgt/9BNhpJl9OoOCp59DKxlH2JJ6K+sksyxI8lFtmJrJOk1imxM/A
Content-Type: application/xml
Transfer-Encoding: chunked
Date: Sat, 26 Mar 2011 00:43:36 GMT
Connection: close
Server: AmazonS3
397 SignatureDoesNotMatchThe request signature we calculated does not match the signature you provided. Check your key and signing method.50 55 54 0a 0a 0a 53 61 74 2c 20 32 36 20 4d 61 72 20 32 30 31 31 20 30 30 3a 34 33 3a 33 36 20 2b 30 30 30 30 0a 78 2d 61 6d 7a 2d 61 63 6c 3a 70 75 62 6c 69 63 2d 72 65 61 64 0a 2f 6d 6c 68 2d 70 72 6f 64 75 63 74 69 6f 6e 2f 31 2d 31 33 32 2d 31 33 30 31 30 34 37 32 30 30 2d 31 2e 6a 70 67A7CB0311812CD721ZUY0mH4Q20Izgt/9BNhpJl9OoOCp59DKxlH2JJ6K+sksyxI8lFtmJrJOk1imxM/AGmgRObHEFuirWPwaqRgdKiQK/EQ=PUT Sat, 26 Mar 2011 00:43:36 +0000 x-amz-acl:public-read /bucket/1-132-1301047200-1.jpg-removed for security- 0
but when sending properly formatted requests, it says I'm not authenticated:
Query being used:
PUT /1-132-1301047200-1.jpg HTTP/1.1 Host: bucket.s3.amazonaws.com Date: Sat, 26 Mar 2011 00:41:50 +0000 x-amz-copy-source: /bucket/clock.jpg x-amz-acl: public-read Authorization: AWS -removed for security-:BMiGhgbFnVAJyiderKjn1cT7cj4=
HTTP/1.1 403 Forbidden
x-amz-request-id: ABE45FD4DFD19927
x-amz-id-2: CnkMmoF550H1zBlrwwKfN8zoOSt7r/zud8mRuLqzzBrdGguotcvrpZ3aU4HR4RoO
Content-Type: application/xml
Transfer-Encoding: chunked
Date: Sat, 26 Mar 2011 00:41:50 GMT
Server: AmazonS3
AccessDenied
Anonymous users cannot copy objects. Please authenticate
ABE45FD4DFD19927CnkMmoF550H1zBlrwwKfN8zoOSt7r/zud8mRuLqzzBrdGguotcvrpZ3aU4HR4RoO 0
Date: Sat, 26 Mar 2011 00:41:50 GMT
Connection: close
Server: AmazonS3