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++! :) Как видите, ничего сложного. Но С++ надо, конечно, знать.

Удачи!



Теги:


Читайте также:



9 Ответов на “CGI-расширение на C++”

  1. max

    Познавательная для новичков статья, только уж если вы пишете на Win - странно использование Апача - он же там тормозит несусветно! Так же - если вы пишете под апач - более чем странно использование exe и win , апачу роднее unix.

  2. novice

    Баталии между юниксойдами и не-юниксоидами были и будут всегда :) Что касается апача, то лично у себя я не чувствую тормозов. Покажите тесты по сравнению производительности работы Apache между Unix и Windows, раз уж на то пошло. Под апач я ничего не пишу, кроме веб-приложений на PHP, а статья была приведена для примера взаимодействия CGI-расширения и веб-страницы.

  3. Константин

    А можно сравнить по скорости функции, написанные для компилируемой библиотеки пхп и для чистого cgi ?

  4. novice

    Можно попробовать, только надо сначала научиться писать расширения для PHP в DLL. Спасибо за идею!

  5. Огромное спасибо за статью! Давно искал что-то подобное

  6. Ninja

    Можете выслать готовый исходник на e-mail? Заранее благодарен.

  7. апаче ковырять не нужно, он работает с cgi по умалчанию, разумеется , если таковое вкомпилировно

  8. Batiatari-dono

    Обидно что статья не кроссплатформенная. Использую Ubuntu 10.10, apache2 и чистый C. Из всей статьи полезны только 2 строчки.

  9. Александр

    Здравствуйте, а какие include нужны для этого кода, с кодом для новичка все ясно, а вот какие include библиотеки подключать пускай сам догадывается да?


© Copyright. . I-Novice. All Rights Reserved. Terms | Site Map