Quantcast
Channel: Common Language Runtime Internals and Architecture forum
Viewing all articles
Browse latest Browse all 1710

VB.NET WebDAV standalone server implementation

$
0
0

Hello.

    

I'm currently developing a WebDAV server in VB.NET. The server I'm trying to build should be standalone, so no IIS, nor Apache,...
I'm using the HttpListener class to wait for HTTP/WebDAV requests.
The server will be used to map files/folders that are contained in a SQL Server DB.

I tried to map a simple local folder to the Windows Explorer (as a network drive) with IIS, and with Apache, and it works "out of the box". But when I try to map my own WebDAV server to the Windows Explorer as a network drive, Windows says it's not a valid folder...

My server receives 3 requests from the Windows Explorer : OPTIONS, PROPFIND, and again PROPFIND.
The server sends a response to all these requests, and if I snif the network traffic with RawCap, I can't see anything wrong in the XML response, nor in the HTTP headers...
I also sniffed the traffic when I tried with IIS or Apache, and there were a few differences, which I tried to correct, but nothing made a difference, the server is still not working as a network drive in Windows Explorer...

I also compared my code to other open-source WebDAV servers, but I can't figure out why it is not working...

I also tried to take the XML sent as a response from IIS or Apache, and to send it with my server, without success.

So, are there any specific things to implement to make it work with the Windows Explorer ?
Maybe I forgot a little thing that's needed, maybe someone can help me out...

The main code :

Module WebServer

    #Region "Main"
        Sub Main()
            Dim prefixes(0) As String
            prefixes(0) = "http://*:80/"
            ProcessRequests(prefixes)
        End Sub
    #End Region

    #Region "Request processing"

        Private Sub HandleRequest(context As HttpListenerContext)
            Dim sw = Stopwatch.StartNew()
            Dim response As HttpListenerResponse = Nothing
            Dim requestHandler As IMethodHandler = Nothing


            Console.WriteLine(String.Format("{0:hh:mm:ss} >>> ", DateTime.Now) + context.Request.HttpMethod + " Request from : " + context.Request.UserAgent)
            Console.WriteLine("    KeepAlive: {0}", context.Request.KeepAlive)
            Console.WriteLine("    Local end point: {0}", context.Request.LocalEndPoint.ToString())
            Console.WriteLine("    Remote end point: {0}", context.Request.RemoteEndPoint.ToString())
            Console.WriteLine("    Is local? {0}", context.Request.IsLocal)
            Console.WriteLine("    HTTP method: {0}", context.Request.HttpMethod)
            Console.WriteLine("    Protocol version: {0}", context.Request.ProtocolVersion)
            Console.WriteLine("    Is authenticated: {0}", context.Request.IsAuthenticated)
            Console.WriteLine("    Is secure: {0}", context.Request.IsSecureConnection)

            ' Create the response
            response = context.Response

            response.ContentType = "text/xml"
            response.ContentEncoding = System.Text.Encoding.UTF8

            ' User authentication
            'Dim identity As HttpListenerBasicIdentity = context.User.Identity

            'If Not identity.Name.Equals("test") Or Not identity.Password.Equals("test") Then
            'response.StatusCode = HttpStatusCode.Unauthorized
            'response.AddHeader("WWW-Authenticate", "Basic realm=""Server""")

            'Else
            Select Case context.Request.HttpMethod.ToUpper()
                Case "OPTIONS"
                    requestHandler = New OPTIONS_Handler(context)
                Case "GET"
                    requestHandler = New GET_Handler(context)
                Case "HEAD"
                    requestHandler = New HEAD_Handler(context)
                Case "PUT"
                    requestHandler = New PUT_Handler(context)
                Case "POST"
                    requestHandler = New POST_Handler(context)
                Case "DELETE"
                    requestHandler = New DELETE_Handler(context)
                Case "COPY"
                    requestHandler = New COPY_Handler(context)
                Case "MOVE"
                    requestHandler = New MOVE_Handler(context)
                Case "MKCOL"
                    requestHandler = New MKCOL_Handler(context)
                Case "PROPFIND"
                    requestHandler = New PROPFIND_Handler(context)
                Case "PROPPATCH"
                    requestHandler = New PROPPATCH_Handler(context)
                Case Else
                    Console.WriteLine("Unknown Command")
                    response.StatusCode = HttpStatusCode.NotImplemented
            End Select

            If Not requestHandler Is Nothing Then
                requestHandler.processRequest()
            End If

            If response IsNot Nothing Then
                response.Close()
            End If
            Console.WriteLine("Time : {0}", sw.Elapsed)
            'End If
        End Sub

        Private Sub ProcessRequests(ByVal prefixes() As String)
            If Not System.Net.HttpListener.IsSupported Then
                Console.WriteLine( _
                    "Windows XP SP2, Server 2003, or higher is required to " & _"use the HttpListener class.")
                Exit Sub
            End If

            ' URI prefixes are required
            If prefixes Is Nothing OrElse prefixes.Length = 0 Then
                Throw New ArgumentException("prefixes")
            End If

            ' Create a listener and add the prefixes
            Dim listener As System.Net.HttpListener = New System.Net.HttpListener()
            For Each s As String In prefixes
                listener.Prefixes.Add(s)
            Next

            Try
                ' Start the listener to begin listening for requests and set authentication
                'listener.AuthenticationSchemes = AuthenticationSchemes.Basic
                listener.Start()
                Console.WriteLine("Listening...")

                While True
                   Try
                        ' Note: GetContext blocks while waiting for a request
                        Dim context As HttpListenerContext = listener.GetContext()
                        Dim t As New Threading.Thread(Sub() HandleRequest(context))
                        t.Start()

                    Catch ex As HttpListenerException
                        Console.WriteLine(ex.Message)
                    End Try
                End While

            Catch ex As HttpListenerException
                Console.WriteLine(ex.Message)
            Finally
                ' Stop listening for requests
                listener.Close()
                Console.WriteLine("Done Listening...")
            End Try
        End Sub
    #End Region

    End Module


The OPTIONS handler :

Public Class OPTIONS_Handler : Implements IMethodHandler

        Private context As HttpListenerContext
        Private response As HttpListenerResponse

        Public Sub New(ByVal ctxt As HttpListenerContext)
            context = ctxt
            response = context.Response
        End Sub

        Public Sub processRequest() Implements IMethodHandler.processRequest
            response.AppendHeader("Allow", "OPTIONS, GET, HEAD, POST, PUT, DELETE, COPY, MOVE, MKCOL, PROPFIND, PROPPATCH")
            response.AppendHeader("Public", "OPTIONS, GET, HEAD, POST, PUT, DELETE, COPY, MOVE, MKCOL, PROPFIND, PROPPATCH")
            response.AppendHeader("DAV", "1, 2, ordered-collections")
            response.AppendHeader("Versioning-Support", "DAV:basicversioning")
            response.AppendHeader("MS-Author-Via", "DAV")
            'response.AppendHeader("X_MSDAVEXT", "1")
            'response.AppendHeader("Translate", "f")

            response.StatusCode = HttpStatusCode.OK
        End Sub
    End Class


And the PROPFIND handler :

Public Class PROPFIND_Handler : Implements IMethodHandler

        Private context As HttpListenerContext
        Private response As HttpListenerResponse

        Public Sub New(ByVal ctxt As HttpListenerContext)
            context = ctxt
            response = context.Response
        End Sub

        Public Sub processRequest() Implements IMethodHandler.processRequest
            Try
                context.Response.SendChunked = False

                ' Check if the XML request is valid and well-formed
                Dim request As HttpListenerRequest = context.Request
                Dim reader As XmlReader = XmlReader.Create(request.InputStream)

                ' See if the inputstream includes some data. If not --> Exception
                Dim buffer(16 * 1024) As Byte
                Dim read As Integer
                Dim memstr As New MemoryStream

                While (read = request.InputStream.Read(buffer, 0, buffer.Length)) > 0
                    memstr.Write(buffer, 0, read)
                End While

                If memstr.Length <> 0 Then
                    Dim doc As XDocument = XDocument.Load(reader)
                End If

                response.StatusCode = 207
                Dim xmlWriter As XmlTextWriter = New XmlTextWriter("test.xml", New System.Text.UTF8Encoding(False))
                'xmlWriter.Formatting = Formatting.Indented

                xmlWriter.WriteStartDocument()
                xmlWriter.WriteStartElement("D:multistatus")
                xmlWriter.WriteAttributeString("xmlns:D", "DAV:")
                xmlWriter.WriteAttributeString("xmlns:b", "urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882")

                ' Get the requested URI
                Dim requestedURI As String = context.Request.Url.LocalPath

                ' Get the files and folders list from the mapped folder
                Dim mappedFolderInfo
                If requestedURI.Equals("/") Then
                    mappedFolderInfo = New IO.DirectoryInfo("MappedFolder")
                Else
                    mappedFolderInfo = New IO.DirectoryInfo("MappedFolder" + requestedURI)
                End If

                Dim filesList As IO.FileInfo() = mappedFolderInfo.GetFiles()
                Dim foldersList As IO.DirectoryInfo() = mappedFolderInfo.GetDirectories()

                ' List all the files and folders and build the corresponding XML response
                Dim tempFile As IO.FileInfo
                Dim tempFolder As IO.DirectoryInfo
                For Each tempFolder In foldersList
                    Dim webDavItem As New WebDAVItem()
                    webDavItem.CollectionYN = True
                    webDavItem.ContentLength = 0
                    webDavItem.CreationDate = tempFolder.CreationTime
                    webDavItem.LastModifDate = tempFolder.LastWriteTime
                    webDavItem.Name = tempFolder.Name + "/"
                    webDavItem.Path = requestedURI
                    webDavItem.BuildXmlResponse(xmlWriter)
                Next
                For Each tempFile In filesList
                    Dim webDavItem As New WebDAVItem()
                    webDavItem.CollectionYN = False
                    webDavItem.ContentLength = tempFile.Length
                    webDavItem.CreationDate = tempFile.CreationTime
                    webDavItem.LastModifDate = tempFile.LastWriteTime
                    webDavItem.Name = tempFile.Name
                    webDavItem.Path = requestedURI
                    webDavItem.BuildXmlResponse(xmlWriter)
                Next

                xmlWriter.Close()


                Dim f As FileStream = File.OpenRead("test.xml")
                Dim fileData(f.Length) As Byte

                response.ContentLength64 = f.Length
                f.Read(fileData, 0, f.Length)
                f.Close()

                response.OutputStream.Write(fileData, 0, response.ContentLength64)

            Catch ex As Exception
                Console.WriteLine("Exception while handling PROPFIND : " + ex.Message)
                response.StatusCode = HttpStatusCode.BadRequest
            End Try
        End Sub
    End Class


Thank you very much !

                                                                                                                                                                         

Viewing all articles
Browse latest Browse all 1710

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>