Download a file Via Http

Here’s the Class.


Imports System 
Imports System.Net 
Imports System.Net.Sockets 
Imports System.Text 
Imports System.IO 
Imports System.Security.Cryptography 
Imports Microsoft.VisualBasic 

'/// <summary>Implements a class that can download file over an HTTP connection.</summary> 
Public Class HttpDownloader 
Inherits FileDownloader 
    '/// <summary>Constructs a new HttpDownloader object.</summary> 
    Public Sub New() 
        Port = 80 
    End Sub 
    '/// <summary>Starts downloading from a URL.</summary> 
    '/// <exceptions cref="DownloadException">Thrown when there was an error while downloading the file.</exceptions> 
    Public Overrides Sub FetchFile() 
        If IsConnected Then Throw New DownloadException("This object is already downloading something.") 
        Dim Buffer(1023) as Byte 
        Dim Ret as Integer 
        'Initialize the class 
        HeaderExtracted = False 
        HeaderPart = "" 
        m_DownloadLength = -1 
        Moving = False 
        'Initialize the things we're going to use 
        ClearBuffer() 
        OpenFile() 
        'Connect to the server 
        Connect() 
        'Send the HTTP query 
        DownloadSocket.Send(Encoding.ASCII.GetBytes(GetQueryString)) 
        'Receive the reply 
        Ret = DownloadSocket.Receive(Buffer) 
        While Ret <> 0 
            If ExtractHeader(Buffer, Ret) Then 
                HandleBytes(Buffer, Ret) 
            End If 
            If ReceivedBytes = MaxDownload Then Exit While 
            Ret = DownloadSocket.Receive(Buffer) 
        End While 
        CloseFile() 
        'Disconnect from the server 
        Disconnect() 
    End Sub 
    '/// <summary>Starts downloading from the specified URL.</summary> 
    '/// <remarks> 
    '/// The following type of input will be accepted: 
    '/// http://www.server.com/dir/file.ext 
    '/// http://www.server.com:1234/dir/file.ext 
    '/// www.server.com/dir/file.ext 
    '/// </remarks> 
    '/// <param name="File">The URL to download.</param> 
    '/// <exceptions cref="ArgumentException">Thrown when the specified URL is invalid.</exceptions> 
    '/// <exceptions cref="DownloadException">Thrown when there was an error while downloading the file.</exceptions> 
    Public Overrides Sub FetchFileString(File as String) 
        ParseURL(File) 
        FetchFile() 
    End Sub 
    '/// <summary>Parses a HTTP URL.</summary> 
    '/// <param name="URL">The URL to parse.</param> 
    '/// <exceptions cref="ArgumentException">Thrown when the specified URL is invalid.</exceptions> 
    Private Sub ParseURL(URL as String) 
        URL = URL.Trim 
        Dim Protocol as Integer = URL.IndexOf("://") 
        Dim StartHost as Integer, EndHost as Integer 
        If Protocol > 0 Then 
            If Not URL.Substring(0, 7).ToLower.Equals("http://") Then 
                Throw New ArgumentException() 
            End If 
            StartHost = 7 
        Else 
            StartHost = 0 
        End If 
        EndHost = URL.IndexOf("/", StartHost) 
        Dim UrlHost as String = URL.Substring(StartHost, EndHost - StartHost) 
        Protocol = UrlHost.IndexOf(":") 
        If Protocol > 0 Then 
            Host = UrlHost.SubString(0, Protocol) 
            Port = Integer.Parse(UrlHost.SubString(Protocol + 1)) 
        Else 
            Host = UrlHost 
            Port = 80 
        End If 
        RequestedFile = URL 
    End Sub 
    '/// <summary>Extracts a HTTP header from a HTTP stream.</summary> 
    '/// <param name="Buffer">The HTTP stream to process.</param> 
    '/// <param name="Bytes">The number of bytes in the stream.</param> 
    '/// <returns>Returns True if the header was successfully extracted, False otherwise.</returns> 
    Private Function ExtractHeader(ByRef Buffer() as Byte, ByRef Bytes as Integer) as Boolean 
        If HeaderExtracted Then Return True 
        Dim Header as String = HeaderPart & Encoding.ASCII.GetString(Buffer, 0, Bytes) 
        Dim Ret as Integer = Header.IndexOf(ControlChars.CrLf & ControlChars.CrLf) - HeaderPart.Length 
        HeaderPart = Header 
        If Ret <> -1 Then 
            Bytes = Bytes - Ret - 4 
            Array.Copy(Buffer, Ret + 4, Buffer, 0, Bytes) 
            HeaderExtracted = True 
            Return ProcessHeader(Header.Substring(0, Ret)) 
        End If 
        Return False 
    End Function 
    '/// <summary>Returns the query string that will be sent to the remote host.</summary> 
    '/// <returns>Returns the query string that will be sent to the remote host.</returns> 
    Private Function GetQueryString() as String 
        Dim QueryString as String = "GET " & RequestedFile & " HTTP/1.0" & ControlChars.CrLf 
        QueryString &= "Accept: */*" & ControlChars.CrLf & "User-Agent: AllAPI.net Download Class" & ControlChars.CrLf & "Host: " & Host & ControlChars.CrLf 
        'Check for resume 
        If UseResume And ResumeFrom > 0 Then 
            QueryString &= "Range: bytes=" & ResumeFrom.ToString & "-" & ControlChars.CrLf 
        End If 
                'Check for HTTP Authentication 
                   If Username <> "" Then 
            QueryString &= "Authorization: Basic " & Convert.ToBase64String(Encoding.ASCII.GetBytes(Username & ":" & Password)) & ControlChars.CrLf 
                   End If 
                   'Check whether or not to allow cache 
                   If Not AllowCache Then 
            QueryString &= "Pragma: no-cache" & ControlChars.CrLf & "Cache-Control: no-cache" & vbCrLf 
                   End If 
                   'Send the query 
        QueryString &= ControlChars.CrLf 
        Return QueryString 
    End Function 
    '/// <summary>Processes a HTTP header.</summary> 
    '/// <param name="Header">The HTTP header to process.</param> 
    '/// <returns>Returns True if the header has been successfully processed, False otherwise.</returns> 
    '/// <exceptions cref="DownloadException">Thrown when the header is invalid.</exceptions> 
    Private Function ProcessHeader(Header as String) as Boolean 
        Dim Lines() as String = Header.Split(New Char() {ControlChars.Cr, ControlChars.Lf}) 
        Dim Cnt as Integer, ColumnPos as Integer 
        Dim NewLocation as String 
        For Cnt = 1 to Lines.Length - 1 
            Lines(Cnt) = Lines(Cnt).Trim 
            ColumnPos = Lines(Cnt).IndexOf(":") 
            If ColumnPos > 0 Then 
                Select Case Lines(Cnt).Substring(0, ColumnPos).ToLower 
                    Case "content-length" 'Content-Length: 12345 
                        m_DownloadLength = Integer.Parse(Lines(Cnt).Substring(ColumnPos + 1)) 
                    Case "location" 'Location: www.newplace.com 
                        NewLocation = Lines(Cnt).Substring(ColumnPos + 1).Trim 
                End Select 
            End If 
        Next Cnt 
        ' Process the first header line 
        Dim ServerReply as Integer 
        Try 
            Lines = Lines(0).Split(" "c) 
            ServerReply = Integer.Parse(Lines(1)) 
        Catch 
            Throw New DownloadException("Unknown server reply.") 
        End Try 
        Select Case ServerReply 
            Case 206 ' "Partial data" reply 
                If Not UseResume Then 
                    Throw New DownloadException("Server did not return the entire file.") 
                End If 
            Case 200 ' "OK" reply 
                If UseResume And ResumeFrom > 0 Then 
                    Throw New DownloadException("Server cannot resume.") 
                End If 
            Case 301, 302 ' "Moved to" reply 
                If Not AutoRedirect Then 
                    Throw New DownloadException("File moved.") 
                End If 
                If NewLocation = "" Then 
                    Throw New DownloadException("Invalid new location.") 
                End If 
                Disconnect(False) 
                HeaderExtracted = False 
                HeaderPart = "" 
                ParseURL(NewLocation) 
                Connect(False) 
                DownloadSocket.Send(Encoding.ASCII.GetBytes(GetQueryString)) 
                Return False 
            Case Else 
                Throw New DownloadException("Unknown server reply.") 
        End Select 
        Return True 
    End Function 
    '/// <summary>Gets or sets a value that specifies whether to allow cached data on proxies or not.</summary> 
    '/// <value>A value that specifies whether to allow cached data on proxies or not.</value> 
    Public Property AllowCache() as Boolean 
        Get 
            Return m_AllowCache 
        End Get 
        Set(Value as Boolean) 
            m_AllowCache = Value 
        End Set 
    End Property 
    '/// <summary>Gets or sets a value that specifies whether to auto-redirect when a URI has moved.</summary> 
    '/// <remarks>If this value is set to False and the URI has moved, a DownloadException will be thrown when calling the FetchFile method.</remarks> 
    '/// <value>A value that specifies whether to auto-redirect when a URI has moved.</value> 
    Public Property AutoRedirect() as Boolean 
        Get 
            Return m_AutoRedirect 
        End Get 
        Set(Value as Boolean) 
            m_AutoRedirect = Value 
        End Set 
    End Property 
    '/// <summary>Gets or sets a value that specifies whether the header has already been extracted from the data or not.</summary> 
    '/// <value>A value that specifies whether the header has already been extracted from the data or not.</value> 
    Private Property HeaderExtracted() as Boolean 
        Get 
            Return m_HeaderExtracted 
        End Get 
        Set(Value as Boolean) 
            m_HeaderExtracted = Value 
        End Set 
    End Property 
    '/// <summary>Gets or sets a string that contains (parts of) the HTTP header.</summary> 
    '/// <value>A string that contains (parts of) the HTTP header.</value> 
    Private Property HeaderPart() as String 
        Get 
            Return m_HeaderPart 
        End Get 
        Set(Value as String) 
            m_HeaderPart = Value 
        End Set 
    End Property 
    '/// <summary>Gets or sets a value that specifies whether the object is currently moving to another URL.</summary> 
    '/// <value>A value that specifies whether the object is currently moving to another URL.</value> 
    Private Property Moving() as Boolean 
        Get 
            Return m_Moving 
        End Get 
        Set(Value as Boolean) 
            m_Moving = Value 
        End Set 
    End Property 
    'Private variables 
    Private m_AllowCache as Boolean = False 
    Private m_AutoRedirect as Boolean = True 
    Private m_HeaderExtracted as Boolean 
    Private m_HeaderPart as String = "" 
    Private m_Moving as Boolean = False 
End Class