Come impedire l'eccezione StackOverflow in HtmlAgilityPack per html molto brutto

c# html-agility-pack stack-overflow

Domanda

Sto utilizzando HtmlAgilityPack in un Web API MVC 5. 99,99% delle volte, non ci sono problemi ... i siti caricano e li analizzo per estrarre il testo che voglio. La mia API potrebbe essere colpita diverse centinaia di volte al giorno senza problemi. Ha gestito felicemente oltre 2 milioni di contatti in 24 ore nel passato ...

Occasionalmente, tuttavia, siti Web terribilmente formati causano una risposta di errore 500. Quindi tutte le richieste successive ottengono 500 errori e il sito diventa completamente inutilizzabile. L'unica soluzione in questo scenario è riavviare l'applicazione Web. Il sito è ospitato su Windows Azure. Ho usato le istanze Large con bilanciamento del carico e una volta che la CPU ha dei picchi rimane alta. In passato, questo ha funzionato bene su una singola istanza di Medium Azure (2 core / 3,5 GB di RAM)

L'errore è uno StackOverflow ... che so che non riesco a cogliere.

Nota che questo codice NON causa il crash di un'applicazione Console

HtmlWeb web = new HtmlWeb();
HtmlDocument doc = web.Load("http://nursingandmidwiferycareersni.com/");            
Console.Write(doc.DocumentNode.InnerText);

... ma sicuramente causerà il crash di un'app Web MVC.

Tuttavia, in una semplice applicazione Web MVC, posso riprodurre l'errore StackOverflow con un sito come http://nursingandmidwiferycareersni.com/ . Se metti http://nursingandmidwiferycareersni.com/ in https://validator.w3.org , riuscirai a ottenere un errore interno del server su validator.w3.org!

Farò una modifica al codice sorgente HAP se necessario per aggirare questo ... al momento sto usando solo il pacchetto Nuget.

È possibile evitare che lo stackoverflow si verifichi in HAP?
O c'è un modo per verificare la presenza di un html orribile e impedire che si verifichi l'arresto anomalo in primo luogo?

Risposta popolare

Dare qualcosa di simile a questo, dove il metodo ParseHtml e il tipo ParsedHtml sono solo segnaposto da riempire:

public async Task<ParsedHtml> TryParseHtml(
    string untrustedHtml,
    CancellationToken cancellationToken)
{
    var tcs = new TaskCompletionSource<ParsedHtml>();

    var thread = new Thread(() =>
    {
        ParsedHtml result = ParseHtml(untrustedHtml);
        tcs.TrySetResult(result);
    });
    thread.Start();

    using (cancellationToken.Register(() => tcs.TrySetCanceled()))
    {
        try
        {
            return await tcs.Task;
        }
        catch (OperationCanceledException)
        {
            thread.Abort();
            throw;
        }
    }
}

L'idea potrebbe essere estesa per essere più efficiente riutilizzando i thread nel caso di successo, piuttosto che accendere e abbattere un thread per ogni pagina HTML.



Related

Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché