Přejít k navigační liště

Zdroják » Databáze » Amazon SimpleDB prakticky v PHP

Amazon SimpleDB prakticky v PHP

Články Databáze

V předchozím článku o Amazon SimpleDB jsme si popsali principy funkce této databáze. Dnes si ukážeme, jak s touto databází pracovat přímo na úrovni API. Amazon SimpleDB nabízí pro přístup k službám rozhraní SOAP a REST. V článku si ukážeme, jak využít to druhé, a to z prostředí jazyka PHP.

Velké webové služby většinou nabízejí vlastní knihovny pro nejpoužívanější jazyky. Ani v případě Amazon SimpleDB tomu není jinak – k dispozici jsou knihovny pro PHP, Javu, Perl, C# a VB.NET. Ačkoli nejsem obecně příznivcem znovuvynalézání kola a řídím se heslem, že je lépe použít vyhovující knihovnu než opakovat stejné chyby jako její autoři, v případě SimpleDB a PHP jsem na vážkách – PHP knihovna nabízená Amazonem mi připadá poněkud těžkopádná. Sice je dobře dokumentovaná a testovatelná, ale osobně mi připadá poměrně nešikovně uspořádaná (jde spíš o otázku preferencí než o rozhodnutelný fakt).

Ať tak či onak, faktem je, že na webu lze nalézt spousty knihoven třetích stran, jejichž autorům zjevně kód od Amazonu nevyhovoval, takže se rozhodli udělat vlastní knihovny, které sahají od jednosouborových wrapperů po komplexní knihovny pro celé Amazon AWS. Jako kuriozitu bych zmínil Super SimpleDB, což je knihovna, která nad SimpleDB simuluje SQL dotazy.

Naštěstí je „REST“ rozhraní SimpleDB bohatě dokumentované, takže není problém s ním pracovat – vystačíme si v zásadě se základní schopností „poslat HTTP GET požadavek“ a s hashovacími funkcemi. V tomto článku si ukážeme, jak vytvořit požadavek na SimpleDB, jak ho podepsat a jak jej poslat.

Poznámka: Amazon své rozhraní nazývá „REST“, ačkoli očividně nesplňuje základní znaky tohoto architektonického stylu – např. pro všechny požadavky, včetně těch co mění data, používá HTTP GET (pro zajímavost – o POSTu se zmiňuje dev guide takto: „If the length of the query string that you are constructing exceeds the maximum length of the HTTP GET URL, use HTTP POST and submit the query string parameters in the body of the message“), přístup není orientovaný na záznamy, ale na procedury atd. Ve skutečnosti jde spíš o „RPC over HTTP“. V dalším textu se ale přidržím terminologie Amazonu a budu toto rozhraní označovat, kvůli zachování konzistence s dokumentací, jako „REST“. Zájemce o podrobnější vysvětlení problematiky RESTu a rozdílu proti RPC odkážu na komentáře Petera Rybára.

Autentizace SimpleDB

SimpleDB používá pro přístup ke službám stejné údaje jako ostatní AWS služby – tedy unikátní dvojici řetězců Access Key a Secret Key. Access Key je posílán spolu s požadavkem jako identifikátor konkrétního uživatele, Secret Key je použit k výpočtu „podpisu“ pro konkrétní požadavek, a to pomocí HMAC-SHA256 algoritmu.

Autentizace požadavku je poměrně jednoduchá, spočívá ve třech krocích:

  1. Příprava požadavku
  2. Výpočet HMAC-SHA256 hashe, kdy je použit Secret Key coby klíč
  3. Přidání spočítaného hashe k požadavku

Příprava požadavku je pravděpodobně nejnáročnější částí. Pojďme si ji probrat podrobněji.

Příprava požadavku

Požadavek se skládá z parametrů ve tvaru „název=hodnota“. Nejprve jsou parametry seřazeny podle názvu abecedně ve vzestupném pořadí. Následně jsou spojeny pomocí ampersandu, stejně jako v query u HTTP požadavků, a stejně jako u HTTP jsou i zde hodnoty enkódovány, s drobnými rozdíly – např. mezera není kódována jako „+“, ale jako „%20“. V PHP můžeme použít funkci rawurlencode.

Příklad: AWSAccessKeyId=0123456789ABCDEF
&Action=Select
&DomainName=MyDomain
&SelectExpression=select%20%2A%20from%20MyDomain%20where%20Ite%20%3E%20%270100.00%27
&SignatureMethod=HmacSHA256
&SignatureVersion=2
&Timestamp=2010-04-14T17%3A08%3A12%2B00%3A00
&Version=2009-04-15

Požadavek se skládá z některých povinných parametrů (AWSAccessKeyId – viz výše zmíněný Access Key, Action – název akce, viz předchozí článek, Domain Name – jméno domény, SignatureMethod, SignatureVersion, Version a Timestamp), z parametrů specifických pro daný požadavek (SelectExpression) a z nepovinných parametrů (zde není použit žádný, ale lze specifikovat např. počet vrácených záznamů).

Máme-li požadavek takto normalizován (parametry seřazeny podle abecedy, hodnoty ošetřeny), vytvoříme si řetězec k podpisu:

HTTPVerb + "n" +
Host + "n" +
RequestURI + "n" +
QueryString

HTTPVerb je použitá metoda (GET nebo POST), Host je plné jméno serveru malými písmeny (většinou „sdb.amazonaw­s.com“), RequestURI je v této verzi API vždy „/“, a QueryString je výše popsaný řetězec s požadavkem. Pro takto vytvořený řetězec spočítáme HMAC-SHA256 hash, kde jako klíč použijeme náš Secret Key, a výsledek přidáme k požadavku jako parametr Signature.

Podepsání požadavku

K výpočtu použijeme v PHP funkci hash_hmac s výstupem jako raw:

$signature = hash_hmac ( 'SHA256', "GETnsdb.amazonaws.comn/n" . $request , 'SECRET KEY', true );

Binární výstup funkce hash_hmac zakódujeme pomocí base64_encode a výsledek před vložením do požadavku ošetříme pomocí rawurlencode. S podepsaným požadavkem pak zavoláme HTTPS GET – buď přes cURL, nebo přes socks, nebo jiným způsobem, podle toho, co náš systém podporuje.

Výsledkem operace je chybové hlášení nebo potvrzení operace. Má vždy formát XML souboru, lze jej tedy snadno parsovat a zpracovat.

Praktická ukázka v PHP

Ukažme si, jak lze pracovat se SimpleDB přímo, bez použití knihovny. V ukázce se zaměříme na algoritmus podepisování a na volání API – nebudeme tedy ošetřovat chyby, timeout u HTTP požadavku, ani nebudeme zpracovávat výsledek.

Nejprve si připravíme data pro požadavek, klidně jako asociativní pole:

$query = array(
'Action'=>'PutAttributes',
'DomainName'=>'MyDomain',
'ItemName'=>'Svetr123',
'Attribute.1.Name'=>'Color','Attribute.1.Value'=>'Blue',
'Attribute.2.Name'=>'Size','Attribute.2.Value'=>'XXL',
'Attribute.3.Name'=>'Price','Attribute.3.Value'=>'000024.99');

Je vidět, že voláme akci PutAttributes, budeme ji vykonávat v doméně MyDomain, jméno položky (klíč) bude Svetr123, a budou jí přiřazeny tři atributy – Color, Size a Price. Jistě vás nepřekvapí, že cena je zleva doplněna nulami – v minulém článku jsme si říkali, že SimpleDB zachází s čísly jako s řetězci, a pokud je chceme porovnávat, musíme je normalizovat.

Všechny hodnoty proženeme přes funkci rawurlencode – lze použít array_filter. V tomto příkladu nejsou použity žádné znaky, které by bylo nutné ošetřovat, výsledek by měl být tedy stejný.

K parametrům přidáme povinné údaje:

$query['Version']='2009-04-15'; //vždy
$query['Timestamp']= urlencode(date('c'));
$query['SignatureVersion']='2'; //vždy
$query['SignatureMethod']='HmacSHA256'; //vždy
$query['AWSAccessKeyId']='0123456789ABCDEF';

Vy samosebou použijete vlastní Access Key.Version, SignatureVersion a SignatureMethod mají v současné verzi API výše uvedené hodnoty.

Při testech jsem měl největší problém se správným formátem Timestamp, nakonec se jako použitelný ukázal urlenkódovaný výstup data a času podle ISO 8601, který vrací funkce date() s řetězcem ‚c‘.

Pokračujeme podepsáním požadavku podle výše zmíněného postupu:

ksort($query); //setřídění podle jména parametru

$request = http_build_query ($query);

$secret = 'bflmpsvzhkrdtn123456zabbfem/asoic-quake987'; //SecretKey

$sign = hash_hmac ( 'SHA256', "GETnsdb.amazonaws.comn/n" . $requesr , $secret, true );

$query = $request . '&Signature=' . urlencode(base64_encode($sign));

Výsledkem je požadavek ($query), doplněný o podpis. Je vidět, že protokol nezaručuje unikátnost požadavku – pokud by požadavek zachytil útočník na trase, může jej teoreticky zopakovat, protože podpis zůstane stejný. AWS umožní provedení operace po dobu 15 minut od času uvedeného v timestamp.

Požadavek stačí už jen odeslat na server https://sdb.a­mazonaws.com/ – lze to udělat pomocí cURL, nebo i pomocí fsockopen – já použil druhou zmíněnou metodu:

$response = '';
$fp = fsockopen("ssl://sdb.amazonaws.com", 443, $errno, $errstr, 30);
if (!$fp) {
    echo "$errstr ($errno)n";
} else {
    $out = "GET /?" . $query . " HTTP/1.1rn";
    $out .= "Host: sdb.amazonaws.comrn";
    $out .= "Connection: Closernrn";
    fwrite($fp, $out);
    while (!feof($fp)) {
        $response .= fgets($fp, 128);
    }
    fclose($fp);
}

Výsledek operace získáme v proměnné response jako XML data.

Závěr

Práce se SimpleDB pomocí REST rozhraní (s výhradou vůči pojmenování REST, viz výše) je opravdu snadná i bez použití knihoven. Jediná možná problematičtější pasáž je podepisování požadavků, ale po podrobnějším pročtení dokumentace (a odhalení správných funkcí pro hash, ošetření hodnot a vygenerování časového razítka) je tento úkol velmi snadný.

Pokud budete zvažovat použití SimpleDB ve své aplikaci, sáhnete pravděpodobně po hotové knihovně, ale pro první pokusy a „osahání“ API na poměrně nízké úrovni lze, jak jsme si ukázali, použít těch nejjednodušších prostředků.

Komentáře

Subscribe
Upozornit na
guest
0 Komentářů
Inline Feedbacks
View all comments

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.