Jakiś czas temu wpadłem na pomysł napisania skryptu pozwalającego na sprawdzanie pozycji danej frazy w wyszukiwarkach na własną rękę. Już na samym początku natrafiłem jednak na istotny problem - w jaki sposób pobrać “stronę” wyszukiwania wysyłając do niej tradycyjne żądanie AJAX? W dzisiejszym wpisie pomogę Wam rozwiązać ten problem.

Wstęp.

Każdy, kto kiedykolwiek “bawił się” AJAXem zapewne kojarzy pojęcie “same origin policy” [wolne tłumaczenie: “polityka tego samego źródła”]. W wielkim skrócie powiem, że dotyczy ona możliwości [a raczej jej braku] wysyłania żądań do różnych serwerów oznaczonych inną niż nasza domeną. Jako, że moja definicja może być nieco niejasna, pozwolę sobie posłużyć się przykładem.

Nasz przykładowy skrypt został załadowany przez użytkownika na serwerze example.com. W ramach domeny możemy wysyłać dowolne żądania AJAXowe - tzn. wszelkie próby dostępu do URLi:

example.com?type=ajax&action=getcomments example.com?type=ajax&action=checkstatus
są przesyłane w mgnieniu oka do użytkownika. Problem powstaje w momencie, kiedy potrzebne stają się dane, które można uzyskać jedynie odwołując się do domeny sameorigin.net [ogólnie: domeny innej niż ta, na której działa skrypt, w tym wypadku jest to example.com]. Wykonujemy żądanie do serwera:
sameorigin.net?type=ajax&action=getexternaldata
i… nic. Myślimy sobie - “pewnie gdzieś mam błąd w kodzie” - i niestety w ten sposób popełniamy błąd, bo takiego żądania po prostu w ten sposób wykonać się nie da. Można jednak w łatwy sposób to zabezpieczenie obejść - opis poniżej.

Rozwiązanie.

Aby obejść zabezpieczenie same origin policy użyjemy tzw. “server side proxy” [wolne tłumaczenie: proxy po stronie serwera]. Nie jest ono obecne tam, gdzie mamy “fizyczny” dostęp do wszelkich narzędzi systemowych, a więc nie dotyczy też języków wykonywanych po stronie serwera. Najpierw zobaczmy w jaki sposób można wykonać żądanie z poziomu języka JavaScript [posługuję się oczywiście genialną biblioteką jQuery ;]]:

$.ajax({
	url: proxyLink,
	dataType: 'text',
	async: false,
	success: function(data, status, xhr)
		{
		contents = data;
		success = true;
		return;
		},
	error: function(xhr, text, code)
		{
		alert('readyState: ' + xhr.readyState
			+ '\nstatus: ' + xhr.status
			+ '\nresponseText: ' + xhr.responseText);
		return;
		}
	});

Jest to fragment nieco wyrwany z kontekstu [jak już dopracuję ten skrypt będziecie się mogli z nim zapoznać i odkryć kontekst ;]], ale widzimy na nim, że ustawiona wcześniej w skrypcie zmienna proxyLink przechowuje adres, z jakiego chcemy pobrać dane, a dwie funkcje: success() i error() pozwalają nam odpowiednio zareagować na rezultat żądania. Teraz zapewne zastanawiacie się w jaki sposób użyjemy naszego proxy. Otóż, skorzystamy z zewnętrznego skryptu PHP, który będziemy musieli umieścić na naszym serwerze:

<?php
header("Content-Type: text/html; charset=utf-8");
$url = $_GET['query'];
$handle = fopen($url, 'r');
if($handle)
	{
    while(!feof($handle))
		{
        $buffer = fgets($handle, 4096);
        echo $buffer;
    	}
    fclose($handle);
	}
?>

Jest to jeden z najprostszych sposobów na jakie można wykonać ten skrypt. Oczywiście można też skorzystać z biblioteki cURL lub podobnej, jednak ja potrzebowałem tego kodu na szybko, dlatego uprościłem sobie sprawę.

Mając taki skrypt na naszym serwerze możemy odwołać się żądaniem AJAXowym do niego, podając adres zewnętrznego serwera, z którego chcemy pobrać dane, tj.:

example.com?type=ajax&link=sameorigin.net%3faction%3dgetexternaldata
i gotowe. Skrypt nie zaprotestuje ani przez chwilę, ponieważ znajduje się w naszej domenie, także wszelkie narzędzia broniące zabezpieczenia same origin policy “nie mają nic do gadania”.

Podsumowanie.

W ramach komentarza do artykułu powiem, że nie należy się denerwować takimi “utrudnieniami” - zabezpieczenie same origin zostało wymyślone m. in. po to, żeby niemożliwe były zmasowane ataki CSRF - wyobraźmy sobie, że wchodząc na stronę internetową ta wykonuje żądanie usunięcia naszego konta w innym serwisie. Nie brzmi przyjemnie, prawda?

Chętnie usłyszę w jaki sposób Wy poradziliście sobie z tego typu problemami. Czlowiek uczy się całe życie, a ja, nawet pomimo danych pobieranych ze wszystkich kanałów RSS w jakie wgryzam się każdego dnia nie jestem w stanie posiąść całej istniejącej wiedzy. Z góry dzięki za wszelkie komentarze!