Disclosure Statement: This site contains affiliate links, which means that I may receive a commission if you make a purchase using these links. As an eBay Partner, I earn from qualifying purchases.

Change response headers.

Post Reply
michal@dorfin.waw.pl
Posts: 41
Joined: Sun Feb 05, 2017 8:53 am

Change response headers.

Post by michal@dorfin.waw.pl »

I'd like to change some values in response headers before the Chrome will acknowledged them. I tried to do this in "OnResourceResponse" with this code:

Code: Select all

            newHeaderMap := TCefStringMultimapOwn.Create;
            TempHeaderMap := TCefStringMultimapOwn.Create;
            aResponse.GetHeaderMap(TempHeaderMap);
            i := 0;
            j := TempHeaderMap.Size;
            while (i < j) do
              begin
               key:=TempHeaderMap.Key[i];
               value:=TempHeaderMap.Value[i];
               if lowercase(key)='content-security-policy' then
                  BEGIN
                   value:='';
                  END;
                newHeaderMap.Append(key, value);
                inc(i);
              end;
            aResponse.SetHeaderMap(newHeaderMap);
Unfortunatelly the headers remain the same.
Is there any other possibility to change response headers.
User avatar
salvadordf
Posts: 4016
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Change response headers.

Post by salvadordf »

The documentation for that event states that the response parameter cannot be modified in that event :
https://magpcss.org/ceforum/apidocs3/projects/(default)/CefResourceRequestHandler.html

The CEF project maintainer suggests using an in-between proxy as the best option :
https://magpcss.org/ceforum/viewtopic.php?f=6&t=14042
michal@dorfin.waw.pl
Posts: 41
Joined: Sun Feb 05, 2017 8:53 am

Re: Change response headers.

Post by michal@dorfin.waw.pl »

If found that I should use my own TCefResourceHandler.
So I tried this:

Code: Select all

{$I cef.inc}

interface
uses
  System.Classes, WinApi.Windows, System.SysUtils,
  uCEFInterfaces, uCEFTypes, uCEFResourceHandler,uCEFUrlrequestClient;

type
  TA21CEFResourceHandler = class(TCefResourceHandlerOwn)
  private
    FOffset: NativeUInt;
    FStream: TMemoryStream;
    FCallback: ICefCallback;
    FResponse: ICefResponse;
  protected
    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); reintroduce;
    destructor Destroy; override;
    procedure WriteResponse(const Request: ICefUrlRequest; Data: Pointer; Size: NativeUInt); virtual;
    procedure CompleteRequest(const Request: ICefUrlRequest); virtual;
  end;

  TA21HttpRequestClient = class(TCefUrlrequestClientOwn)
  private
    FHandler: TA21CEFResourceHandler;
  protected
    procedure OnDownloadData(const Request: ICefUrlRequest; Data: Pointer; DataLength: NativeUInt); override;
    procedure OnRequestComplete(const Request: ICefUrlRequest); override;
  public
    constructor Create(Handler: TA21CEFResourceHandler); reintroduce;
  end;


implementation
Uses uCefStringMultimap, uCEFUrlRequest;


constructor TA21CEFResourceHandler.Create(const Browser: ICefBrowser; const Frame: ICefFrame; const SchemeName: ustring; const Request: ICefRequest);
begin
  inherited Create(Browser, Frame, SchemeName, Request);
  FStream := TMemoryStream.Create;
end;

destructor TA21CEFResourceHandler.Destroy;
begin
  FStream.Free;
  inherited;
end;

function TA21CEFResourceHandler.ProcessRequest(const Request: ICefRequest; const Callback: ICefCallback): Boolean;
begin
  Result := True;
  // reset the offset value
  FOffset := 0;
  // store the callback reference
  FCallback := Callback;
  // create the URL request that will perform actual data exchange (you can replace
  // it with any other; e.g. with MSXML, or an Indy client)
  TCefUrlRequestRef.New(Request, TA21HttpRequestClient.Create(Self), Nil);
end;

procedure TA21CEFResourceHandler.GetResponseHeaders(const Response: ICefResponse; out ResponseLength: Int64; out RedirectUrl: ustring);
var
  HeaderMap: ICefStringMultimap;
begin
  // return the size of the data we have in the response stream
  ResponseLength := FStream.Size;
  // fill the header fields from the response returned by the URL request
  Response.Status := FResponse.Status;
  Response.StatusText := FResponse.StatusText;
  Response.MimeType := FResponse.MimeType;
  // copy the header map from the response returned by the URL request
  HeaderMap := TCefStringMultimapOwn.Create;
  FResponse.GetHeaderMap(HeaderMap);
  if HeaderMap.Size <> 0 then
    FResponse.SetHeaderMap(HeaderMap);
end;

function TA21CEFResourceHandler.ReadResponse(const DataOut: Pointer; BytesToRead: Integer; var BytesRead: Integer; const Callback: ICefCallback): Boolean;
begin
  // since this method can be called multiple times (reading in chunks), check if we
  // have still something to transfer
  if FOffset < FStream.Size then
  begin
    Result := True;
    BytesRead := BytesToRead;
    // copy the data from the response stream to the browser buffer
    Move(Pointer(NativeUInt(FStream.Memory) + FOffset)^, DataOut^, BytesRead);
    // increment the offset by the amount of data we just copied
    Inc(FOffset, BytesRead);
  end
  else
    Result := False;
end;

procedure TA21CEFResourceHandler.WriteResponse(const Request: ICefUrlRequest; Data: Pointer; Size: NativeUInt);
begin
  // write the just downloaded data to the intermediate response stream
  FStream.Write(Data^, Size);
end;

procedure TA21CEFResourceHandler.CompleteRequest(const Request: ICefUrlRequest);
begin
  FStream.Position := 0;
  // store the response reference for the GetResponseHeaders method
  FResponse := Request.GetResponse;
  // this signals the handler that the request has completed and that it can process
  // the response headers and pass the content to the browser
  if Assigned(FCallback) then
    FCallback.Cont;
end;

{ TA21HttpRequestClient }

constructor TA21HttpRequestClient.Create(Handler: TA21CEFResourceHandler);
begin
  inherited Create;
  FHandler := Handler;
end;

procedure TA21HttpRequestClient.OnDownloadData(const Request: ICefUrlRequest; Data: Pointer; DataLength: NativeUInt);
begin
  inherited;
  FHandler.WriteResponse(Request, Data, DataLength);
end;

procedure TA21HttpRequestClient.OnRequestComplete(const Request: ICefUrlRequest);
begin
  inherited;
  FHandler.CompleteRequest(Request);
end;
And I'm using it like that way:

Code: Select all

procedure TMainForm.CHROMEGetResourceHandler(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame; const request: ICefRequest; var aResourceHandler: ICefResourceHandler);
begin
 aResourceHandler:=TA21CEFResourceHandler.Create(browser, frame, 'TA21CEFResourceHandler', request);
end;
I this code I haven't changed any header yet. Just try to use this handler.
But when I did that I have a strange result when I open any website:
screen.png
Have I missed something in my handler ?
You do not have the required permissions to view the files attached to this post.
User avatar
salvadordf
Posts: 4016
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Change response headers.

Post by salvadordf »

I haven't tested your code but it looks like there's an issue copying the data to/from the stream or perhaps a string encoding issue.
Write the stream contents in a local file to compare them with the original and check the "Content-Encoding" and "Content-Type" headers.
michal@dorfin.waw.pl
Posts: 41
Joined: Sun Feb 05, 2017 8:53 am

Re: Change response headers.

Post by michal@dorfin.waw.pl »

You are right. There was a bug in GetResponseHeaders.
Here is code that works better:

Code: Select all

unit Klasy.A21CEFResourceHandler;

{$I cef.inc}

interface
uses
  System.Classes, WinApi.Windows, System.SysUtils,
  uCEFInterfaces, uCEFTypes, uCEFResourceHandler,uCEFUrlrequestClient, uCEFConstants;

type
  TA21CEFResourceHandler = class(TCefResourceHandlerOwn)
  private
    FStream: TMemoryStream;
    FCallback: ICefCallback;
    FResponse: ICefResponse;
  protected
    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); reintroduce;
    destructor Destroy; override;
    procedure WriteResponse(const Request: ICefUrlRequest; Data: Pointer; Size: NativeUInt); virtual;
    procedure CompleteRequest(const Request: ICefUrlRequest); virtual;
  end;

  TA21HttpRequestClient = class(TCefUrlrequestClientOwn)
  private
    FHandler: TA21CEFResourceHandler;
  protected
    procedure OnDownloadData(const Request: ICefUrlRequest; Data: Pointer; DataLength: NativeUInt); override;
    procedure OnRequestComplete(const Request: ICefUrlRequest); override;
  public
    constructor Create(Handler: TA21CEFResourceHandler); reintroduce;
  end;


implementation
Uses uCefStringMultimap, uCEFUrlRequest;


constructor TA21CEFResourceHandler.Create(const Browser: ICefBrowser; const Frame: ICefFrame; const SchemeName: ustring; const Request: ICefRequest);
begin
  inherited Create(Browser, Frame, SchemeName, Request);
  FStream := TMemoryStream.Create;
end;

destructor TA21CEFResourceHandler.Destroy;
begin
  FStream.Free;
  inherited;
end;

function TA21CEFResourceHandler.ProcessRequest(const Request: ICefRequest; const Callback: ICefCallback): Boolean;
begin
  Result := True;
  if (FStream  <> nil) then FStream.Seek(0, soFromBeginning);
  Request.Flags:=UR_FLAG_ALLOW_STORED_CREDENTIALS;// and UR_FLAG_DISABLE_CACHE and UR_FLAG_SKIP_CACHE;
  // store the callback reference
  FCallback := Callback;
  // create the URL request that will perform actual data exchange (you can replace
  // it with any other; e.g. with MSXML, or an Indy client)
  TCefUrlRequestRef.New(Request, TA21HttpRequestClient.Create(Self), Nil);
end;

procedure TA21CEFResourceHandler.GetResponseHeaders(const Response: ICefResponse; out ResponseLength: Int64; out RedirectUrl: ustring);
var
  HeaderMap: ICefStringMultimap;
begin
  // return the size of the data we have in the response stream
  ResponseLength := FStream.Size;
  // fill the header fields from the response returned by the URL request
  Response.Status := FResponse.Status;
  Response.StatusText := FResponse.StatusText;
  Response.MimeType := FResponse.MimeType;
  Response.Charset  :=FResponse.Charset;
  Response.Error    :=FResponse.Error;
  Response.URL      :=FResponse.URL;
  // copy the header map from the response returned by the URL request
  HeaderMap := TCefStringMultimapOwn.Create;
  FResponse.GetHeaderMap(HeaderMap);
  if HeaderMap.Size <> 0 then
    Response.SetHeaderMap(HeaderMap);
end;

function TA21CEFResourceHandler.ReadResponse(const DataOut: Pointer; BytesToRead: Integer; var BytesRead: Integer; const Callback: ICefCallback): Boolean;
begin
  if (FStream <> nil) and (DataOut <> nil) then
    begin
      BytesRead := FStream.Read(DataOut^, BytesToRead);
      Result    := (BytesRead > 0);
    end
   else
    Result := False;
end;

procedure TA21CEFResourceHandler.WriteResponse(const Request: ICefUrlRequest; Data: Pointer; Size: NativeUInt);
begin
  // write the just downloaded data to the intermediate response stream
  FStream.Write(Data^, Size);
end;

procedure TA21CEFResourceHandler.CompleteRequest(const Request: ICefUrlRequest);
begin
  FStream.Position := 0;
  // store the response reference for the GetResponseHeaders method
  FResponse := Request.GetResponse;
  // this signals the handler that the request has completed and that it can process
  // the response headers and pass the content to the browser
  if Assigned(FCallback) then
    FCallback.Cont;
end;

{ TA21HttpRequestClient }

constructor TA21HttpRequestClient.Create(Handler: TA21CEFResourceHandler);
begin
  inherited Create;
  FHandler := Handler;
end;

procedure TA21HttpRequestClient.OnDownloadData(const Request: ICefUrlRequest; Data: Pointer; DataLength: NativeUInt);
begin
  inherited;
  FHandler.WriteResponse(Request, Data, DataLength);
end;

procedure TA21HttpRequestClient.OnRequestComplete(const Request: ICefUrlRequest);
begin
  inherited;
  FHandler.CompleteRequest(Request);
end;


end.
I don't know how to handle redirects.
When I use my TA21CEFResourceHandler and navigate to : "facebook.pl" - which redirects to "https://facebook.com" - there is an error on FB site: "Cannot fullfill the request. We are working to solve the problem". When I navigete to this URL with my ResourceHandler disabled - everything works fine.
Student
Posts: 72
Joined: Tue Aug 07, 2018 9:20 am

Re: Change response headers.

Post by Student »

The request does not pass the security check, try this, add security headers

Code: Select all

function TA21CEFResourceHandler.ProcessRequest(const Request: ICefRequest; const Callback: ICefCallback): Boolean;
var FRequest: ICefRequest;
HeaderMapTemp: ICefStringMultimap;
begin
  Result := True;
  if (FStream  <> nil) then FStream.Seek(0, soFromBeginning);
  FRequest := TCefRequestRef.New;
  FRequest.Flags:=UR_FLAG_ALLOW_STORED_CREDENTIALS;// and UR_FLAG_DISABLE_CACHE and UR_FLAG_SKIP_CACHE;
  // store the callback reference
  FCallback := Callback;
  // create the URL request that will perform actual data exchange (you can replace
  // it with any other; e.g. with MSXML, or an Indy client)
  
  FRequest := TCefRequestRef.New;
  
  HeaderMapTemp := TCefStringMultimapOwn.Create;
  Request.GetHeaderMap(HeaderMapTemp);
  HeaderMapTemp.Append('Sec-Fetch-Mode', 'navigate');
  
  FRequest.SetReferrer(Request.ReferrerUrl, Request.ReferrerPolicy);
  FRequest.Assign(Request.url, Request.Method, Request.PostData, HeaderMapTemp);
  
  TCefUrlRequestRef.New(FRequest, TA21HttpRequestClient.Create(Self), Nil);
end;
michal@dorfin.waw.pl
Posts: 41
Joined: Sun Feb 05, 2017 8:53 am

Re: Change response headers.

Post by michal@dorfin.waw.pl »

Thank you. It is working for FB. But unfortunatelly doesn't help in other sites when You start from one url but there is redirection for login.
Maybe let's change the level of difficulty. I want only to change response headers. It can be done only in custom resource handler. Is there a way to override the resource handler with only the "GetResponseHeaders" method. So the rest of default resource engine is default ?
Student
Posts: 72
Joined: Tue Aug 07, 2018 9:20 am

Re: Change response headers.

Post by Student »

Yes, for all sites not need set such a header, I showed an example of how you can get around the problem, filter problem sites that are loaded incorrectly by default by setting the desired header, view the headers through the sniffer as the site loads without ResourceHandler and replace or add those that are missing when loading via ResourceHandler.

If I find out another way to solve this problem, I will write.
Post Reply