Search code examples
cookieshttp-headersabcpdf

abcpdf HttpAdditionalHeaders cookies


Hi I'm trying to add cookies to the http headers when generating a pdf file with abcpdf. What I have done so far is based on the documentation:

http://www.websupergoo.com/helppdf9net/default.htm?page=source%2F5-abcpdf%2Fxhtmloptions%2F2-properties%2Fhttpadditionalheaders.htm

When requesting the page to be converted to PDF from my browser the headers are:

Connection: keep-alive
Accept: text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01
Accept-Encoding: gzip, deflate
Accept-Language: en-ie
Cookie: __utma=111872281.2030591713.1334325919.1348754241.1349943306.47; __utmz=111872281.1334325919.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); CurrentClient=AMF; UserHasMultipleClients=False; favFunds=%5B%7B%22id%22%3A57%2C%22alias_id%22%3A1690%7D%2C%7B%22id%22%3A794%2C%22alias_id%22%3Anull%7D%2C%7B%22id%22%3A818%2C%22alias_id%22%3Anull%7D%2C%7B%22id%22%3A21%2C%22alias_id%22%3Anull%7D%5D; favComparisons=%5B%7B%22selectedFunds%22%3A%5B%7B%22id%22%3A%22794%22%2C%22alias%22%3A%22null%22%7D%2C%7B%22id%22%3A%2217%22%2C%22alias%22%3A%22null%22%7D%2C%7B%22id%22%3A%22593%22%2C%22alias%22%3A%22old%22%7D%2C%7B%22id%22%3A%22593%22%2C%22alias%22%3A%221606%22%7D%5D%2C%22selectedIndexes%22%3A%5B%7B%22id%22%3A%2224002259%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22timePeriosSelection%22%3A%7B%22dateOne%22%3A%2223-11-2011%22%2C%22dateTwo%22%3A%2223-11-2012%22%7D%2C%22defaultTimePeriodSelection%22%3A%7B%22OneYear%22%3Atrue%2C%22ThreeYears%22%3Afalse%2C%22FiveYears%22%3Afalse%7D%7D%2C%7B%22selectedFunds%22%3A%5B%7B%22id%22%3A%22774%22%2C%22alias%22%3A%22null%22%7D%2C%7B%22id%22%3A%22215%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22selectedIndexes%22%3A%5B%7B%22id%22%3A%2224002259%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22timePeriosSelection%22%3A%7B%22dateOne%22%3A%2223-11-2011%22%2C%22dateTwo%22%3A%2223-11-2012%22%7D%2C%22defaultTimePeriodSelection%22%3A%7B%22OneYear%22%3Atrue%2C%22ThreeYears%22%3Afalse%2C%22FiveYears%22%3Afalse%7D%7D%2C%7B%22selectedFunds%22%3A%5B%7B%22id%22%3A%22215%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22selectedIndexes%22%3A%5B%7B%22id%22%3A%2224002259%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22timePeriosSelection%22%3A%7B%22dateOne%22%3A%2223-11-2011%22%2C%22dateTwo%22%3A%2223-11-2012%22%7D%2C%22defaultTimePeriodSelection%22%3A%7B%22OneYear%22%3Atrue%2C%22ThreeYears%22%3Afalse%2C%22FiveYears%22%3Afalse%7D%7D%5D
Host: localhost:63706
Referer: http://localhost:63706/
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20100101 Firefox/15.0.1
X-Requested-With: XMLHttpRequest

When the request comes from abcpdf the headers by are:

Connection: keep-alive
Keep-Alive: 115
Accept: text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01
Accept-Charset: ISO-8859-1, utf-8; q=0.7, *; q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-us, en; q=0.5
Host: localhost:63706
Referer: http://localhost:63706/
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/ /2.0.1
X-Requested-With: XMLHttpRequest

As you can see it is missing the cookies. So I manually aded the following to the headers(theDoc.HtmlOptions.HttpAdditionalHeaders value before abcpdf send request)

Cookie: __utma=111872281.2030591713.1334325919.1348754241.1349943306.47; __utmz=111872281.1334325919.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); CurrentClient=AMF; UserHasMultipleClients=False; favFunds=%5B%7B%22id%22%3A57%2C%22alias_id%22%3A1690%7D%2C%7B%22id%22%3A794%2C%22alias_id%22%3Anull%7D%2C%7B%22id%22%3A818%2C%22alias_id%22%3Anull%7D%2C%7B%22id%22%3A21%2C%22alias_id%22%3Anull%7D%5D; favComparisons=%5B%7B%22selectedFunds%22%3A%5B%7B%22id%22%3A%22794%22%2C%22alias%22%3A%22null%22%7D%2C%7B%22id%22%3A%2217%22%2C%22alias%22%3A%22null%22%7D%2C%7B%22id%22%3A%22593%22%2C%22alias%22%3A%22old%22%7D%2C%7B%22id%22%3A%22593%22%2C%22alias%22%3A%221606%22%7D%5D%2C%22selectedIndexes%22%3A%5B%7B%22id%22%3A%2224002259%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22timePeriosSelection%22%3A%7B%22dateOne%22%3A%2223-11-2011%22%2C%22dateTwo%22%3A%2223-11-2012%22%7D%2C%22defaultTimePeriodSelection%22%3A%7B%22OneYear%22%3Atrue%2C%22ThreeYears%22%3Afalse%2C%22FiveYears%22%3Afalse%7D%7D%2C%7B%22selectedFunds%22%3A%5B%7B%22id%22%3A%22774%22%2C%22alias%22%3A%22null%22%7D%2C%7B%22id%22%3A%22215%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22selectedIndexes%22%3A%5B%7B%22id%22%3A%2224002259%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22timePeriosSelection%22%3A%7B%22dateOne%22%3A%2223-11-2011%22%2C%22dateTwo%22%3A%2223-11-2012%22%7D%2C%22defaultTimePeriodSelection%22%3A%7B%22OneYear%22%3Atrue%2C%22ThreeYears%22%3Afalse%2C%22FiveYears%22%3Afalse%7D%7D%2C%7B%22selectedFunds%22%3A%5B%7B%22id%22%3A%22215%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22selectedIndexes%22%3A%5B%7B%22id%22%3A%2224002259%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22timePeriosSelection%22%3A%7B%22dateOne%22%3A%2223-11-2011%22%2C%22dateTwo%22%3A%2223-11-2012%22%7D%2C%22defaultTimePeriodSelection%22%3A%7B%22OneYear%22%3Atrue%2C%22ThreeYears%22%3Afalse%2C%22FiveYears%22%3Afalse%7D%7D%5D

This is the full code:

public MemoryStream HtmlToPdf(Uri pdfUrl, HttpCookieCollection cookies, bool landscape)
{
    var pdfStream = new MemoryStream();
    var theDoc = new Doc();
    if (landscape)
    {
        // apply a rotation transform
        double w = theDoc.MediaBox.Width;
        double h = theDoc.MediaBox.Height;
        double l = theDoc.MediaBox.Left;
        double b = theDoc.MediaBox.Bottom;
        theDoc.Transform.Rotate(90, l, b);
        theDoc.Transform.Translate(w, 0);
        // rotate our rectangle
        theDoc.Rect.Width = h;
        theDoc.Rect.Height = w;
    }

    theDoc.HtmlOptions.Engine = EngineType.Gecko;
    theDoc.HtmlOptions.Timeout = 60000;
    theDoc.HtmlOptions.UseScript = true;
    theDoc.HtmlOptions.OnLoadScript = "(function(){window.ABCpdf_go = false; setTimeout(function(){window.ABCpdf_go = true;}, 1000);})();";
    var url = Uri.UnescapeDataString(pdfUrl.ToString());

    if (cookies.Count > 0)
    {
        //send copy of cookies from original request
        var additionalHeadersString = "Cookie: ";
        foreach (var key in cookies.AllKeys)
        {
            if (!cookies.AllKeys[0].Equals(key)) //is NOT first element in cookie collection
            {
                additionalHeadersString += "; ";
            }
            additionalHeadersString += key + "=" + cookies[key].Value;
        }
        additionalHeadersString += "\r\n";
        theDoc.HtmlOptions.HttpAdditionalHeaders = additionalHeadersString;
        theDoc.HtmlOptions.NoCookie = true;
        theDoc.HtmlOptions.HostWebBrowser = false;
    }

    //send request
    var pageRef = theDoc.AddImageUrl(url, true, 1200, false);
    while (theDoc.Chainable(pageRef))
    {
        theDoc.Page = theDoc.AddPage();
        pageRef = theDoc.AddImageToChain(pageRef);
    }
    for (var i = 1; i <= theDoc.PageCount; i++)
    {
        theDoc.PageNumber = i;
        theDoc.Flatten();
    }

    theDoc.Save(pdfStream);
    theDoc.Clear();
    var byteInfo = pdfStream.ToArray();
    pdfStream.Write(byteInfo, 0, byteInfo.Length);
    pdfStream.Position = 0;
    return pdfStream;
}

The problem is that the cookies still missing from the header after using the HttpAdditionalHeaders property, do you know what I'm missing?

Thanks in advance :)


Solution

  • Here the solution and why it didn't work:

    Why it didn't work?

    AbcPdf does NOT support theDoc.HtmlOptions.HttpAdditionalHeaders when using Gecko as rendering engine.

    Work arround

    Action that will generate PDF given an URL:

    public class PdfController : Controller
    {
        private readonly IPdfGenerator _pdfGenerator;
    
        public PdfController(IPdfGenerator pdfGenerator)
        {
            if (pdfGenerator == null)
                throw new ArgumentNullException();
            this._pdfGenerator = pdfGenerator;
        }
    
        public FileStreamResult Index(string url)
        {
            Response.AppendHeader("Content-Disposition", "attachment;filename=Download.Pdf");
            this._pdfGenerator.SetCookies(Request.Cookies);
            return new FileStreamResult(_pdfGenerator.HtmlToPdf(url.Uri, false, 1200), "application/pdf");
        }
    }
    

    PDF generation class:

        public class AbcPdfGenerator : IPdfGenerator
        {
            private static HttpCookieCollection _cookies = new HttpCookieCollection();
    
            public void SetCookies(HttpCookieCollection cookies)
            {
                lock (_cookies)
                {
                    _cookies = cookies;
                    System.Threading.Thread.MemoryBarrier();
                }
            }
    
            public HttpCookieCollection GetCookies()
            {
                return _cookies;
            }
    
            public MemoryStream HtmlToPdf(Uri pdfUrl, bool landscape, int screenResolutionWidth, bool enableCache)
            {
                var pdfStream = new MemoryStream();
                var theDoc = new Doc();
                if (landscape)
                {
                    // apply a rotation transform
                    double w = theDoc.MediaBox.Width;
                    double h = theDoc.MediaBox.Height;
                    double l = theDoc.MediaBox.Left;
                    double b = theDoc.MediaBox.Bottom;
                    theDoc.Transform.Rotate(90, l, b);
                    theDoc.Transform.Translate(w, 0);
                    // rotate our rectangle
                    theDoc.Rect.Width = h;
                    theDoc.Rect.Height = w;
                }
                theDoc.HtmlOptions.Engine = EngineType.Gecko;
                theDoc.HtmlOptions.Timeout = 60000;
                theDoc.HtmlOptions.UseScript = true;
                theDoc.HtmlOptions.OnLoadScript = "(function(){window.ABCpdf_go = false; setTimeout(function(){window.ABCpdf_go = true;}, 3000);})();";
                var url = Uri.UnescapeDataString(pdfUrl.ToString());
    
                var pageRef = theDoc.AddImageUrl(url, true, screenResolutionWidth, enableCache);
                while (theDoc.Chainable(pageRef))
                {
                    theDoc.Page = theDoc.AddPage();
                    pageRef = theDoc.AddImageToChain(pageRef);
                }
                for (var i = 1; i <= theDoc.PageCount; i++)
                {
                    theDoc.PageNumber = i;
                    theDoc.Flatten();
                }
    
                theDoc.Save(pdfStream);
                theDoc.Clear();
                var byteInfo = pdfStream.ToArray();
                pdfStream.Write(byteInfo, 0, byteInfo.Length);
                pdfStream.Position = 0;
                return pdfStream;
            }
    
            /// <summary>
            /// Will install the pdf software license on the server, this should be called on a Application_Start()
            /// </summary>
            public void InitializeService()
            {
                XSettings.InstallLicense(System.Configuration.ConfigurationManager.AppSettings["PdfGeneratorLicenseKey"]);
                //XSettings.InstallTrialLicense(System.Configuration.ConfigurationManager.AppSettings["PdfGeneratorLicenseKey"]);
            }
        }
    

    Action that return HTML to be converted to PDF:

    [RequireCookiesForPdf]
    public ActionResult Index(string layout)
    {
        return View();
    }
    

    The action filter used before:

    public class RequireCookiesForPdf : ActionFilterAttribute
    {
        private readonly IPdfGenerator _pdfGenerator;
    
        public RequireCookiesForPdf()
        {
            //"Bastard injection" :( as action filters don't allow constructor injection
            this._pdfGenerator = DependencyResolver.Current.GetService<IPdfGenerator>();
        }
    
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var cookies = this._pdfGenerator.GetCookies();
            if (cookies != null)
            {
                foreach (var key in cookies.AllKeys)
                {
                    var httpCookie = cookies[key];
                    if (httpCookie != null) filterContext.HttpContext.Response.Cookies.Set(new HttpCookie(key, httpCookie.Value));
                }
                this._pdfGenerator.SetCookies(new HttpCookieCollection());
            }
            base.OnActionExecuting(filterContext);
        }
    }