URL-адреса больших двоичных объектов и ошибки разных источников с помощью DCEF GetResourceHandler

Я реализовал вызов GetResourceHandler в Delphi 2007, используя последнюю оболочку Delphi Chromium Embedded (которая использует libcef 3.1750.1738, я думаю, скомпилирован создателем DCEF3). Я основал свой обработчик на некотором коде Python, размещенном на сайте magpcss (с некоторыми изменениями).

Все работает хорошо, за исключением пары моментов. Прежде всего, URL-адреса «blob:» не работают, и я ломаю голову, как их получить. Я даже не знаю о них в целом, так что это часть проблемы.

Во-вторых, при использовании моего обработчика ресурсов появляются некоторые вещи из разных источников, которые в противном случае не появляются. Это в моих тестах против https://maps.google.com/. Я получил:

GET blob:https%3A//map.google.com/93e08d0a-b8b3-4c60-8a82-71d424b0893c 404 (Not Found) rs=ACT90oHHUBc59MzTbg1AU48l7w-F8f1yuA:1107
GET blob:https%3A//map.google.com/4c89a387-5958-4c19-ae9c-0640007db009 404 (Not Found) rs=ACT90oHHUBc59MzTbg1AU48l7w-F8f1yuA:1107
GET blob:https%3A//map.google.com/bd839949-a77b-43a1-9f47-1cb841f858a9 404 (Not Found) /maps/_/js/k=maps.m.en.oD0-LnUwxmc.O/m=sy437,sy444,sy446,sy486,sy494,wrc,sy438,sy436,vw,sy139,sy220…:134
XMLHttpRequest cannot load https://mt0.google.com/vt?pb=!1m8!4m7!2u15!5m2!1x450831026!2x3361961734!6m2!1x451029165!2x3362284457!2m1!1e0!3m1!5e1105!4e5!18m1!1b1. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://map.google.com' is therefore not allowed access. /maps/@45.0930104,-93.28442,15z:1
XMLHttpRequest cannot load https://mt0.google.com/vt/pb=!1m8!3m7!1m2!1u2020096!2u3014144!2m2!1u1024!2u768!3i15!2m3!1e0!2sm!3i290!3m2!2sen!5e1105!4e4!11m2!1e2!2b1!20m1!1b1. No 'Access-Control-Allow-    Origin' header is present on the requested resource. Origin 'https://map.google.com' is therefore not allowed access. /maps/@45.0930104,-93.28442,15z:1

Тем не менее, ни одна из этих проблем не возникает, если я опускаю обработчик ресурсов и использую CEF без него. Я обнаружил, что могу обойти проблемы CORS, отключив веб-безопасность. Это вряд ли кажется идеальным и, опять же, не нужно при использовании CEF без моего обработчика ресурсов.

Вот мой код обработчика ресурсов:

unit WebInterceptHandler;

interface

uses
  ceflib, Classes;

type
  TWebInterceptHandler = class(TCefResourceHandlerOwn)
  protected
    FDataStream: TMemoryStream;
    FResponseHeadersReadyCallback: ICefCallback;
    FOffsetRead: NativeUInt;
    FResponse: ICefResponse;

    function ProcessRequest(const Request: ICefRequest;
        const Callback: ICefCallback): Boolean; override;
    procedure GetResponseHeaders(const Response: ICefResponse;
        out ResponseLength: Int64; out RedirectUrl: ustring); override;
    function ReadResponse(const DataOut: Pointer; BytesToRead: Integer;
        var BytesRead: Integer; const Callback: ICefCallback): Boolean; override;
  public
    constructor Create(const Browser: ICefBrowser; const Frame: ICefFrame;
        const SchemeName: ustring; const Request: ICefRequest); override;
    destructor Destroy; override;
  end;

implementation

uses windows, sysutils;

type
  TWebInterceptHandlerClient = class(TCefUrlRequestClientOwn)
  private
    FResourceHandler: TWebInterceptHandler;
  protected
    procedure OnDownloadData(const Request: ICefUrlRequest; Data: Pointer;
        DataLength: NativeUInt); override;
    procedure OnRequestComplete(const Request: ICefUrlRequest); override;
  end;

{ TWebInterceptHandlerClient }

procedure TWebInterceptHandlerClient.OnDownloadData(const Request: ICefUrlRequest;
  Data: Pointer; DataLength: NativeUInt);
begin
  inherited;

  FResourceHandler.FDataStream.Write(Data^, DataLength);

  //OutputDebugStringW(PWideChar(Request.GetRequest.Url));
end;

procedure TWebInterceptHandlerClient.OnRequestComplete(const Request: ICefUrlRequest);
begin
  inherited;

  FResourceHandler.FResponse := Request.GetResponse;
  if FResourceHandler.FResponseHeadersReadyCallback <> nil then
    FResourceHandler.FResponseHeadersReadyCallback.Cont;
end;


{ TWebInterceptHandler }

constructor TWebInterceptHandler.Create(const Browser: ICefBrowser;
  const Frame: ICefFrame; const SchemeName: ustring;
  const Request: ICefRequest);
begin
  inherited;

  FDataStream := TMemoryStream.Create;
end;

//function HTTPDecode(const AStr: ustring): rbstring;
//var
//  Sp, Rp, Cp: PAnsiChar;
//  src: rbstring;
//begin
//  src := rbstring(AStr);
//  SetLength(Result, Length(src));
//  Sp := PAnsiChar(src);
//  Rp := PAnsiChar(Result);
//  while Sp^ <> #0 do
//  begin
//    case Sp^ of
//      '+': Rp^ := ' ';
//      '%': begin
//             Inc(Sp);
//             if Sp^ = '%' then
//               Rp^ := '%'
//             else
//             begin
//               Cp := Sp;
//               Inc(Sp);
//               if (Cp^ <> #0) and (Sp^ <> #0) then
//                 Rp^ := AnsiChar(StrToInt('$' + Char(Cp^) + Char(Sp^)))
//               else
//               begin
//                 Result := '';
//                 Exit;
//               end;
//             end;
//           end;
//    else
//      Rp^ := Sp^;
//    end;
//    Inc(Rp);
//    Inc(Sp);
//  end;
//  SetLength(Result, Rp - PAnsiChar(Result));
//end;

destructor TWebInterceptHandler.Destroy;
begin
  FDataStream.Free;

  inherited;
end;

function TWebInterceptHandler.ProcessRequest(const Request: ICefRequest;
      const Callback: ICefCallback): Boolean;
var
  wrc: TWebInterceptHandlerClient;
//  url: ustring;
//  i: Integer;
//  headerMap: ICefStringMultimap;
begin
  //headerMap := TCefStringMultimapOwn.Create;

  //OutputDebugStringW(PWideChar(Request.Url));

  //Request.GetHeaderMap(headerMap);
  //if headerMap.Size <> 0 then
  //  for i := 0 to headerMap.Size - 1 do
  //    OutputDebugStringW(PWideChar(headerMap.Key[i] + ': ' + headerMap.Value[i]));

  //if Pos('blob:', WideLowerCase(Request.Url)) = 1 then
  //  request.Url := HTTPDecode(Copy(Request.Url, 1, Length(Request.Url) - Length('blob:')));

  FOffsetRead := 0;

  FResponseHeadersReadyCallback := Callback;
  wrc := TWebInterceptHandlerClient.Create;
  wrc.FResourceHandler := Self;
  TCefUrlRequestRef.New(request, wrc);

  Result := True;
end;

procedure TWebInterceptHandler.GetResponseHeaders(const Response: ICefResponse;
    out ResponseLength: Int64; out RedirectUrl: ustring);
var
  headerMap: ICefStringMultimap;
begin
  headerMap := TCefStringMultimapOwn.Create;
  Response.Status := FResponse.Status;
  Response.StatusText := FResponse.StatusText;
  Response.MimeType := FResponse.MimeType;

  FResponse.GetHeaderMap(headerMap);
  if headerMap.Size <> 0 then
    FResponse.SetHeaderMap(headerMap);

  ResponseLength := FDataStream.Size;
end;

function TWebInterceptHandler.ReadResponse(const DataOut: Pointer; BytesToRead:
    Integer; var BytesRead: Integer; const Callback: ICefCallback): Boolean;
begin
  if FOffsetRead < FDataStream.Size then
  begin
    BytesRead := BytesToRead;

    Move(Pointer(NativeUInt(FDataStream.Memory) + FOffsetRead)^, DataOut^,
        BytesRead);

    Inc(FOffsetRead, BytesRead);

    Result := True;
  end
  else
    Result := False;
end;

end.

Который я звоню из тестового приложения:

object MainForm: TMainForm
  Left = 0
  Top = 0
  Caption = 'MainForm'
  ClientHeight = 716
  ClientWidth = 752
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object URLBox: TEdit
    Left = 0
    Top = 0
    Width = 752
    Height = 21
    Align = alTop
    TabOrder = 0
    Text = 'https://map.google.com/'
    OnKeyPress = URLBoxKeyPress
  end
  object Panel1: TPanel
    Left = 0
    Top = 21
    Width = 752
    Height = 41
    Align = alTop
    BevelOuter = bvNone
    TabOrder = 1
    ExplicitLeft = 296
    ExplicitTop = 360
    ExplicitWidth = 185
    object Button1: TButton
      Left = 8
      Top = 8
      Width = 120
      Height = 25
      Caption = 'Show Dev Tools'
      TabOrder = 0
      OnClick = Button1Click
    end
    object UseResourceHandlerBox: TCheckBox
      Left = 152
      Top = 12
      Width = 233
      Height = 17
      Caption = 'Use Resource Handler'
      TabOrder = 1
    end
  end
end


unit MainUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, cefgui, ceflib, cefvcl, StdCtrls, ExtCtrls;

type
  TMainForm = class(TForm)
    URLBox: TEdit;
    Panel1: TPanel;
    Button1: TButton;
    UseResourceHandlerBox: TCheckBox;
    procedure FormCreate(Sender: TObject);
    procedure URLBoxKeyPress(Sender: TObject; var Key: Char);
    procedure Button1Click(Sender: TObject);
  private
    browser: TChromium;
    procedure BrowserGetResourceHandler(Sender: TObject;
        const Browser: ICefBrowser; const Frame: ICefFrame;
        const Request: ICefRequest; out Result: ICefResourceHandler);
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

uses
  WebInterceptHandler;

procedure TMainForm.URLBoxKeyPress(Sender: TObject; var Key: Char);
begin
  if Key = #13 then
  begin
    if UseResourceHandlerBox.Checked then
      browser.OnGetResourceHandler := BrowserGetResourceHandler
    else
      browser.OnGetResourceHandler := nil;

    browser.Load(URLBox.Text);
  end;
end;

procedure TMainForm.Button1Click(Sender: TObject);
begin
  browser.ShowDevTools;
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  browser := TChromium.Create(Self);
  browser.Align := alClient;
  browser.Parent := Self;
  //browser.Options.WebSecurity := STATE_DISABLED;
end;

procedure TMainForm.BrowserGetResourceHandler(Sender: TObject; const Browser:
    ICefBrowser; const Frame: ICefFrame; const Request: ICefRequest;
    out Result: ICefResourceHandler);
begin
  Result := TWebInterceptHandler.Create(Browser, Frame, 'webintercept', Request);
end;

end.

program DCEF3WebIntercept;

uses
  ceflib,
  Forms,
  MainUnit in 'MainUnit.pas' {MainForm},
  WebInterceptHandler in 'WebInterceptHandler.pas';

{$R *.res}

begin
  CefCache := 'cache';
  CefSingleProcess := False;
  if not CefLoadLibDefault then
    Exit;

  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TMainForm, MainForm);
  Application.Run;
end.

Наряду с любой помощью по этим проблемам приветствуются любые предложения по улучшению этого кода.

Кроме того, если кому-то интересно, я планирую позже изменить этот код, чтобы я мог изменить ответ. Сейчас это просто проверка концепции GetResourceHandler.


person jep    schedule 03.02.2015    source источник


Ответы (1)


URL-адрес большого двоичного объекта — это специальный URL-адрес, который ссылается на данные, которые ваш браузер в настоящее время имеет в памяти для текущей страницы. Мне кажется, что в BrowserGetResourceHandler() вы должны определить, начинается ли URL-адрес с «blob:», и в таком случае вернуть NULL, и позволить Chromium обрабатывать этот URL-адрес внутри. Если это все еще не работает, это может быть ограничение в CEF, и об ошибке следует сообщить.

Проблема безопасности CORS, по-видимому, вызвана отсутствием заголовков в запросе/ответе. Выполните отладку:

  1. Проверить заголовки в Google Chrome для того же запроса
  2. В TWebInterceptHandler.GetResponseHeaders проверьте заголовки в аргументе headerMap и убедитесь, что они соответствуют заголовкам в Google Chrome.
  3. Убедитесь, что вызывается FResponse.SetHeaderMap(), и убедитесь, что заголовки были успешно установлены в ответе — вызовите FResponse.GetHeaderMap и сравните его с аргументом headerMap.
person Czarek Tomczak    schedule 03.02.2015
comment
Для большого двоичного объекта обход установки обработчика ресурсов сработал (кажется настолько очевидным, но, по-видимому, это не так!) Что касается проблемы CORS, я буду ее проверять. Это кажется очень странным, поскольку я напрямую копирую исходные заголовки запроса в обернутый CefUrlRequest. Так что странно, что это будет нормально работать с CEF, выполняющим запрос, но не с CEF + мой обработчик ресурсов, выполняющий запрос. Но я все еще пытаюсь исправить некоторые другие вещи, поэтому мне придется вернуться к вам, чтобы они работали. - person jep; 04.02.2015