Здравствуйте, читатели.
В данной статье хочу рассказать о библиотеке Libcurl . Libcurl — это кроссплатформенное программное обеспечение, библиотека, клиент использующийся для передачи URL. Она поддерживает огромное множество протоколов, таких как FTP, FTPS, HTTP, HTTPS, SCP, SFTP, TFTP, TELNET, DICT, LDAP, LDAPS и FILE. Так же libcurl поддерживает SSL сертификаты, HTTP POST, HTTP PUT, FTP загрузку, HTTP загрузку, основанную на формах, прокси, cookies, и многое другое. Пользователи могут довольно легко встроить её в свои программы при помощи API cURL . cURL действует как автономная обёртка для библиотеки libcurl. Для неё имеется более 30 различных привязок к языкам программирования, но я бы хотел показать механизм работы на примере PHP.
На работе столкнулся с такой задачей. Нужно было реализовать серверный метод защиты директорий и файлов. Сам механизм работы защиты долго рассказывать, и выходит за рамки статьи, но для этой задачи мне понадобилось делать запрос от самого сервера (то есть через прокси-сервер), получать результат, и уже потом, выдавать обратно пользователю в браузер. Плюс, чтобы для пользователя это было незаметно и абсолютно прозрачно.
Немного почитав документацию по cURL, принялся писать код на PHP, ничего сложного в нём нет, приведу участок ниже.
// ...
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
// ...
?>
Перед использованием cURL нужно проверить присутствует ли вообще у вас данный модуль, поэтому следует использовать метод function_exists("curl_init") . Создав новый cURL resource и выставив необходимые опции (подробнее по каждой из них можно узнать здесь: http://php.net/manual/ru/function.curl-setopt.php) мы делаем запрос curl_exec и выводим ответ в браузер.
Запускаем скрипт на исполнение, проверяем на конкретной странице, выставляем параметр CURLOPT_URL
равный http://webaurum.dev/index.php
. И, о чудо, заработало! Мы получили результат исполнения страницы index.php
. Так как мне нужен прокси для получения любого контента, решил проверить и на других видах содержимого, подставил jpg-файл. Задав в качестве запрашиваемого URL изображение http://webaurum.dev/image.jpg
, но в ответ получил только каракули в окне браузера, вместо ожидаемой картинки.
Начал разбираться в чём же дело. Перехватив заголовки, получил следующее в Response Headers :
Date
Fri, 21 Nov 2008 10:24:00 GMT
Server
X-Powered-By
PHP/5.2.3
Expires
Cache-Control
Pragma
no-cache
Content-Type
text/html
Keep-Alive
timeout=5, max=100
Connection
Keep-Alive
причём и для изображения и для скрипта php в заголовке Content-Type почему-то стояло text/html . Получается curl не передаёт ответ сервера в чистом виде, хотя и получает адекватно содержимое. В документации описана опция CURLOPT_HEADER , там сказано, что при установке этого параметра в ненулевое значение результат будет включать полученные заголовки. Включаем его, и заново проводим эксперимент, подав изображение на вход. В итоге в браузере увидел следующее:
HTTP/1.1 200 OK Date: Fri, 21 Nov 2008 10:24:01 GMT Server: Apache/2.2.4 (Unix) mod_ssl/2.2.4 OpenSSL/0.9.8e DAV/2 PHP/5.2.3 Last-Modified: Fri, 19 Sep 2008 14:23:29 GMT ETag: "4927-4b124-72007a40" Accept-Ranges: bytes Content-Length: 307492 Content-Type: image/jpeg ╪ р� JFIF� �H�H�� с �Exif��MM�*��� � � ���
… и дальше шла куча иероглифов. Как ни странно, но curl смог адекватно определить mime тип запрашиваемого содержимого (Content-Type: image/jpeg ), и даже правильно сформировал заголовки. Тем не менее, в респонсе сервера и дальше красовалось Content-Type: text/html . Почему же не выставляются таким образом заголовки, если до того никакого вывода в браузер не было, для меня осталось загадкой.
Недолго думая, решил распарсить эти заголовки вручную, и установить их явно при получении ответа от curl. У библиотеки curl есть мощный механизм для получения технических аспектов отработки запроса – метод curl_getinfo . Он может принимать множество параметров, но нас, в частности, интересует ключ CURLINFO_HEADER_SIZE . При использовании этого ключа, метод возвращает длину заголовков в ответе curl_exec . Зная длину заголовков, можно вырезать их из общего содержимого, установить их явно при помощи функции header() , а потом уже вывести остаток содержимого в бинарном виде. Тогда браузер должен адекватно проинтерпретировать mime тип полученного содержимого. Код приведен ниже.
// ...
//если cURL доступен – то используем его
if ((bool)function_exists("curl_init"))
{
// создаём новый cURL resource
$ch = curl_init();
// выставляем соответствующие опции и требуемый URL
curl_setopt($ch, CURLOPT_URL, $FILE_URL);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
// сделать запрос по URL и вывести в браузер
$data = curl_exec($ch);
$headers = substr($data, 0, curl_getinfo($ch, CURLINFO_HEADER_SIZE));
$data = substr($data, curl_getinfo($ch, CURLINFO_HEADER_SIZE));
// парсим заголовки
$headers = explode("\r\n", $headers);
foreach ($headers as $header)
{
// устанавливаем каждую часть заголовка отдельно
header($header);
}
// выводим остатки контента в браузер
print($data);
// закрыть cURL ресурс и высвободить системные ресурсы
curl_close($ch);
}
// ...
?>
Теперь перехватим заголовки ответа сервера, и проверим результат:
Date Fri, 21 Nov 2008 11:12:49 GMT
Server Apache/2.2.4 (Unix) mod_ssl/2.2.4 OpenSSL/0.9.8e DAV/2 PHP/5.2.3
X-Powered-By PHP/5.2.3
Expires Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma no-cache
Last-Modified Fri, 19 Sep 2008 14:23:29 GMT
Etag "4927-4b124-72007a40"
Accept-Ranges bytes
Content-Length 307492
Content-Type image/jpeg
Keep-Alive timeout=5, max=100
Connection Keep-Alive
После запуска PHP скрипта на исполнение мы получили долгожданную картинку в окне браузера, а не набор иероглифов, как было раньше. При желании с помощью метода curl_setopt можно задать любые параметры запроса или передать точную копию cookie пользователя, таким образом, что между запросом пользователя и запросом сервера-посредника (прокси-сервера) почти не будет разницы.
Данный пример не претендует, конечно, на новизну и полноту, но таким образом я хотел показать, что работа с заголовками и реализация простейшего прокси-сервера доступна каждому.
(PHP 4 >= 4.0.4, PHP 5, PHP 7)
curl_getinfo — Get information regarding a specific transfer
Parameters
This may be one of the following constants:
- CURLINFO_EFFECTIVE_URL - Last effective URL
- CURLINFO_HTTP_CODE - The last response code. As of PHP 5.5.0 and cURL 7.10.8, this is a legacy alias of CURLINFO_RESPONSE_CODE
- CURLINFO_FILETIME - Remote time of the retrieved document, with the CURLOPT_FILETIME enabled; if -1 is returned the time of the document is unknown
- CURLINFO_TOTAL_TIME - Total transaction time in seconds for last transfer
- CURLINFO_NAMELOOKUP_TIME - Time in seconds until name resolving was complete
- CURLINFO_CONNECT_TIME - Time in seconds it took to establish the connection
- CURLINFO_PRETRANSFER_TIME - Time in seconds from start until just before file transfer begins
- CURLINFO_STARTTRANSFER_TIME - Time in seconds until the first byte is about to be transferred
- CURLINFO_REDIRECT_COUNT - Number of redirects, with the CURLOPT_FOLLOWLOCATION option enabled
- CURLINFO_REDIRECT_TIME - Time in seconds of all redirection steps before final transaction was started, with the CURLOPT_FOLLOWLOCATION option enabled
- CURLINFO_REDIRECT_URL - With the CURLOPT_FOLLOWLOCATION option disabled: redirect URL found in the last transaction, that should be requested manually next. With the CURLOPT_FOLLOWLOCATION option enabled: this is empty. The redirect URL in this case is available in CURLINFO_EFFECTIVE_URL
- CURLINFO_PRIMARY_IP - IP address of the most recent connection
- CURLINFO_PRIMARY_PORT - Destination port of the most recent connection
- CURLINFO_LOCAL_IP - Local (source) IP address of the most recent connection
- CURLINFO_LOCAL_PORT - Local (source) port of the most recent connection
- CURLINFO_SIZE_UPLOAD - Total number of bytes uploaded
- CURLINFO_SIZE_DOWNLOAD - Total number of bytes downloaded
- CURLINFO_SPEED_DOWNLOAD - Average download speed
- CURLINFO_SPEED_UPLOAD - Average upload speed
- CURLINFO_HEADER_SIZE - Total size of all headers received
- CURLINFO_HEADER_OUT - The request string sent. For this to work, add the CURLINFO_HEADER_OUT option to the handle by calling curl_setopt()
- CURLINFO_REQUEST_SIZE - Total size of issued requests, currently only for HTTP requests
- CURLINFO_SSL_VERIFYRESULT - Result of SSL certification verification requested by setting CURLOPT_SSL_VERIFYPEER
- CURLINFO_CONTENT_LENGTH_DOWNLOAD - Content length of download, read from Content-Length: field
- CURLINFO_CONTENT_LENGTH_UPLOAD - Specified size of upload
- CURLINFO_CONTENT_TYPE - Content-Type: of the requested document. NULL indicates server did not send valid Content-Type: header
- CURLINFO_PRIVATE - Private data associated with this cURL handle, previously set with the CURLOPT_PRIVATE option of curl_setopt()
- CURLINFO_RESPONSE_CODE - The last response code
- CURLINFO_HTTP_CONNECTCODE - The CONNECT response code
- CURLINFO_HTTPAUTH_AVAIL - Bitmask indicating the authentication method(s) available according to the previous response
- CURLINFO_PROXYAUTH_AVAIL - Bitmask indicating the proxy authentication method(s) available according to the previous response
- CURLINFO_OS_ERRNO - Errno from a connect failure. The number is OS and system specific.
- CURLINFO_NUM_CONNECTS - Number of connections curl had to create to achieve the previous transfer
- CURLINFO_SSL_ENGINES - OpenSSL crypto-engines supported
- CURLINFO_COOKIELIST - All known cookies
- CURLINFO_FTP_ENTRY_PATH - Entry path in FTP server
- CURLINFO_APPCONNECT_TIME - Time in seconds it took from the start until the SSL/SSH connect/handshake to the remote host was completed
- CURLINFO_CERTINFO - TLS certificate chain
- CURLINFO_CONDITION_UNMET - Info on unmet time conditional
- CURLINFO_RTSP_CLIENT_CSEQ - Next RTSP client CSeq
- CURLINFO_RTSP_CSEQ_RECV - Recently received CSeq
- CURLINFO_RTSP_SERVER_CSEQ - Next RTSP server CSeq
- CURLINFO_RTSP_SESSION_ID - RTSP session ID
Return Values
If opt is given, returns its value. Otherwise, returns an associative array with the following elements (which correspond to opt), or FALSE on failure:
- "url"
- "content_type"
- "http_code"
- "header_size"
- "request_size"
- "filetime"
- "ssl_verify_result"
- "redirect_count"
- "total_time"
- "namelookup_time"
- "connect_time"
- "pretransfer_time"
- "size_upload"
- "size_download"
- "speed_download"
- "speed_upload"
- "download_content_length"
- "upload_content_length"
- "starttransfer_time"
- "redirect_time"
- "certinfo"
- "primary_ip"
- "primary_port"
- "local_ip"
- "local_port"
- "redirect_url"
- "request_header" (This is only set if the CURLINFO_HEADER_OUT is set by a previous call to curl_setopt() )
Changelog
Version | Description |
---|---|
5.5.0 | Introduced CURLINFO_RESPONSE_CODE , CURLINFO_HTTP_CONNECTCODE , CURLINFO_HTTPAUTH_AVAIL , CURLINFO_PROXYAUTH_AVAIL , CURLINFO_OS_ERRNO , CURLINFO_NUM_CONNECTS , CURLINFO_SSL_ENGINES , CURLINFO_COOKIELIST , CURLINFO_FTP_ENTRY_PATH , CURLINFO_APPCONNECT_TIME , CURLINFO_CONDITION_UNMET , CURLINFO_RTSP_CLIENT_CSEQ , CURLINFO_RTSP_CSEQ_RECV , CURLINFO_RTSP_SERVER_CSEQ and CURLINFO_RTSP_SESSION_ID . |
5.4.7 | Introduced CURLINFO_PRIMARY_IP , CURLINFO_PRIMARY_PORT , CURLINFO_LOCAL_IP and CURLINFO_LOCAL_PORT . |
5.3.7 | Introduced CURLINFO_REDIRECT_URL . |
5.3.0 | Introduced CURLINFO_CERTINFO . |
5.2.4 | Introduced CURLINFO_PRIVATE . |
5.1.3 | Introduced CURLINFO_HEADER_OUT . |
Examples
Example #1 curl_getinfo() example
// Create a cURL handle
$ch
=
curl_init
("http://www.example.com/"
);
// Execute
curl_exec
($ch
);
// Check if any error occurred
if (!
curl_errno
($ch
)) {
$info
=
curl_getinfo
($ch
);
echo
"Took "
,
$info
[
"total_time"
],
" seconds to send a request to "
,
$info
[
"url"
],
"\n"
;
}
// Close handle
curl_close
($ch
);
?>
В этой статье мы с Вами продолжим заниматься изучением модуля cURL , и в этот раз Вы узнаете, как узнать HTTP-заголовки сервера с помощью cURL .
Для получения HTTP-заголовков сервера через cURL надо запустить следующий код:
if($curl = curl_init()) {
curl_setopt($curl,CURLOPT_URL,"http://сайт");
curl_setopt($curl,CURLOPT_RETURNTRANSFER,true);
curl_setopt($curl,CURLOPT_NOBODY,true);
curl_setopt($curl,CURLOPT_HEADER,true);
$out = curl_exec($curl);
echo $out;
curl_close($curl);
}
?>
Как и раньше, curl_init() инициализирует сеанс cURL . Затем мы начинаем устанавливать следующие опции:
- CURLOPT_URL = "http://сайт" . Это адрес сайта, от которого мы хотим получить заголовок.
- CURLOPT_RETURNTRANSFER = true . Это опцией мы требуем, чтобы ответ возвращался, а не выводился сразу в браузер.
- CURLOPT_NOBODY = true . Здесь мы требуем, чтобы в ответ не входило содержимое самого документа.
- CURLOPT_HEADER = true . Вот это самая главная опция, именно она включает в ответ от сервера его HTTP-заголовки .
Затем мы выполняем наш cURL-запрос с помощью функции curl_exec() и получаем ответ, который записываем в переменную $out . Затем мы выводим её и закрываем соединение.
Как можно использовать данную информацию? Самый простой пример - это проверять: доступен сайт или нет . Соответственно, если код статуса - 404 , то сайт недоступен. Вот Вы можете создать такой полезный сервис, на котором люди смогут проверить: работает сайт или же нет. Это бывает очень полезно, так как иногда Ваш компьютер блокирует доступ к каким-нибудь сайтам, а также какой-нибудь сайт может блокировать к Вам доступ по IP-адресу . Но благодаря такому сервису станет понятно о реальном положении дел на сервере. Так что дерзайте, и удачи Вам!