Byte Range Request Detection in .NET HttpHandler

I have an HttpHandler that will perform some checks on incoming requests and in some cases perform some function. One of the conditions that need to be checked is a request for a request for a byte range . How it's done?

+3
source share
3 answers

You need to find the title Rangein the object Requestthat the part is HttpContextpassed to your method ProcessRequest. There is HttpRequestno property in the class Range, so you need to look in Headers. If there is Range, it will look like:

Range: bytes=<start>-<end>

<start> <end> - . , - 64K :

Range: bytes=32768-98304

.

+4

, , "0-500, 100-1500" ( ) "-500" ( 500 ). . RFC 2616 gory, .

+3

, @brent-keller, , , CodePlex - . FDM ( ). MultiRange (). Web.config.

CodePlex - Accept-Ranges bytes, , . Content-Range. - , , , .

. , URL-, , . / .

, - .

HTTP

Public Class Upload
  Implements IHttpHandler

  Public Sub ProcessRequest(Context As HttpContext) Implements IHttpHandler.ProcessRequest
    Dim oFile As FileInfo

    oFile = New FileInfo(Context.Server.MapPath("~/0HCJ0LE.zip"))

    Me.UploadRange(Context, oFile)
  End Sub



  Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
    Get
      Return False
    End Get
  End Property



  Private Sub UploadRange(Context As HttpContext, File As FileInfo)
    Dim oResponse As Response
    Dim oRequest As Request

    Dim _
      nOffset,
      nLength As Long

    Using oReader As New StreamReader(File.FullName)
      Context.Response.AddHeader("Accept-Ranges", "bytes")

      oResponse = New Response(oReader)
      oRequest = New Request(oResponse, Context)

      If oRequest.HasRange Then
        If oRequest.IsMultiRange Then
          ' At the moment we only support single ranges.'
          '         * Multiple range support requires some more work'
          '         * to comply with the specifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2'
          '         *'
          '         * Multirange content must be sent with multipart/byteranges mediatype,'
          '         * (mediatype = mimetype)'
          '         * as well as a boundary header to indicate the various chunks of data.'
          ' '        
          ' (?) Shoud this be issued here, or should the first'
          ' range be used? Or should the header be ignored and'
          ' we output the whole content?'
          Me.ThrowBadRange(Context, oResponse)
        Else
          If oRequest.IsBadRange Then
            Me.ThrowBadRange(Context, oResponse)
          Else
            Context.Response.StatusCode = 206

            oResponse.Start = oRequest.Start
            oResponse.End = oRequest.End

            nOffset = oReader.BaseStream.Seek(oResponse.Start, SeekOrigin.Begin)
            nLength = oResponse.End - oResponse.Start + 1
          End If
        End If
      Else
        nOffset = 0
        nLength = oResponse.Size
      End If
    End Using

    Context.Response.ContentType = MediaTypeNames.Application.Zip
    Context.Response.AddHeader("Content-Disposition", $"attachment; filename={File.Name}")
    Context.Response.AddHeader("Content-Length", nLength)
    Context.Response.AddHeader(oResponse.HeaderName, oResponse.HeaderValue)
    Context.Response.WriteFile(File.FullName, nOffset, nLength)
    Context.Response.End()
  End Sub



  Private Sub ThrowBadRange(Context As HttpContext, Response As Response)
    Context.Response.AddHeader(Response.HeaderName, Response.HeaderValue)
    Throw New HttpException(416, "Requested range not satisfiable")
  End Sub
End Class

Friend NotInheritable Class Request
  Public Sub New(Response As Response, Context As HttpContext)
    Me.Response = Response
    Me.Context = Context
  End Sub



  Public ReadOnly Property Start As Long
    Get
      If Me.Range(0) = String.Empty Then
        Start = Me.Response.Size - Me.Range(1)
      Else
        Start = Me.Range(0)
      End If
    End Get
  End Property



  Public ReadOnly Property [End] As Long
    Get
      If Me.Range(0) = String.Empty Then
        [End] = Me.Response.End
      Else
        If Long.TryParse(Me.Range(1), 0) Then
          [End] = Me.Range(1)
        Else
          [End] = Me.Response.Size
        End If
      End If

      [End] = Math.Min(Me.Response.End, [End])
    End Get
  End Property



  Public ReadOnly Property HasRange As Boolean
    Get
      Return String.IsNullOrEmpty(Me.Context.Request.ServerVariables(HTTP_RANGE)) = False
    End Get
  End Property



  Public ReadOnly Property IsMultiRange As Boolean
    Get
      Return Me.Context.Request.ServerVariables(HTTP_RANGE).Contains(",")
    End Get
  End Property



  Public ReadOnly Property IsBadRange As Boolean
    Get
      Return Me.Start > Me.End OrElse Me.Start > Me.Response.Size - 1 OrElse Me.End >= Me.Response.Size
    End Get
  End Property



  Private ReadOnly Property Range As List(Of String)
    Get
      Return Me.Context.Request.ServerVariables(HTTP_RANGE).Split("=")(1).Split("-").ToList
    End Get
  End Property



  Private ReadOnly Response As Response
  Private ReadOnly Context As HttpContext

  Private Const HTTP_RANGE As String = "HTTP_RANGE"
End Class

Friend NotInheritable Class Response
  Public Sub New(Reader As StreamReader)
    _Size = Reader.BaseStream.Length
    Me.End = Me.Size - 1
  End Sub



  Public Property Start As Long
  Public Property [End] As Long
  Public ReadOnly Property Size As Long



  Public ReadOnly Property HeaderName As String
    Get
      Return "Content-Range"
    End Get
  End Property



  Public ReadOnly Property HeaderValue() As String
    Get
      Return $"bytes {Me.Start}-{Me.End}/{Me.Size}"
    End Get
  End Property
End Class
0
source

Source: https://habr.com/ru/post/1778007/


All Articles