.NET Framework 3.5 SP1 – HttpWebRequest – problém s hlavičkou Range

Pravidelnejší čitatelia tohto blogu vedia, že sa už dlhší čas venujem vývoju programu s názvom Mar3ek’s Download Manager. A dnes sa mi pri príprave jeho nasledujúcej verzie podarilo objaviť celkom závažný nedostatok v MS .NET Framework-u.

Včera som si chcel s použitím MDM stiahnuť jeden dosť veľký súbor (cca 6,6 GB). Všetko šlapalo ako hodinky, no keď som mal hotových zhruba 40%, potreboval som sťahovanie prerušiť a stiahnuť jeden menší súbor. Tak som si povedal “prečo nie, veď RapidShare podporuje nadväzovanie prerušených sťahovaní”. Aké veľké bolo moje prekvapenie, keď som sa pokúsil sťahovanie znovu naštartovať a stalo sa len to, že sa sťahovanie prehlásilo za úspešne dokončené.

Po nejakej tej chvíli pátrania v útrobách môjho programu sa mi podarilo zistiť, že mi server pri pokuse o spustenie sťahovania vracia chybu “Requested range not satisfiable”, teda že stanovený rozsah nie je možné spracovať. Tu by asi bolo vhodné, aby čitateľ vedel, čo sú to HTTP hlavičky – zjednodušene povedané ide o kúsky údajov, ktoré serveru upresnia, čo má s požiadavkou urobiť.

Pri reštartovaní čiastočne načatého sťahovania sa používa HTTP hlavička Range, ktorá serveru povie, že má posielať dáta až od pozície špecifikovanej v hlavičke. Zjednodušene: Ak pošlem hlavičku “Range: 1000”, dostanem od serveru len tú časť súboru, ktorá začína tisícim bytom.

A práve v tomto leží problém .NET Framework-u 3.5 SP1. Trieda HttpWebRequest, ktorá sa používa na posielanie HTTP požiadaviek, má možnosť nastaviť hlavičku Range len pomocou metódy AddRange, ktorá má jediný argument typu Int32 – teda 32-bitové číslo so znamienkom. A maximálna hodnota typu int je 2 147 483 647. Teda aj maximálna hodnota, ktorú môžeme vložiť do hlavičky Range je 2 147 483 647, čo zodpovedá približne pozícii 2 GB v súbore.

Z toho samozrejeme vyplýva, že ak prerušíte sťahovanie po stiahnutí viac ako 2 GB dát, sťahovanie sa nebude dať bežným spôsobom nadviazať. A to je v dnešnej dobe veľkých súborov a výkonných file hostingov celkom problém.

Našťasie existujú dneď dve riešenia. Prvé z nich je previesť projekt na .NET Framework 4.0, ktorý už podporuje metódu HttpWebRequest.AddRange(Int64). Dokáže tak do hlavičky Range uložiť až 64-bitové číslo so znamienkom, ktoré ma maximálnu hodnotu 9 223 372 036 854 775 807, čo je približne 8 exabytov (1 exabyte = 10246 bytov).

Pre tých, ktorí z nejakého dôvodu nechcú prestúpiť na .NET 4.0 (napríklad preto, že ešte nie je dostatočne rozšírený), existuje aj iné riešenie. Ide viac menej o workaround/hack a nie je veľmi pekný – využíva .NET reflection a volanie členských funkcií deklarovaných ako protected. Ale funguje to.

private static void SetLongRange(long rangeValue, HttpWebRequest request)
{
	var method = typeof(WebHeaderCollection).GetMethod(
						"AddWithoutValidate",	
						BindingFlags.Instance | BindingFlags.NonPublic);

	var val = string.Format("bytes={0}", rangeValue);
	method.Invoke(request.Headers, new object[] { "Range", val });
}

Ide o mierne upravený kód, ktorý vytvoril užívateľ Mutant_Fruit na CodeGuru.com. Týmto mu ďakujem.

S pomocou tohto kódu je možné znovu nadväzovať prakticky ľubovoľné sťahovanie a MDM tak opäť pokročil na ceste k výkonnému a spoľahlivému správcovi sťahovania.

Bližšie informácie o programe Mar3ek’s Download Manager nájdete na samostatnej stránke.

Reklamy

Pridaj komentár

Zadajte svoje údaje, alebo kliknite na ikonu pre prihlásenie:

WordPress.com Logo

Na komentovanie používate váš WordPress.com účet. Odhlásiť sa / Zmeniť )

Twitter picture

Na komentovanie používate váš Twitter účet. Odhlásiť sa / Zmeniť )

Facebook photo

Na komentovanie používate váš Facebook účet. Odhlásiť sa / Zmeniť )

Google+ photo

Na komentovanie používate váš Google+ účet. Odhlásiť sa / Zmeniť )

Connecting to %s