CGI-расширение на C++
Сегодня мы попробуем немного отойти от PHP и написать CGI-расширение для сайта на C++. Код, приведенный в этой статье, должен быть понятен даже тем, кто не знаком с C++, но я сомневаюсь, что таковые найдутся
Цель статьи – показать взаимодействие C++ программы и веб-страницы. На месте C++ конечно мог бы быть любой язык, например, Perl. Поставим себе простую задачу: передадим с помощью HTML-формы некоторую строку в наше расширение CGI, перевернем эту строку (первый символ окажется последним и т.д.) и выведем ее в браузер. Но для начала нам нужно правильно настроить Apache для этих экспериментов.
Настройка Apache
У меня стоит Apache 2.2, поэтому все нижеприведенное будет справедливо для него (думаю для более старых версий не должно быть сильных отличий).
Допустим, что у нас есть виртуальный хост https://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 – она включает возможность обращения к скриптам по адресу https://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++! Как видите, ничего сложного. Но С++ надо, конечно, знать.
Удачи!
Познавательная для новичков статья, только уж если вы пишете на Win - странно использование Апача - он же там тормозит несусветно! Так же - если вы пишете под апач - более чем странно использование exe и win , апачу роднее unix.
Баталии между юниксойдами и не-юниксоидами были и будут всегда Что касается апача, то лично у себя я не чувствую тормозов. Покажите тесты по сравнению производительности работы Apache между Unix и Windows, раз уж на то пошло. Под апач я ничего не пишу, кроме веб-приложений на PHP, а статья была приведена для примера взаимодействия CGI-расширения и веб-страницы.
А можно сравнить по скорости функции, написанные для компилируемой библиотеки пхп и для чистого cgi ?
Можно попробовать, только надо сначала научиться писать расширения для PHP в DLL. Спасибо за идею!
Огромное спасибо за статью! Давно искал что-то подобное
Можете выслать готовый исходник на e-mail? Заранее благодарен.
апаче ковырять не нужно, он работает с cgi по умалчанию, разумеется , если таковое вкомпилировно
Обидно что статья не кроссплатформенная. Использую Ubuntu 10.10, apache2 и чистый C. Из всей статьи полезны только 2 строчки.
Здравствуйте, а какие include нужны для этого кода, с кодом для новичка все ясно, а вот какие include библиотеки подключать пускай сам догадывается да?