chromium - send custom header info on initial page load c#
Asked Answered
M

3

11

Or How to inject a custom header into the initial request to a site when new-ing up an instance of the ChromiumWebBrowser.

I'm a noob with Chromium and could really use some help. I have a winforms app with a CEF window. K, no prob so far. What I need to do is to call/load the initial url with a custom http-header that contains authentication info. Is this possible?

The following is essentially what is at play and all parts work except the custom header (Doh!)

Winform(CEF httpRequest(with custom header)) [never gets past this point]=> C# MVC web app => Owin_Authentication_Pipeline segment => MVC Response with populated Razor view => Shows up in Winform Chromium app.

Maybe this will help as well:

using CefSharp;
using CefSharp.WinForms;
...
private void Form1_Load(object sender, EventArgs e)
{
    Cef.Initialize();
    ChromiumWebBrowser myBrowser = new ChromiumWebBrowser("whatever.com");
    // ??How do i get a custom header be sent with the above line??

    myBrowser.Dock = DockStyle.Fill;
    //myBrowser.ShowDevTools();
    //myBrowser.RequestHandler = new DSRequestHander();
    //myBrowser.FrameLoadStart += myBrowser_FrameLoadStart;
    this.Controls.Add(myBrowser);
}

I Groggled this to death, looked, tried all the tricks in my toolbox and then some.

Any ideas, help or hints on how I might be able to solve or get around this boggler is greatly appreciated. Thanks in advance.

Massorete answered 6/7, 2015 at 16:18 Comment(7)
Are you using the CEFSharp project? github.com/cefsharp/CefSharp/wikiBalfour
@Balfour Yes, yes i am.Massorete
It's next to impossible to help you if you don't share more info e.g. on how far in this chain of components your custom header gets. Does it appear at the Owin_Authentication_Pipeline segment? Does it reject it? Etc. etc. Provide us with some basic debugging info of what you have tried. Good stackoverflow questions have an MVCESenate
@Senate ..chill. no need for a flair of denigration. And 'no' it does not reach the owin pipeline. The question is how to inject a custom header into the initial request to a site when new-ing up a ChromiumWebBrowser. I have edited the question to clarify. Thank you for you feedback. Cheers...it's all good.Massorete
No intention to denigrate at all - just want to take out as much guesswork out to better be able to offer help :-). Your edits greatly helped with that! Have you tried something along the lines of groups.google.com/forum/m/#!topic/cefsharp/Yi9bWHmASQU ? That post is a bit dated now, not sure if the API has changed since then.Senate
The API has changed a little, pretty much the same concept though. I split Request and Response out into two separate objects quite some time ago. At some point we should probably wrap the underlying HeaderMap object to provide for a slightly nicer API.Casework
Although this project has morphed away from the CEFSharp project, I am going to work on a simple proof of concept (if not already done) and push it out to Github for anyone who might be interested. Thanks to all that have commented on this question. -felixdMassorete
C
17

Updated to reflect major Chromium changes

Updated to reflect changes made in version 75 (should work in 75 and newer)

The method you're after should be OnBeforeResourceLoad, a basic example should look like:

public class CustomResourceRequestHandler : ResourceRequestHandler
{
    protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback)
    {
        var headers = request.Headers;
        headers["User-Agent"] = "My User Agent";
        request.Headers = headers;

        return CefReturnValue.Continue;
    }
}

public class CustomRequestHandler : RequestHandler
{
    protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling)
    {
        return new CustomResourceRequestHandler();
    }
}

browser.RequestHandler = new CustomRequestHandler();

Using the IRequest.Headers property you must read the headers property, make changes then reassign it. It's now possible to use the SetHeaderByName/GetHeaderByName functions to get/set a single header.

Casework answered 12/7, 2015 at 11:15 Comment(8)
I created a CustomChromeBrowser class inheriting from ChromiumWebBrowser and assign a RequestHandler class (comes with demo project) via Initialized event of the CustomChromeBrowser class.Dionysian
All the Handlers are simple properties, assigning them in the constructor would be sufficient.Casework
Can we have more details please, I've the same issue and I'm not sure to understand how to do what you suggest.Uranian
This isn't working for me. The Headers property of the request just isn't changing. No exception is being thrown, it just won't update. Headers.IsReadOnly is false.Camden
@Casework Will this automatically apply to page requests and ajax requests? I am mainly wondering about changing the ajax requests and how to accomplish that. It looks like it should because i don't see other ways to accomplish scanning the API Doc. Anyway a word from you will kind of make my design decision and keep me from going other directions.Mastership
All requests directly related to the browser should flow through here. You can use cefsharp.github.io/api/81.3.x/html/… for RequestContext level handling. If you are having trouble isolating just the Ajax requests please ask a new question.Casework
See github.com/cefsharp/CefSharp/wiki/… for background details for RequestContext. From memory requests from Service workers will only be accessible through the RequestContext level handling as they aren't necessarily directly associated with a browser.Casework
@Casework Thanks! you do a great job on CefSharp. Working like a charm thanks for all you do. +3Mastership
U
6

You should create a class that implement IRequestHandler then set an instance of that class as RequestHandler in your browser object.

With version 53, that class should look like:

class ChromeBrowserRequestHandler: IRequestHandler
    {
        public bool GetAuthCredentials(IWebBrowser browserControl, IBrowser browser, IFrame frame, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback)
        {
            return false;
        }

        public bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isRedirect)
        {
            return false;
        }

        public bool OnBeforePluginLoad(IWebBrowser browser, string url, string policyUrl, WebPluginInfo info)
        {
            return false;
        }

        public CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback)
        {
            var headers = request.Headers;
            headers["Custom-Header"] = "My Custom Header";
            request.Headers = headers;

            return CefReturnValue.Continue;
        }

        public bool OnCertificateError(IWebBrowser browser, CefErrorCode errorCode, string requestUrl)
        {
            return false;
        }

        public void OnPluginCrashed(IWebBrowser browser, string pluginPath)
        {
        }

        public void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status)
        {
        }

        public IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response) 
        { 
            return null; 
        }

        public bool OnCertificateError(IWebBrowser browserControl, IBrowser browser, CefErrorCode errorCode, string requestUrl, ISslInfo sslInfo, IRequestCallback callback) 
        {
            return false; 
        }

        public bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture) 
        {
            return false; 
        }

        public void OnPluginCrashed(IWebBrowser browserControl, IBrowser browser, string pluginPath) 
        {
        }

        public bool OnProtocolExecution(IWebBrowser browserControl, IBrowser browser, string url) 
        {
            return false;
        }

        public bool OnQuotaRequest(IWebBrowser browserControl, IBrowser browser, string originUrl, long newSize, IRequestCallback callback) 
        {
            return false;
        }

        public void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser) 
        { 
        }

        public void OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength)
        { 
        }

        public void OnResourceRedirect(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, ref string newUrl) 
        { 
        }

        public bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response) 
        {
            return false;
        }
    }

Then, while creating your browser object:

ChromiumWebBrowser myBrowser = new ChromiumWebBrowser("whatever.com")
{
    RequestHandler = new ChromeBrowserRequestHandler()
};

Note that the request handler must be set before loading the page. If you can't set the request handler during instanctiaction, you can still set it later a reload the page with myBrowser.Load("whatever.com") .

Uranian answered 15/12, 2016 at 23:8 Comment(1)
Using version 47.0.2.0, I'm not seeing any change to the request headers in dev tools.Hydrotherapeutics
C
6

In the one of the latest versions some callbacks have been moved from IRequestHandler to IResourceRequestHandler interface. Simplest way is to override default implementations RequestHandler & ResourceRequestHandler, for example:

class BearerAuthResourceRequestHandler : ResourceRequestHandler
    {
        public BearerAuthResourceRequestHandler(string token)
        {
            _token = token;
        }

        private string _token;

        protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback)
        {
            if (!string.IsNullOrEmpty(_token))
            {
                var headers = request.Headers;
                headers["Authorization"] = $"Bearer {_token}";
                request.Headers = headers;
                return CefReturnValue.Continue;
            }
            else return base.OnBeforeResourceLoad(chromiumWebBrowser, browser, frame, request, callback);
        }

    }
    class BearerAuthRequestHandler : RequestHandler
    {
        public BearerAuthRequestHandler(string token)
        {
            _token = token;
        }

        private string _token;

        protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling)
        {
            if (!string.IsNullOrEmpty(_token)) return new BearerAuthResourceRequestHandler(_token);
            else return base.GetResourceRequestHandler(chromiumWebBrowser, browser, frame, request, isNavigation, isDownload, requestInitiator, ref disableDefaultHandling);
        }
    }

Then, assign it to browser RequestHandler:

Browser.RequestHandler = new BearerAuthRequestHandler(token);
Ctenoid answered 31/7, 2019 at 13:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.