CGI-расширение на C++
Сегодня мы попробуем немного отойти от PHP и написать CGI-расширение для сайта на C++. Код, приведенный в этой статье, должен быть понятен даже тем, кто не знаком с C++, но я сомневаюсь, что таковые найдутся
Цель статьи – показать взаимодействие C++ программы и веб-страницы. На месте C++ конечно мог бы быть любой язык, например, Perl. Поставим себе простую задачу: передадим с помощью HTML-формы некоторую строку в наше расширение CGI, перевернем эту строку (первый символ окажется последним и т.д.) и выведем ее в браузер. Но для начала нам нужно правильно настроить Apache для этих экспериментов.
Настройка Apache
У меня стоит Apache 2.2, поэтому все нижеприведенное будет справедливо для него (думаю для более старых версий не должно быть сильных отличий).
Допустим, что у нас есть виртуальный хост http://test:
<VirtualHost *:80>
ServerName test
ServerAdmin admin@i-novice.net
DocumentRoot “D:/MyDocs/My Works/Development/PHP/test”
ScriptAlias /cgi-bin/ “D:/MyDocs/My Works/Development/PHP/test/cgi-bin/”
DirectoryIndex index.php index.html
<Directory />
Options FollowSymLinks +ExecCGI
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
Это описание виртуального хоста в файле апача httpd-vhosts.conf, который включается в настроечном файле httpd.conf. Обращаем внимание на директиву ScriptAlias – она включает возможность обращения к скриптам по адресу http://test/cgi-bin/. Тут важно поставить слеш в конце, иначе Apache не найдет заданный путь. Также тут нужно обратить внимание на директиву Options – в ней должна быть опция «+ExecCGI», разрешающая выполнять CGI-расширения в директории DocumentRoot и во всех ее поддиректориях. Кроме этого в httpd.conf должен быть включен модуль cgi_module:
LoadModule cgi_module modules/mod_cgi.so
Итак, Apache настроили . Подготовим теперь форму.
Форма для передачи строки
<HTML> <HEAD> <META http-equiv="Content-Type" content="text/html; charset=windows-1251" /> </HEAD> <BODY> Введите строку: <FORM action="./cgi-bin/test.exe" method="post"> <INPUT type="text" name="string"> <INPUT type="submit" value="Submit"> </FORM> </BODY> </HTML>
Здесь единственное, на что нужно обратить внимание, – это путь к нашей скомпилированной программе. Пусть ее исполняемый файл будет называться test.exe и будет лежать в директории cgi-bin, которая будет лежать рядом с нашим HTML-файлом формы.
Пишем CGI-расширение на C++
Не знаю, кто какой средой пользуется, но я для своих программ на C++ использую Visual Studio. Условимся сразу, что программа будет принимать переданную строку в однобайтовой кодировке – windows-1251. В UTF-8 передавать не пробовал, но скорее всего нужно использовать уникодовый тип символа wchar_t вместо обычного char:
// функция DecodeHex
// раскодирование строки %хх
CHAR DecodeHex(IN LPSTR str) {
CHAR ch;
// обрабатываем старший разряд
if (str[0] >= 'A') {
ch = ((str[0] & 0xDF) - 'A') + 10;
} else {
ch = str[0] - '0';
}
// сдвигаем его влево на 4 бита
ch <<= 4;
// oбрабатываем младший разряд и складываем его со старшим
if (str[1] >= 'A') {
ch += ((str[1] & 0xDF) - 'A') + 10;
} else {
ch += str[1] - '0';
}
return ch;
}
// Функция DecodeStr
// раскодирование строки в URL
VOID DecodeStr(IN LPSTR sString) {
int src, dst;
CHAR ch;
// цикл по строке
for (src = 0, dst = 0; sString[src]; src++, dst++) {
// получаем очередной символ перекодируемой строки
ch = sString[src];
// заменяем символ + на пробел
ch = (ch == '+') ? ' ' : ch;
// сохраняем результат
sString[dst] = ch;
// обработка шестнадцатеричных кодов вида %хх
if (ch == '%') {
// выполняем преобразование строки %хх в код символа
sString[dst] = DecodeHex(&sString[src + 1]);
src += 2;
}
}
// закрываем строку нулем
sString[dst] = 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR lpCmdLine, int nShowCmd) {
// вывод строки заголовка HTTP
printf("Content-type: text/plain\n\n");
// определение общей длины полученных от браузера данных (берется из
// CONTENT_LENGTH)
LPSTR sDataLength;
_dupenv_s(&sDataLength, NULL, "CONTENT_LENGTH");
int lData = atoi(sDataLength);
free(sDataLength);
// выделяем для переданных данных память
LPSTR sBuff = new CHAR[lData + 1];
// копируем эти данные (длиной lData) из входного потока stdin в буфер
// sBuff
fread(sBuff, lData, 1, stdin);
sBuff[lData] = 0; // закрываем строку нулем
// разбираем данные по переменным
// разделитель между переменными - &
LPSTR next_token;
LPSTR current_token = strtok_s(sBuff, "&", &next_token);
LPSTR lpString = "";
while (current_token) {
DecodeStr(current_token); // раскодируем строку из URL-представления
if (strstr(current_token, "string")) {
// нашли переменную с именем "string"
lpString = strchr(current_token, '=') + 1;
}
current_token = strtok_s(NULL, "&", &next_token);
}
// переворачиваем строку и выводим ее в браузер
printf(_strrev(lpString));
return 0;
}
В итоге после передачи формы мы должны попасть на новую страницу, на которой будет выведена перевернутая введенная строка. Обратим внимание на функцию DecodeStr – она используется для раскодирования строки параметров, переданных из формы, из представления URL в обычное, т.е. это по сути PHP-функция urldecode.
Как видим, передача данных осуществляется через порты ввода-вывода stdin/stdout – кто программировал консольные приложения на C++, тот поймет. Чтобы узнать длину переданной информации в наше расширение, мы использовали значение переменной окружения CONTENT_LENGTH. Мы также можем узнать метод, с помощью которого передавались данные (POST или GET), заглянув в значение переменной окружения REQUEST_METHOD. В нашем случае там было бы значение POST.
Сами переданные через форму переменные содержатся в виде переменная1=значение1&переменная2=значение2 и т.д. Поэтому нам пришлось их отсоединять друг от друга с помощью токенов.
Поздравляю, теперь мы знаем, как писать собственные CGI-расширения на C++!
Как видите, ничего сложного. Но С++ надо, конечно, знать.
Удачи!
Май 5th, 2009
Познавательная для новичков статья, только уж если вы пишете на Win – странно использование Апача – он же там тормозит несусветно! Так же – если вы пишете под апач – более чем странно использование exe и win , апачу роднее unix.
Май 6th, 2009
Баталии между юниксойдами и не-юниксоидами были и будут всегда
Что касается апача, то лично у себя я не чувствую тормозов. Покажите тесты по сравнению производительности работы Apache между Unix и Windows, раз уж на то пошло. Под апач я ничего не пишу, кроме веб-приложений на PHP, а статья была приведена для примера взаимодействия CGI-расширения и веб-страницы.
Июнь 21st, 2009
А можно сравнить по скорости функции, написанные для компилируемой библиотеки пхп и для чистого cgi ?
Июнь 21st, 2009
Можно попробовать, только надо сначала научиться писать расширения для PHP в DLL. Спасибо за идею!
Сентябрь 30th, 2009
Огромное спасибо за статью! Давно искал что-то подобное
Октябрь 7th, 2009
Можете выслать готовый исходник на e-mail? Заранее благодарен.