Tags

Custom WebDAV Server Using .NET

by Geoff Lane on February 2nd, 2006

As I discussed in a previous post "ASP.NET Web Application without ASPX Extension", I’ve been working on a custom WebDAV server for a client. The initial proof-of-concept was just to see if we could get some .NET code that would respond to all paths for a given web application. From there I had to make WebDAV work.

What is WebDAV

To recap: WebDAV is an IETF Standard Protocol. It is an extension to the ubiquitous HTTP standard, adding on request methods to get property information about files and folders, make folders (called collections in WebDAV parlance), change properties, lock files, etc. Basically anything that you would want on a distributed file system. WebDAV is natively supported by Windows 2000 and XP, Mac OS X, and Linux (using davfs). When using this native support, you’re able to interact with WebDAV just like you are using a local filesystem or a network share. There are also many standalone client implementations that act more like FTP clients.

Why Would You Write Your Own DAV Server?

Sharing files over a network is nothing new of course. Using Windows File Sharing, many people are used to using network shares to store files on a server and to share with their coworkers. The limitation of most of these file sharing systems is that they share exactly what is on the filesystem. What if you want to integrate file sharing with an application that stores file meta-data in a database? What if you want to programmatically control the files and folders that you show to your users? By building your own WebDAV server you can query a database to get all of the meta-data (and even file contents) to serve to your users.

How To Build a WebDAV Server

I did quite a bit of searching and found WebDAV server implementation in Python, Ruby, and Java, but could not find anything done in a .NET language which my client uses for all of their application infrastructure. So, I built one!

As I mentioned in my previous post, we can take over the complete HTTP request for all requested paths. I use a Command Pattern to handle the different types of WebDAV requests. The Commands are responsible for getting information from the HTTP Request that they need to fulfill the request and for writing the proper HTTP Response. Each of the Commands delegates the actual File manipulation to a set of provider classes. Those classes are accessed through an Abstract Factory which allows for me to easily change the backend implementation. This allows for a good separation of concerns. The WebDAV Service layer knows how to read and write WebDAV and the File Provider layer knows how to get and store information about files and folders. (The abstraction between the Commands and the File Providers is a bit leaky still, but I’m confident I’ll be able to refactor it into a cleaner separation.)

The first backend provider that I created for testing was a FileSystem provider. I’ve mapped the WebDAV server to a specific directory and use standard System.IO commands to manipulate files. This is a lot easier because it allowed me to focus on implementing the protocol without worrying about interacting with a less familiar data access layer.

Test Your Implementation

The WebDAV folks have built a great test suite called Litmus that you can use to validate your server implementation. It performs all of the basic operations on files and collections to validate that the server performs to the specification. Start with Litmus and you’ve got your entire functional test harness written for you. That is some TDD in action!
Litmus goes a long way to get you to protocol compliance, but it took a bit of cleaning and tweaking before Windows Explorer would happily mount the Web Folder.

In addition to Litmus I found a couple of other tools really handy for debugging:

  • Ethereal – a very nice protocol analyzer that will let you inspect a series of requests and responses.
  • DAVExplorer – a Java WebDAV client that will write detailed logs of requests and responses.

Don’t be afraid to write your own WebDAV server, it’s really fun!

References

WebDAV Reference Book:
WebDAV: Next-Generation Collaborative Web Authoring by Lisa Dusseault

From → .NET, Code, WebDAV

89 Comments
  1. RefuX permalink

    The only thing better than writing a WebDAV server is seeing the developer do his end-zone happy dance when he gets the darn thing to finally mount from Windows Explorer!

  2. Hi
    Can you give me some insight on sending the HTTPResponse to client for a WebDAV request ??

    Regards

  3. DMA permalink

    I do need to create webdav server using C#, which can return documents and their properties (from databse) . Can you please provide some initial pointers to start on this.

  4. Kavitas,
    I built the WebDAV server using a custom IHttpModule. As I described, it was mapped to handle all incoming requests. When you get a Request you will have access to the HttpApplication object that contains both the incoming HttpRequest object as well as an HttpResponse object.

    The HttpRequest object gives you access to all of the HTTP Header information. That header information will contain the kind of WebDAV request you are getting (PROPFIND, PUT, MOVE, COPY, GET). Using that information along with other information in the header such as the Path, the Depth, and possibly the Destination you have to fulfill that response.

    Basically there are a couple of different kinds of responses:

    1. PROPFIND for example responds with an XML document defining the resources available at a given path. Some basic headers are set (app.Request.Headers.Add(“Content-Type”, “text/xml”)) and then the XML is written to the Request.Ouptut stream.
    2. COPY and MOVE don’t write a response to the output stream, but generally just set HTTP Response Codes.
    3. GET Requests are just like a regular HTTP GET. The person is requesting the contents of a file. In that case you set the Response status code, set the Content-Type, and then just stream the contents of the file to the Response.Output stream.

    All of the specifics about the status codes and things are available at http://www.webdav.org. The Response status codes are really important for protocol compliance.

    Hope That Helps.

  5. DMA,
    I thought I did give some initial pointers, as that is exactly what I described building. I just provided some more information in my Response to Kavitas. Maybe that will help you out a bit?

    If you have specific questions, I can try to answer them.

    The best place to start is probably with the specs from http://www.webdav.org. It tells you basically everything you need to implement the protocol. The only tricky part for me was figuring out the IIS Mapping to get my code to respond to every incoming request. I describe that in ASP.NET Web Application Without ASPX Extension

  6. Hi Geoff

    I tried configuring IIS to handle all request to my website by the way you described in your article “ASP NET Web Application Without ASPX Extension” but as soon as I make the extensions to .* and try to open my website, it starts asking me for my username and password and on suppling my credentials also, it gives 401.1 error “You are not authorozed to view this page.” So please suggest how should i trap all my request

    Thanks

  7. Kavitas,
    Did you map an HttpModule that you created in your site’s Web.config?

    In your Web.config:

    
    <system.web>
        <httpModules>
            <add name="DavModule" type="Zorched.Dav.DavModule, DavModule"/>
        </httpModules>
    </system.web>
    

    Zorched.Dav.DavModule implements System.Web.IHttpModule. It’s the code that gets the incoming request and then writes the response. You could start with something simple by just writing some plain-text back to the browser.

    If you’ve already done that, then I would recommend looking at authentication and permissions. You shouldn’t be getting a login prompt unless you have authentication enabled or you’re trying to access something that your webserver doesn’t have access to. Unfortunately, authentication an IIS are not an easy subject, so I don’t know exactly what it could be.

  8. DMA permalink

    Geoff,

    Thanks a ton for initial pointers. I tried out on the basis of your response. Basically I derived a class from IHttpModule and registered in web.config. Using this I am able to trap all requests to my web site (including WebDav requests).

    But when writing to response for a WebDav request, the App.User object is returning me “”. Appreciate your help on this.

    - DMA

  9. DMA,

    Great to see you got it started!

    As for the User issue, I would imagine that you just don’t have any authentication going on. The User object is an IPrincipal. See the documentation of HttpApplication for details. Basically, you can’t create a log-in form, so you’re going to have to use Basic or Digest authentication. Depending on your needs, I guess the built-in IIS Authentication might work (although I don’t know if that will set the IPrincipal that you need), otherwise you are going to need to implement your own Authentication and set your own IPrincipal on each Request.

    Just for your info:
    Basic authentication sends the username and password in clear-text (technically base64 encoded) so should only be used over an SSL connection. Windows Explorer (in some versions) will not send Basic Authentication credentials over an non-secure connection.

  10. Johan permalink

    I tried a few different ways of doing this – but I’m trying to build a webdav-imlementation on top of an application I can’t modify – and the application hi-jacks the IIS but doesn’t supply the correct Response headers (Allow OPTIONS for instance). This made it quite difficult to handle.

    But then I stumbled on the ISAPI_Rewrite filter which can do pretty much the same as your described wildcard mapping, but is much more easy to implement. Just a hint.

    – Johan

  11. DMA permalink

    1. My requirement is to create a custom webdav server, which will return me documents and their meta data from database (instead of physical directory). Users may access this webdav server from windows exploer, so requirment is to display folders/documents similar to windows folders.

    2. What I have done :

    a. I have created a custom webdav server using httpmodule that traps all request and if the request contains a WebDAV method, it returns a Custom response.

    b. The response that I am sending is below.

    
    <a>
      </a><a>
        </a><a>http://localhost/test/</a>
        <a>
          </a><a>HTTP/1.1 200 OK</a>
          <a>
            </a><a>0</a>
            <a>2005-12-02T04:42:45.033Z</a>
            <a>test</a>
            <a>"36d979d6faf6c51:c46a"</a>
            <a>Fri, 02 Dec 2005 04:42:45 GMT</a>
            <a>
              </a><a / rel="nofollow">
            </a>
            <a / rel="nofollow">
            </a><a>0</a>
            <a>1</a>
            <a / rel="nofollow">
          </a>
    

    header

    c. Along with this response I am sending these headers,
    Server : Microsoft-IIs/5.1
    X-Powered by : ASP.NET
    ContentLocation : http://localhost/test
    ContentType : text/xml
    TransferEncoding : chunked

    d. After accessing my custom webdav server from explorer, the following error occurs.
    “\\localhost\CustomWebDAVServer is not accessible. You might not have permission to use this network resource. Contact the administrator of this server to find out if you have access permissions.The directory name is invalid.”

    e. I am using “integrated windows authentication” in IIS. Also I am administrator on my machine so I have full access.

    Can you please advise if I am missing anything (either in header or response format or with code)

  12. DMA,
    Are you literally sending back a response that uses <a> wrapped around the values? That’s not gonna work of course.

    Content-Location and Content-Type also have dashes in them.

    You’re trying to access \\localhost\CustomWebDAVServer but sending back a Content-Location and an href of http://localhost/test. It expects a URL that corresponds to the URL requested.

    Implementing a protocol is all about details. The details really matter a lot.

  13. Johan,
    The URL rewriting is a great idea as well. It might make things quite a bit simpler.

    I’ve gotten the whole thing working – mapping from Explorer, working with Office and its own WebDAV implementation, etc. So it can definitely be done either way.

  14. DMA permalink

    Geoff,

    One again thanks a lot for your valuable response. My mistake was I was sending wrong content location. After correcting that, I am able to open the folder that is requested. But IE does not show the contents inside the folder. As long as I am sending only the root folder to be shown in response it works fine, but when I give one document to be shown then it throws error of “Network path not found”.
    I am sending the response in XML format but XML is not able to upload on your website and it shows only the value inside the xml tags. Can you please give some insight on this.

  15. Alfred permalink

    Geoff,

    thanks for this article.

    I found myself excactly in your words: “”I did quite a bit of searching and found WebDAV server implementation in Python, Ruby, and Java, but could not find anything done in a .NET language…”

    We need to implement a webdav server for some kind of document management system. I’ve got a two questions:

    1. If you handle all incoming request, can you run normal http on the same server – can i run normal asp.net pages and webdav on the same iis?

    2. Are you interested in selling your code? I’m not a developer and don’t like the idea of reinventing the wheel. It would be a nice idea to save some time and spend some money :-)

    Thanks Alfred

  16. Alfred,

    1. You can map WebDAV to a virtual location (a sub-directory) under a main site or as a virtual host, so yes you can run it alongside other applications. You can also use URL rewriting as Johan suggested to accomplish this.

    2. The code was done as a work-for-hire (I’m a consultant), so does not belong to me. Email me privately if you are interested in consulting services.

  17. stacy permalink

    HI Geoff

    Thanks a ton for your valuable article. It greatly helped me to start with my webdav server.

    But I am stuck at one point can you help me with that.

    1. I need to get my documents from database and for that i need guid of the folder clicked by user. But IE seems to send only the path of folder along with the webdav method.Is there any way by which we can get a property back from IE along with a Propfind request.

    2. The opaquelocktoken sent with the Lock response is a unique GUID. How to generate it?? I read about some algos creating UUId but they talk of some NodeId and version — which i am not very clear on.

    Thanks in advance

    Regards

  18. Stacey,
    1. As long as you are going to be WebDAV compliant, the Path is all you are going to get. You will need a way to query the database and get an item based on its virtual path. The WebDAV useage is probably very different from the how your other applications are using the database, so consider creating creating views or “reporting tables” to help make the data access easier.

    2, In .NET there is a System.Guid class that has a NewGuid method. For all pratical purposes you could use a few random characters, your host name and a timestamp or something like that if you weren’t programming with a language that had easy GUID generation.

  19. stacy permalink

    OK I have used .Net’s System.GUID class. GUId (lock-token) now gets generated on LOCK request. But, the problem is I do that on every lock request (even if it is for the same resource) received by the server. I want to have exclusive locks on a resource. Would I have to keep a track within my custom webdav server as to which resource is currently locked and what’s the lock-token?

  20. stacy permalink

    One more thing, I found that anyone can find any one else’s lock token by DAV:lockdiscovery property. How can we use this property?

  21. Stacy,
    1. Yes you will have to keep track of the lock status/token for a given resource. You also have to check whether a resource is locked (and if the user supplies the proper lock token) for requests that modify the resource. The exact command are specified in the WebDAV RFC. This is easily done in a database or with a simple Hash structure mapping a resource to a lock. One caveat, a collection can be locked which means all of the children of that collection have an implied lock as well.

    2. lockdiscovery returns information about the lock. It can include things like who has the lock, when the timeout expires and the locktoken itself. The RFC also states: “The server is free to withhold any or all of this information if the requesting principal does not have sufficient access rights to see the requested data.” So, basically, don’t give back the lock token to a user who should not be able to override that lock in some way.

  22. stacy permalink

    HI Geoff

    I am stuck on one point now. A Non Microsoft office documnet (like tiff) files does not send a LOCK or PUT request so they cant be saved if are modified by some user. So is it a general phenomena or its only with some formats that PUT request is not sent.

    Thanks

  23. Stacy,
    The Mini-Redirector (the one that allows you to map a network location to a drive letter) does not support locking. When you open an Office document, they are actually handed off to Office and Office uses its own WebDAV implementation to open it. The Office WebDAV supports LOCK and UNLOCK.

    I have not see applications that won’t do a PUT. What happens if you drag and drop a TIFF file? Does it save then?

    If you add a network location in ‘My Network Places’ and use a defined port or https, then you will uses the Office Web Folders all the time. When you do that, I’ve see it where you can’t double-click non-Office files to GET them.

    In general, I’m coming to the conclusion that the Microsoft WebDAV implementations are pretty weak. I’ve had to open multiple support cases with Microsoft to get information on what deviations they have from the standard to work around them. You might need to do the same thing.

  24. Fahad permalink

    I am trying to implement the WebDav for Readonly access to documents.
    I tried your idea and got “ASP.NET Web Application without ASPX Extension” working.
    Now when I tired to open the same location (http://localhost/WebDav)
    by typing \\localhost\WebDav in the Explorer
    i am getting the error

    “\\localhost\WebDav is not accessible. You might not have permission to use this network resource. Contact the administrator of this erver to find out if you have access permission”

    “The network path was not found”

    Thanks

  25. Geoff,

    I am in the process of completing a webdav server framework in C# and was wondering the best way to distribute the codebase to the public (I was initially thinking CodeProject or Sourceforge). I have been working on the framework for approx 1 week and am almost done (NDoc comments soon to come!). The framework will allow a developer to easily expose webdav server functionality for any custom solution (I am including a filesystem adapter example to show how easy it is to use).

    I decided to create this framework after being unable to find any good articles on the web regarding webdav and C# and also to webdav enable my company’s Ayos File Managment System (formerly Spheros File Managment Server).

    Let me know!

  26. Germinal,
    I would go with SourceForge if you plan on making it Open Source. That way you could build up a development community around the project. Code Project is only good for one-off examples and not for real, working code.

  27. Fahad,
    You don’t want to use a UNC path (\\example\Path) to connect to a WebDAV share. You want to map a network drive and use the HTTP path (http://example/Path). From Internet Explorer you can also do File -> Open and use the HTTP path along with checking the checkbox on the form.

    In general, the “No permission” or “Can’t map network location” kind of errors could be just about any sort of problem with the response. So, unfortunately, I can’t tell you a lot about what the specific error might be.

  28. Fahad permalink

    Germinal Ibarrola,
    When you will be releasing the code? I am really interested in that.
    Thanks

  29. Thanks Geoff,

    All… I’ll be releasing the code on SourceForge in the next month or so (as time permits!) and will also have the latest code available on http://www.sphorium.com.

  30. stacy permalink

    Hi Geoff

    I am in the process of making my webdav server. But when i try to create a new folder through webdav, it sends me a Head request to whose my response is “200″ and other necessary headers. But I always get an error that
    “A folder named ‘New Folder’ already exists. Please enter a different name.”
    Other then this whenever i get a Head request, the processing is fine but while creating a new folder, i get this error.I dont get MKCOL request as expected.

    Thanks

  31. Stacy,

    If it’s sending a HEAD (or most other verb really) request for a path like ‘/webdav/example/New Folder’ and New Folder does not exist, the proper response is returning a 404 to indicate that the resource is not found on the server.

    It sounds like your WebDAV client is probing to see if the resource exists.

    The only command this doesn’t apply to that I can recall is the LOCK command. A client can LOCK a non-existant resource. Basically they lock in anticipation of creating that resource to ensure that no one sneaks in and takes the name behind them.

  32. Hi,
    All the info here has been very helpful and while i am reading rfc 2518 and 3253 i wanted to ask a question.
    I got as far as being able to intercept any calls for files but not for directories.
    Also , how would i intercept the first call that displays the root of the virtual directory and its files and folders.
    I am trying to not show the file system but instead show my own files which are on a database.

    Thanks in advance

  33. Oleg permalink

    If you have IIS 6.0, this article will helpful to enable wildcards: http://www.ureader.com/message/1251379.aspx

  34. Thanks Oleg, that’s a great link that might help people out.

  35. Guys,

    I’ve initial ASP.NET WebDav Server framework (beta 2) has finally been released https://sourceforge.net/projects/webdav/. It currently supports RFC2518 (http://www.webdav.org/specs/rfc2518.html) and I am in the process for supporting RFC3253 (http://www.webdav.org/specs/rfc3253.html)

    It includes all source code in addition to a custom file implementation demo project.

    I’m open for suggestions!

    Thanks.

  36. Fahad Azeem permalink

    Thanks a lot !

  37. Yahia permalink

    looks really good :-))

    though one thing is not clear – the license: is it GPL or LGPL ?

    Yahia

  38. Kavita permalink

    Hi Geoff

    when an application sends a LOCK request,my WebDAV server locks the file in my database as well as on web server. But if any problem occurs like application hangs then UNLOCK of webDAV server is not called hence i am not able to unlock it from my database. but the server unlocks it on its own.

    Any Idea how to solve it??

    Thanks

  39. Kavita,

    A LOCK command should have a timeout value. You can store the timeout value with the LOCK and check and see if the timeout has expired when you check the database. If it’s expired, then remove the lock from the database.

    A client will resend LOCK commands to keep the lock fresh if it needs to hold on to it, so you would then just update the timeout/timestamp when you get a LOCK request to update an existing lock.

  40. Miss Lane permalink

    My IT team is trying to connect to a WebDAV connection offsite. We’re able to connect to it at times, but periodically we’re receiving the message “You might not have permission to use this network source. Contact the administrator of this server to find out if you have access permissions.”

    I’ve called the guy who handles the WebDAV service there, and he remarked that everything seemed fine on his side. We ended up recreating our connections and everything was fine.

    One thing that came to mind was password expirations, but he said that flag had not been set on his side.

    Are there any other evident solutions to this problem, or are we to believe that the best way to evade this situation is to have to recreate an isntance of the connection everytime we are prompted with this error message.

    Anyhelp will be muchos graciasly appreciated.

  41. Stacy permalink

    Hi

    In my WebDAV server, i do not allow user to delete files which exist in the lowest level of my hierarchy. so whenever a request for file deletion comes, i am sending a status code of 405.

    But when an XP machine sends a deletion request, it always sends delete request for the lowest level object.
    Now since i am denying the file deletion, i am not able to delete even folders if the requesting client machine is an XP.
    However it works fine with 2003 server machine as it directly sends request for the selected resource.

    can anyone suggest how to deal with this situation.

  42. Sorry… I don’t understand… where is this custom Webdav server?

    Thanks

  43. JJ,
    Sorry, the code I did was for a client, so I was not able to share it. I just wanted to share some ideas in case anyone else was doing the same thing.

    One of my blog readers has implemented an Open Source project available at https://sourceforge.net/projects/webdav/
    if you would like to look at someone else’s code.

  44. Stacy permalink

    Geoff

    I have been working on Custom WebDAV server and am about to complete it.I have developed it on a Windows 2003 server machine and it works perfectly fine but when I tested it with an XP client (IIs v5.1), I found it is sending me different requests from a windows 2003 server(IIS v6.0) client.

    Like the “COPY” operation on an XP machine sends request to first create a collection by sending ‘MKCOL’ and then getting the files from the source of copy location (‘Get’ request)and then putting them on to newly created collection by a ‘PUT’ request. It never sends a ‘COPY’ command.

    Did u also face this and can you suggest how to get rid of this because my implementation of MKCOL and COPY are different. did u apply any update to get it fixed??

    Thanks in Advance

    stacy

  45. Stacy,
    The IIS version on the client won’t matter (IIS would only matter on the server receiving the requests). What might matter is the version of the mini-redirector and/or the version of Microsoft Office installed on the client machine. I never had to deal with the problem myself, all of the client machines were going to be running Windows XP.

    In general, the standards support in Windows is pretty bad. There are a lot of things that they do that I would consider incorrect with regards to the WebDAV standard. In general I found though that if my code passed the WebDAV litmus tests, then Windows XP would work, even if it was doing more or different commands from what I would expect.

    Check out my other WebDAV article. It’s got some information on the different implementations as well as some useful (I hope) links that talk about the different WebDAV implementations in various version of Windows.

  46. Mux permalink

    Hi DMA, try this one: http://www.webdavsystem.com/

    ed. I need to start charging for these links

  47. Anton permalink

    Hi

    Tell me please which external tool can I use to track the request and response packets send to/from WebDav server?

    I tried Ethereal, but I can’t cath any packets. I created a web application in visual studio 2005, so the address of the page, which I run from VS is like http://localhost:1233/WebDavServer. Even when I use no filters in Etherreal I can’t see any packets.

    Thaks for your Consideration
    Anton

  48. Anton,
    Windows does not provide an interface to capture packets against the localhost. To get around this, I actually set up 2 virtual servers, one to host the server and the other to use as a client. That way I could capture packets with Ethereal going to the “remote” server (really a virtual instance on the same machine).

    You could do the same thing by connecting to your development machine from another machine on the same network.

    Hope that helps.

  49. Anton permalink

    Thanks, Geoff
    I will try to deploy my web application to IIS.

    Other thing I would like to aks you: What is the specific behaviour of Mini-Redirector while mounting http connection.

    I tried to use Stasys WebDAV.Net framework and the test server. DAVExplorer successfully mounts the http connection, but Windows Mini-Redirector fails.

    Any suggestions?

    Best Regards
    Anton

  50. Anton permalink

    I have more one weird issue which I can not figure out. Maybe you could clear if for me.

    While browsing through Stacys WebDav Demo Server, it does not handle PROPFIND requests on folders. I mean when I put a breakpoint In DavModule, which is inherited from IHttpModule and click on the folder in DAVExplorer, the execution does not hit the breakpoint. But it’s hit while sending any other requests.

    Do you have any ideas whats wrong?

    Thanks
    Anton

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS