Версия для слабовидящих
Навигация:  Сайт колледжа » Форум » Раздел "Операционные системы, программирование" » [Просмотр темы в подразделе " Delphi"]
piople
  Тема создана: 10 сентября 2010 в 15:35 :: изменено - 19.01.2011 в 11:57
Горбунов Александр Валерьевич

Группа: супермодератор
ICQ: 112
Проживает:
 Красноярский край, Канск

Преамбула. Год назад я задался одним вопросом – как можно подсчитать количество обращений к статическому контенту сайта? Ответов удалось найти 2а:

  • Разместить на каждой статической страничке скрипт, который будет обращаться к счетчику каждый раз когда страницу загружают;
  • Найти способ отслеживать все запросы к  сайту на предмет обращения к файлам и уже исходя из этого фильтровать запросы.

Собственно первый вариант очень просто в реализации скрипта счетчика, но перспектива добавлять JavaScript в каждую из 1000 статических страниц меня не прельщала. Поэтому как говориться «Правильные герои идут в обход». Оставалось найти способ реализовать вторую задумку.

 

Поскольку наш сайт крутится на базе IIS мой взгляд был направлен в сторону ISAPI-фильтров. Поискав в интернете интересующую меня информацию понял что просто скачать исходник, переделать его под себя и выставить не получится. По простой банальной причине, просто не нашел ни одного готового исходника на Delphi, удалось найти только модули реализующие спецификацию ISAPI-фильтра. А те исходники что попадались на глаза реализованы на Си. А мануалы на английском.

 

Но самое главное, что мне удалось переложить исходники на Delphi, и заставить мой ISAPI-фильтр делать то что от него требуется.

 

Итак, вот что у меня получилось (я надеюсь, вы знакомы с принципом написания библиотек на Delphi):

library StaticCount;

uses

   SysUtils,   Windows,   SyncObjs,   ISAPIFilters in 'ISAPIFilters.pas';

Var

   csec: Tcriticalsection;

procedure TerminateFilter(dwFlags: Cardinal; var Result: LongBool);

begin

      csec.Free;

end;

function GetFilterVersion(var pVer: HTTP_FILTER_VERSION): BOOL; export; stdcall;

begin

   try

      csec := TCriticalsection.Create;

     //Создания подключения к БД

      pVer.dwFilterVersion := MAKELONG(0, 1);

      StrPCopy(pVer.lpszFilterDesc, 'URL Version - ISAPI Filter');

      pVer.dwFlags := (SF_NOTIFY_URL_MAP);

      result := True;

      LogInfo(#13#10 + DateTimeToStr(Now) + ': Runnig...' + #13#10);

   except

      on E: Exception do begin

         LogInfo('Error (GetFilterVersion): ' + E.Message + #13#10);

         result := False;

      end;

   end;

end;

 function HttpFilterProc(var pfc: HTTP_FILTER_CONTEXT; NotificationType: DWORD; pvNotification: LPVOID): DWORD; export; stdcall;

var

   pvHTTP_FILTER_URL_MAP: HTTP_FILTER_URL_MAP;

   Buffer: array[0..1023] of char;

   BuffSize: DWORD;

   Ext, HisAddress: string;

    function OnUrlMap: DWORD;

   begin

      csec.Enter;

      try

         pvHTTP_FILTER_URL_MAP := HTTP_FILTER_URL_MAP(pvNotification^);

         Ext := LowerCase(ExtractFileExt(pvHTTP_FILTER_URL_MAP.pszPhysicalPath));

         if (Ext = '.htm') or (Ext = '.zip') or (Ext = '.rar') then

         begin

            TGetServerVariable(pfc.GetServerVariable)(pfc, 'REMOTE_ADDR', @Buffer, @BuffSize);

            HisAddress := StrPas(Buffer);

            if HisAddress <> '' then

            begin

               //Тут происходит обновление счетчика

            end;

         end;

      except

         on E: Exception do begin

            LogInfo(Error (HttpFilterProc): ' + E.Message + #13#10);

            Result := SF_STATUS_REQ_ERROR;

         end;

      end;

      csec.Leave;

      Result := SF_STATUS_REQ_NEXT_NOTIFICATION;

   end;

begin

   case NotificationType of

      SF_NOTIFY_URL_MAP: Result := OnUrlMap;

   else

      begin

         Result := SF_STATUS_REQ_NEXT_NOTIFICATION;

      end;

   end;

end;

exports

   HttpFilterProc,

   GetFilterVersion,

   TerminateFilter;

end.

 

Итак поясню:

 

function GetFilterVersion(var pVer: HTTP_FILTER_VERSION): BOOL; export; stdcall;

Эта функция вызывается только один раз в момент запуска сервера. В виду такого ее поведения она подходит для инициализации данных, если они нужны. Когда добавляется новый фильтр выделяется новая ячейка (наверное :). А вот чтобы заполнить ее информацией сервер просто передает вам указатель на структуру HTTP_FILTER_VERSION, чтобы Вы могли ее заполнить сами.

 

Структура эта такая:

 typedef struct _HTTP_FILTER_VERSION

{

DWORD dwServerFilterVersion;   // Версия сервера Данные передаются заполненные !!!

DWORD dwFilterVersion;                              // версия фильтра

CHAR lpszFilterDesc[SF_MAX_FILTER_DESC_LEN+1];     // короткое описание

DWORD dwFlags;                                           // приоритет фильтра

} HTTP_FILTER_VERSION, *PHTTP_FILTER_VERSION;

 

Кроме приоритетов мы должны так же указать серверу о каких событиях передавать уведомления. Вот эти флаги:

  • SF_NOTIFY_READ_RAW_DATA - Сервер считывает данные из socket
  • SF_NOTIFY_PREPROC_HEADERS - Сервер начинает обработку заголовка запроса
  • SF_NOTIFY_AUTHENTICATION - Сервер проверяет пользователя
  • SF_NOTIFY_URL_MAP - Сервер отображает URL на диск
  • SF_NOTIFY_SEND_RAW_DATA - Сервер отправляет данные обратно клиенту
  • SF_NOTIFY_LOG - Сервер заносит данные в LOG файл
  • SF_NOTIFY_END_OF_NET_SESSION - Связь между сервером и клиентом закрыта
  • SF_NOTIFY_ACCESS_DEFINED - Сервер не смог получить документ
  • SF_NOTIFY_SEND_RESPONSE - Сервер отправил клиенту ответный HTTP заголовок

Следующей важной функцией для создания фильтра является HttpFilterProc. HttpFilterProc и GetFilterVersion являются функциями взаимодействия с сервером.

 Давайте посмотрим описание HttpFilterProc.

DWORD WINAPI HttpFilterProc

(

PHTTP_FILTER_CONTEXT pfc,  // структура для информации запроса иуказатель на функции сервера

  DWORD notificationType,     // типа уведомления

  LPVOID pvNotification          // указатель на структуре для данного уведомления

);

 

Структура PHTTP_FILTER_CONTEXT для каждого запроса уникальна. Вот в этом вся и соль. Для каждого запроса она разная. Но в ней еще и передаются указатели на функции сервера. Ими можно воспользоваться для того, чтобы вызвать функции сервера.

При передаче работы в функцию HttpFilterProc фильтра IIS будет ждать, когда фильтр вернет функцию назад. Возвратить можно следующие параметры:

  • SF_STATUS_REQ_FINISHED - Обработка запроса закончена завершение связи с клиентом
  • SF_STATUS_REQ_FINISHED_KEEP_CONN - Обработка закончена сеан оставить открытым
  • SF_STATUS_REQ_NEXT_NOTIFICATION - Вызвать следующий фильтр
  • SF_STATUS_REQ_HANDLED_NOTIFICATION - Все сделал фильтр
  • SF_STATUS_REQ_ERROR - Ошибка фильтра
  • SF_STATUS_REQ_READ_NEXT - Фильтр считал не все данные

 Ну, вот собственно все, что у меня получилось, единственное, что я выкинул из проекта это код для соединения с БД и непосредственно место для обновления счетчика, ибо для каждого сайта я думаю, они будут уникальны)

 

 По поводу критических секций, если без них то фильтр периодически выдает ошибки, поэтому лучше с ними :)

 

Исходник смотрите в "прикрепленке".

 

P.S. Использование материалов портала разрешено только при условии указания источника: при публикации в Интернете необходимо размещение прямой гипертекстовой ссылки, не запрещенной к индексированию для хотя бы одной из поисковых систем: Google, Yandex; при публикации вне Интернета - указание адреса сайта.


Прикрепленные файлы:
StaticCounter.rar  (Размер: 8,39 KB)

подпись однак0
Страницы: (1) - [1]
Нет ответов!
Страницы: (1) - [1]
  Юридический и фактический адрес КГА ПОУ «Канский педагогический колледж»: 663606, Красноярский край, г. Канск, ул. 40 лет Октября, д. 65.
  Если у вас есть замечания или предложения по нашему сайту, то просим сюда: piople@cross-kpk.ru
Публикация персональных данных, в том числе фотографий, производится в соответствии с Федеральным законом РФ № 152-ФЗ "О персональных данных" от 27.07.2006 г. (с изм. и доп.)