LOCK PUT UNLOCK with cURL / WebDAV
My idea is to LOCK a file on an apache/WebDAV server, PUT an updated version of it on the server and UNLOCK it afterwards.
I just tried the following with cadaver:
- create a file A.txt with content a file
- GET file A.txt which yields a file
- edit A.txt to be updated file and save it (in cadaver)
- GET file A.txt which yields still yields a file
- close edit (VIM) in cadaver
- GET file A.txt which yields updated file
I guess internally cadaver LOCKs the file, GETs it and changes it locally. Then it PUTs it and UNLOCKs it.
QUESTION: how can I do this with curl?
PROBLEM: When then connection is slow and I do a PUT for a file, that is not yet completely uploaded, I only get the yet uploaded part. I would like to get the old one as long as the new one isn't complete.
TRIED: I tried the following to LOCK the file by hand (i.e. with cURL):
curl -v -X LOCK --user "user:password" http://myServer/newFile
What I get is:
* About to connect() to myServer port 80 (#0) * Trying xx.xx.xxx.xxx... connected * Connected to myServer (xx.xx.xxx.xxx) port 80 (#0) * Server auth using Basic with user 'user' > LOCK /newFile HTTP/1.1 > Authorization: Basic xxxxxxxxxxxxxxxxx > User-Agent: curl/7.21.6 (x86_64-pc-linux-gnu) libcurl/7.21.6 OpenSSL/1.0.0e zlib/22.214.171.124 libidn/1.22 librtmp/2.3 > Host: myServer > Accept: */* > < HTTP/1.1 400 Bad Request < Date: Wed, 02 May 2012 15:20:55 GMT < Server: Apache/2.2.3 (CentOS) < Content-Length: 226 < Connection: close < Content-Type: text/html; charset=iso-8859-1 < <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>400 Bad Request</title> </head><body> <h1>Bad Request</h1> <p>Your browser sent a request that this server could not understand.<br /> </p> </body></html> * Closing connection #0
Looking at the apache log file I find:
[Wed May 02 15:20:55 2012] [error] [client xx.xx.xxx.xxx] The lock refresh for /newFile failed because no lock tokens were specified in an "If:" header. [400, #0] [Wed May 02 15:20:55 2012] [error] [client xx.xx.xxx.xxx] (20)Not a directory: No locktokens were specified in the "If:" header, so the refresh could not be performed. [400, #103]
Thanks for any hint!!
UPDATE: I added my problem description.. Cheers!
The LOCK method requires a body which contains an XML description of the lock you want to take out. Your cURL test didn't include this body, hence the 400 error response.
But if I understand your question correctly, you want to:
If that's true, why would you bother with the LOCK and UNLOCK? Just do the PUT! Locks would only be useful if you want to carry out multiple operations while you are holding the lock and avoid having another client see the object in its partially modified state or (perhaps worse) modify the object concurrently with you.
A typical case where locking can be useful is a read-modify-write cycle: you want to GET the object, modify it locally, and PUT it back, but disallow another client from making a competing change between the time you GET it and the time you PUT it. However, for dealing with this specific case, HTTP offers a different method of resolving the issue, without using locks (which are ill-suited for a stateless protocol like HTTP):
- GET the object
- Modify it locally
- PUT the object back with an If-Match header that contains the original ETag returned in step 1
- If the PUT results in a 412 error, go back to step 1. Otherwise, you are done.
UPDATE: based on your updated question, I see that you are see a partial truncated or half-uploaded version of the new file if you do a GET concurrent with a PUT. This is unfortunate. The server should treat the PUT as atomic with respect to other requests. Other clients should either see the old version or the new version, never a state in between. There's nothing you should need to do from the client end to make this true. It should be fixed in the server.