Скрипт 1 ниже — это bash, и находится в https://example.com/cgi-bin/test
. При извлечении он выводит сообщение «В разработке». Он выводит Status
и Content-type
заголовки, и немного HTML. Если вместо этого я попытаюсь вывести весь HTML-документ, Apache просто выдаст сообщение о недопустимом заголовке.
Скрипт 2 ниже — это php, и находится в https://example.com/cgi-bin/test2.php
. В отличие от скрипта bash, этот возвращает HTML-документ.
Почему скрипт 2 может отправить целый HTML-документ, а скрипт 1 — нет?
Сценарий 1
#!/bin/bash
cat <<'EOF'
Status: 200 OK
Content-type: text/html
<p>Under construction.</p>
EOF
Сценарий 2
<?php
print <<<EOF
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
...etc
</head>
<body>
...etc
</body>
</html>
EOF;
?>
РЕДАКТИРОВАТЬ
php
поставляется в 2 вариантах: CLI и CGI версии. Если вы просто запустите Script 2 из командной строки, php test2.php
тотолькоВывод, произведенный командой, php
— это именно то, что вы видите: HTML-документ php-cgi
— это версия CGI (устанавливается в Ubuntu/Deb как apt install php-cgi
). Apache (фактически) запускает версию CGI (в реальной жизни он делает это немного иначе, но с теми же результатами):
$ php-cgi test2.php
Content-type: text/html; charset=UTF-8
<!DOCTYPE html>
...rest of doc
Скрипты CGI должны возвращать как минимум Content-type
Apache (но могут возвращать больше заголовков, включая Status
). Так что ответ таков:обаСкрипты работают, потому что Скрипт 1 явно возвращает Content-type
, в то время как внутренняя CGI-версия php
делает то же самое.
Скрипт bash может вернуть весь HTML-документ, если он также возвращает расширение Content-type
.
решение1
В CGI вам нужно отправить Content-Type. PHP сгенерирует его для вас. (проверьте вывод через браузер, вы увидите его, даже если не видите его в своем коде.)
У меня есть CGI, который я сделал на языке C, и вам действительно нужен content-type;
В моем случае, например;
printf("Тип содержимого: text/html;charset=us-ascii\n\n");
Если вам нужно изменить заголовок в php, вам нужно вызвать header в начале вашего скрипта.
то есть;
header('Тип содержимого: application/json');
решение2
В этом контексте PHP и CGI — это две разные вещи.
CGI — это интерфейс между программой (в данном случае скриптом bash) и веб-сервером. Этот интерфейс определяет связь между веб-сервером и программой.
Этот стандарт требует, чтобы программа возвращалавсезаголовки, включая заголовки статуса, перед фактическим содержанием. В HTTP заголовки и тело разделяются одной строкой - таким образом, формат, в котором вы имеете
Header
Header
Content
После заголовков вы можете включить полный HTML-документ или любые другие данные, соответствующие отправляемым вами заголовкам.
PHP делает несколько предположений за вас, и если вы их не переопределите, он автоматически устанавливает тип контента, код состояния и т. д.
решение3
Вам нужно включить допустимый ответ заголовка CGI, а не ответ HTTP, если вы вызываете скрипт через CGI через Apache. Я думаю, что по сути (подробности ниже) вам нужно удалить "Status: 200 OK" из вашего файла, и это может сработать.
Есть хорошая статья от самих Apache (https://httpd.apache.org/docs/2.2/howto/cgi.html), который показывает вам минимум для получения допустимого ответа.
Пример заголовка, полученного клиентом, может быть следующим:
HTTP/1.x 200 OK
Transfer-Encoding: chunked
Date: Tue, 06 Dec 2021 19:58:00 GMT
Server: My_Bash_Script
Connection: close
X-Powered-By: My_Bash_Script
Pragma: public
Expires: Tue, 06 Dec 2021 20:58:00 GMT
Cache-Control: max-age=3600, public
Last-Modified: Tue, 06 Dec 2021 20:58:00 GMT
Content-Encoding: gzip
Vary: Accept-Encoding, Cookie, User-Agent
Content-Type: text/html; charset=UTF-8
<!DOCTYPE html>
<head><title>Under construction</title>
<body><p>Under construction.</p></body>
</html>
Но ваш CGI должен отправлять только данные из «Content-Type: text/html; charset=UTF-8» и ниже.
Довольно хорошую статью с объяснением заголовков можно найти здесь: https://code.tutsplus.com/tutorials/http-headers-for-dummies--net-8039
Что касается обслуживаемого PHP-файла через Apache, существует несколько уровней взаимодействия:
- Запрос Apache выполняется на порт TCP. Это включает заголовок запроса, если через HTTP.
- Apache запускает все необходимые правила (например, mod_rewrite) и обрабатывает все необходимые SSL-соединения/рукопожатия.
- Затем Apache определяет расширение файла как PHP и вызывает PHP-скрипт через интерпретатор PHP.
- PHP-код интерпретируется и преобразуется в статическую строку (надеюсь :-), которая возвращается Apache и содержит HTML-код.
- Затем Apache добавляет информацию заголовка на HTML-страницу вместе с любой другой исходящей обработкой.
- Данные сериализуются и передаются обратно клиенту по TCP-соединению.
Другой хороший способ изучить заголовки — использовать Firefox / Chrome Developer Tools (нажатие F12 в Firefox открывает их). Перейдите на вкладку Network, как только Developer Tools будут открыты, и перезагрузите страницу (Ctrl + R в Windows/Linux). Есть опция «Raw», где вы можете увидеть точные данные, которые были отправлены и получены.
Наконец, если вы обнаружите веб-сайт или даже то, что ваш CGI обслуживается по протоколу http, а не https, вы можете установить Wireshark (https://www.wireshark.org) и легко отслеживать трафик разговоров, чтобы узнать разницу между тем, что вы отправляете и что неправильно понимается, и тем, как выглядит обычный статический HTML-диалог, обслуживаемый Apache.
P.S. (В 2022 году) Если вы действительно используете Apache 2.2:
- Возможно, вам не хватает других функций Apache2.4, которые могут помочь вам быстрее достичь желаемого:https://httpd.apache.org/docs/2.4/new_features_2_4.html
- Обратите внимание, что Apache 2.2 устарел (с 2017 года) и содержит ряд уязвимостей:https://httpd.apache.org/security/vulnerabilities_22.html