Конференция "Начинающим" » TWebModule непонятки с IfModifiedSince [D7, XP]
 
  • ford © (16.03.17 10:53) [0]
    Для настройки кэширования, решил использовать параметр из HTTP запроса
    IfModifiedSince. Т.е. на сколько я понял, то в этом поле передается дата файла хранящегося в кэше браузера клиента. CGI-приложение, проверяет эту дату и дату файла хранящегося на сервере, и если даты не поменялись, то возвращает статус 304, и пустой поток.
    Периодически, при обращении к этому полю объекта Request Класса TWebRequest, приложение вылетает с ошибкой 500 Internal server error
    HTTP запрос выглядит вполне корректно:

    Accept */*
    Accept-Encoding gzip, deflate
    Accept-Language ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3
    Cache-Control max-age=0
    Connection keep-alive
    Cookie nut=7b8e525fd6d014a036822efa1e868dcc
    Host test.tst
    If-Modified-Since Mon, 13 Mar 2017 16:00:46 GMT



    HTTP заголовок ответа:

    Connection close
    Content
    Content-Length 129
    Content-Type text/html; charset=windows-1251
    Date Thu, 16 Mar 2017 07:29:50 GMT



    А вот содержимое ответа:

    <html><body><h1>Internal Application Error</h1>
    <p>'Tue' is not a valid integer value
    <p><hr width="100%"></body></html>



    Сделал проверку, непосредственно в теле процедуры ответа:

    procedure TCGI.WebModule1indexAction(Sender: TObject;
     Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
    var st:TstringList;
    begin
    //main func

    st:=TStringList.Create;
    st.LoadFromFile(ExtractFileDir(ParamStr(0))+'\111.log');
    try
     st.Append(request.Query+' : '+DateTimeToStr(Request.IfModifiedSince));
    except
     on e : Exception do
     st.Append(request.Query+' : exception:'+e.Message);
    end;
    st.SaveToFile(ExtractFileDir(ParamStr(0))+'\111.log');
    st.Free;
    ....




    Содержимое лога вот такое:

    what=/index.html&id=200&log=1 : 29.12.1899
    what=/css/struct.css : exception:'Tue' is not a valid integer value
    what=/image/img.png : exception:'Mon' is not a valid integer value



    Т.е. поле IfModifiedSince, в TwebRequest, работает не правильно, не может преобразовать строку типа "Mon, 13 Mar 2017 16:00:46 GMT" в тип TDateTime

    Тогда вопрос: Как вытащить заголовок HTTP запроса?
    Я сам распарсю это поле, но как его получить?
  • ford © (16.03.17 12:49) [1]
    попробовал вытащить в виде строки значение этого поля

    Request.GetFieldByName('If-Modified-Since')


    возвращает всегда пустую строку, несмотря на то что в заголовке HTTP запроса этот параметр есть!!! Его видно в Firebug!!
    Попробовал получить значения других полей, нуу мало ли может не работает, класс то абстрактный.
    Значения полей "Connection", "Refer" и остальные возвращает нормально!
  • Плохиш © (16.03.17 13:55) [2]
    В Delphi есть отладчик.
  • Плохиш © (16.03.17 14:06) [3]
    Request.Headers[]
  • ford © (16.03.17 15:16) [4]

    > Плохиш ©   (16.03.17 13:55) [2]
    >
    > В Delphi есть отладчик.

    Отладчик есть, но к чему его аттачить?


    > Плохиш ©   (16.03.17 14:06) [3]
    >
    > Request.Headers[]

    "мимо", нет такого свойства.
    Вот описание этого класса:

     TWebRequest = class(TObject)
     private
       FContentParser: TAbstractContentParser;
       FMethodType: TMethodType;
       FContentFields,
       FCookieFields,
       FQueryFields: TStrings;
       function GetContentParser: TAbstractContentParser;
       function GetContentFields: TStrings;
       function GetCookieFields: TStrings;
       function GetQueryFields: TStrings;
       function GetFiles: TAbstractWebRequestFiles;
     protected
       function GetStringVariable(Index: Integer): string; virtual; abstract;
       function GetDateVariable(Index: Integer): TDateTime; virtual; abstract;
       function GetIntegerVariable(Index: Integer): Integer; virtual; abstract;
       function GetInternalPathInfo: string; virtual;
       function GetInternalScriptName: string; virtual;
       procedure UpdateMethodType;
     public
       constructor Create;
       destructor Destroy; override;
       // Read count bytes from client
       function ReadClient(var Buffer; Count: Integer): Integer; virtual; abstract;
       // Read count characters as a string from client
       function ReadString(Count: Integer): string; virtual; abstract;
       // Translate a relative URI to a local absolute path
       function TranslateURI(const URI: string): string; virtual; abstract;
       // Write count bytes back to client
       function WriteClient(var Buffer; Count: Integer): Integer; virtual; abstract;
       // Write string contents back to client
       function WriteString(const AString: string): Boolean; virtual; abstract;
       // Write HTTP header string
       function WriteHeaders(StatusCode: Integer; const ReasonString, Headers: string): Boolean; virtual; abstract;
       // Utility to extract fields from a given string buffer
       procedure ExtractFields(Separators, WhiteSpace: TSysCharSet;
         Content: PChar; Strings: TStrings);
       // Fills the given string list with the content fields as the result
       // of a POST method
       procedure ExtractContentFields(Strings: TStrings);
       // Fills the given string list with values from the cookie header field
       procedure ExtractCookieFields(Strings: TStrings);
       // Fills the given TStrings with the values from the Query data
       // (ie: data following the "?" in the URL)
       procedure ExtractQueryFields(Strings: TStrings);
       // Read an arbitrary HTTP/Server Field not lists here
       function GetFieldByName(const Name: string): string; virtual; abstract;
       // The request method as an enumeration
       property MethodType: TMethodType read FMethodType;
       // Content parser
       property ContentParser: TAbstractContentParser read GetContentParser;
       // Field lists
       property ContentFields: TStrings read GetContentFields;
       property CookieFields: TStrings read GetCookieFields;
       property QueryFields: TStrings read GetQueryFields;
       // HTTP header Fields
       property Method: string index 0 read GetStringVariable;
       property ProtocolVersion: string index 1 read GetStringVariable;
       property URL: string index 2 read GetStringVariable;
       property Query: string index 3 read GetStringVariable;
       property PathInfo: string index 4 read GetStringVariable;
       property PathTranslated: string index 5 read GetStringVariable;
       property Authorization: string index 28 read GetStringVariable;
       property CacheControl: string index 6 read GetStringVariable;
       property Cookie: string index 27 read GetStringVariable;
       property Date: TDateTime index 7 read GetDateVariable;
       property Accept: string index 8 read GetStringVariable;
       property From: string index 9 read GetStringVariable;
       property Host: string index 10 read GetStringVariable;
       property IfModifiedSince: TDateTime index 11 read GetDateVariable;
       property Referer: string index 12 read GetStringVariable;
       property UserAgent: string index 13 read GetStringVariable;
       property ContentEncoding: string index 14 read GetStringVariable;
       property ContentType: string index 15 read GetStringVariable;
       property ContentLength: Integer index 16 read GetIntegerVariable;
       property ContentVersion: string index 17 read GetStringVariable;
       property Content: string index 25 read GetStringVariable;
       property Connection: string index 26 read GetStringVariable;
       property DerivedFrom: string index 18 read GetStringVariable;
       property Expires: TDateTime index 19 read GetDateVariable;
       property Title: string index 20 read GetStringVariable;
       property RemoteAddr: string index 21 read GetStringVariable;
       property RemoteHost: string index 22 read GetStringVariable;
       property ScriptName: string index 23 read GetStringVariable;
       property ServerPort: Integer index 24 read GetIntegerVariable;
       property InternalPathInfo: string read GetInternalPathInfo;
       property InternalScriptName: string read GetInternalScriptName;
       property Files: TAbstractWebRequestFiles read GetFiles;



    свойство IfModifiedSince: TDateTime index 11 read GetDateVariable;
    Как я писал выше или выдает полную чушь или exception
  • ford © (16.03.17 15:35) [5]
    Можно заставить браузер отправлять If-None-Match, если установить в ответе Etag. Но как видите, в классе не предусмотрена обработка этого поля и добраться можно к нему только прочитав весь заголовок и распарсив его.
    Т.е. вопрос опять - как получить HTTP заголовок запроса?

    Пробовал читать StdIn, т.к. он там должен быть, но ... пусто.
  • Плохиш © (16.03.17 16:46) [6]

    > Отладчик есть, но к чему его аттачить?

    w3wp.exe
  • ford © (17.03.17 09:42) [7]

    > Плохиш ©   (16.03.17 16:46) [6]
    >
    >
    > > Отладчик есть, но к чему его аттачить?
    >
    > w3wp.exe

    А с чего Вы взяли что я использую IIS?
    И еще, как Вы собираетесь трассировать абстрактно виртуальный метод?

     function GetDateVariable(Index: Integer): TDateTime; virtual; abstract;


    Точку останова куда прикажете ставить?
    Если на вызов метода, то дебагер пролетит его, без захода, или использовать дизассемблер?

    Тут все проще, надо просто получить заголовок до его парсинга TWebApplication, но вот как?
    Я пробовал еще до инициализации стырить StdIn - фугшки, пусто там.
    Сделал приложение спецально, простое консольное и подусунул апачу как CGI, приложение вылетает с ошибкой.
    ИМХО дело в апаче, который както-то не так передает поток ввода, своему CGI приложению. Но исходя из того что TWebApplication все-таки получает какой-то заголовок (часть параметров то отображается нормально), следовательно как-то его получить можно.
    КАК?!
  • Плохиш © (17.03.17 11:39) [8]

    > ford ©   (17.03.17 09:42) [7]
    > А с чего Вы взяли что я использую IIS?

    Да мне в общем-то пофигу.

    > И еще, как Вы собираетесь трассировать абстрактно виртуальный
    > метод?

    При работе программы нет работающих абстрактных методов.

    PS. Проблема -то у вас.
  • ford © (17.03.17 15:06) [9]

    > Плохиш ©   (17.03.17 11:39) [8]
    >
    >
    > > ford ©   (17.03.17 09:42) [7]
    > > А с чего Вы взяли что я использую IIS?
    >
    > Да мне в общем-то пофигу.

    То что Вам пофигу, оно понятно из ваших ответов (лишь бы чего написать).

    А вот ответ на ту заморочку подсказали на cyberforum.ru, там видимо люди которым не "пофигу":

    Может кому пригодится, оказывается борланд несколько по другому назвал эти поля. Они описаны в модуле CGIHTTP.pas


    CGIServerVariables: array[0..28] of string = (
       'REQUEST_METHOD',
       'SERVER_PROTOCOL',
       'URL',
       'QUERY_STRING',
       'PATH_INFO',
       'PATH_TRANSLATED',
       'HTTP_CACHE_CONTROL',
       'HTTP_DATE',
       'HTTP_ACCEPT',
       'HTTP_FROM',
       'HTTP_HOST',
       'HTTP_IF_MODIFIED_SINCE',
       'HTTP_REFERER',
       'HTTP_USER_AGENT',
       'HTTP_CONTENT_ENCODING',
       'HTTP_CONTENT_TYPE',
       'HTTP_CONTENT_LENGTH',
       'HTTP_CONTENT_VERSION',
       'HTTP_DERIVED_FROM',
       'HTTP_EXPIRES',
       'HTTP_TITLE',
       'REMOTE_ADDR',
       'REMOTE_HOST',
       'SCRIPT_NAME',
       'SERVER_PORT',
       '',
       'HTTP_CONNECTION',
       'HTTP_COOKIE',
       'HTTP_AUTHORIZATION');



    т.е. надо запрашивать не IF-MODIFIED-SINCE (что логично, т.к. в заголовке поле называется именно так), а так HTTP_IF_MODIFIED_SINCE.
    Тогда Request.GetFieldByName('IF-MODIFIED-SINCE') возвращает строку со значением этого поля. Нууу а дальше преобразовывай не хочу :)
  • ford © (17.03.17 15:08) [10]
    описался, т.е. конечно:
    Request.GetFieldByName('HTTP_IF_MODIFIED_SINCE')
    вернет строку со значением поля IF-MODIFIED-SINCE из заголовка.
  • Игорь Шевченко © (17.03.17 15:22) [11]
    function TCGIRequest.GetFieldByName(const Name: string): string;

     function AdjustHTTP(const Name: string): string;
     const
       SHttp = 'HTTP_';     { do not localize }
     begin
       if Pos(SHttp, Name) = 1 then
         Result := Copy(Name, 6, MaxInt)
       else
         Result := SHttp + Name;
     end;

    begin
     Result := GetEnvironmentVariable(Name);
     if Result = '' then
       Result := GetEnvironmentVariable(AdjustHTTP(Name));
    end;



    или

    function TCGIRequest.GetFieldByName(const Name: AnsiString): AnsiString;

     function AdjustHTTP(const Name: AnsiString): AnsiString;
     const
       SHttp = AnsiString('HTTP_');     { do not localize }
     begin
       if AnsiStrPos(PAnsiChar(SHttp), PAnsiChar(Name)) = PAnsiChar(Name) then
         Result := Copy(Name, 6, MaxInt)
       else
         Result := SHttp + Name;
     end;

     function GetCGIEnvironmentVariable(const Name: AnsiString): AnsiString;
     const
       BufSize = 1024;
     var
       Len: Integer;
       Buffer: array[0..BufSize - 1] of AnsiChar;
     begin
       Result := '';
       Len := GetEnvironmentVariableA(PAnsiChar(Name), @Buffer, BufSize);
       if Len < BufSize then
         SetString(Result, PAnsiChar(@Buffer), Len)
       else
       begin
         SetLength(Result, Len - 1);
         GetEnvironmentVariableA(PAnsiChar(Name), PAnsiChar(Result), Len);
       end;
     end;

    begin
     Result := GetCGIEnvironmentVariable(Name);
     if Result = '' then
       Result := GetCGIEnvironmentVariable(AdjustHTTP(Name));
    end;



    Из кода видно, что без разницы,
    Request.GetFieldByName('HTTP_IF_MODIFIED_SINCE')


    или
    Request.GetFieldByName('IF_MODIFIED_SINCE')

 
Конференция "Начинающим" » TWebModule непонятки с IfModifiedSince [D7, XP]
Есть новые Нет новых   [118480   +45][b:0][p:0.005]