http最高

先日Fedora Core 2 LinuxのDVDイメージをダウンロードしようとして,そのファイルサイズが32bitを越えていることに気付いた.
例えば以下はKDDI研究所のミラーサイトであるが,HTTPでこのファイルにアクセスしたときの挙動が面白い.
http://ftp.kddilabs.jp/Linux/packages/fedora/core/2/i386/iso/
ftp://ftp.kddilabs.jp/Linux/packages/fedora/core/2/i386/iso/

HTTP/1.1 301 Moved Permanently
Date: Wed, 26 May 2004 16:12:01 GMT
Server: Apache
Location: ftp://ftp.kddilabs.jp/Linux/packages/fedora/core/2/i386/iso/FC2-i386-DVD.iso

現状httpサーバ/クライアントともに(4GB)2GBを越えるファイルを扱えるものは意外と少ない.それを考えるとこのリダイレクトは妥当と言えるかもしれないが,Internet Explorer 6.0はFTPでもファイルサイズの下位32bitしか見ないようで結局ダウンロードに失敗する*1.世の中なかなかうまく行かないものだ.ちなみにFFFTPではダウンロードに成功している.
なお,HTTPによるダウンロードに関していくらか実験してみた結果が以下である.

System.Net.WebClient (.NET Framework 1.1)
"Content-Length"を下位32bitしか見ない.負値になったら即切断.
Internet Explorer 6.0
"Content-Length"の下位32bitしか見ない.負値になったら即切断.
Irvine 1.1.1
ダウンロードサイズが2GBを越えてから表示が負値になる.4GBを越えると表示が-1になる.ダウンロードが終わる(向こうから切断される)とダウンロードが失敗したと解釈される(そしてデフォルトでは再びダウンロードを開始する).
Mozilla 1.8a1
ダウンロードサイズが2GBを越えてから表示が負値になる.4GBを越えたあたりでダウンロードが停止する.

4GBを越えるデータを扱えるhttpdサーバだが,探すのが面倒なのでC#でごく簡単なものを作ってみた.以下,http的には無茶苦茶だがとりあえず山のような"!"を返すプログラムである.
>

using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Globalization;
using System.Text;

static void Server()
{
    IPAddress addr = IPAddress.Loopback;
    System.Net.Sockets.TcpListener serv = new System.Net.Sockets.TcpListener( addr, 80 );
    serv.Start();
    while(true)
    {
        using( TcpClient client = serv.AcceptTcpClient() )
        {
            using( NetworkStream ns = client.GetStream() )
            {
                try
                {
                    StreamReader sr = new StreamReader( ns );

                    // 全体のサイズ
                    long dataSize = 0x110000000;

                    // 一度に出力するデータのサイズ
                    int dataBlock = 4*1024*1024;

                    while(true)
                    {
                        string line = sr.ReadLine();
                        if( line == "" )
                        {
                            break;
                        }
                    }

                    DateTimeFormatInfo dtf = CultureInfo.InvariantCulture.DateTimeFormat;
                    string msg = string.Format(
                        "HTTP/1.1 200 OK\r\n" + 
                        "Date: {0}\r\n" + 
                        "Server: TestServer\r\n" + 
                        "Content-Type: text/plain\r\n" +
                        "Content-Length: {1}\r\n" +
                        "\r\n",
                        DateTime.Now.ToString(dtf.RFC1123Pattern, CultureInfo.InvariantCulture),
                        dataSize );

                    byte[] data = Encoding.ASCII.GetBytes( new string('!',dataBlock) );

                    StreamWriter sw = new StreamWriter( ns );
                    sw.Write( msg );
                    sw.Flush();
                    forlong i = 0; i < dataSize / dataBlock; ++i )
                    {
                        ns.Write( data, 0, data.Length );
                    }
                }
                catch(Exception)
                {
                }
            }
        }
    }
}

*1:最初の200MBちょっとしかダウンロードしない