Search code examples
javarestonedrive

OneDrive REST API gives 400 Invalid Verb for file upload


I'm trying to upload a file to OneDrive using the REST API. The code below opens a socket to the OneDrive host and feeds it the payload from the example in the OneDrive doc, but OneDrive returns a 400 status with an "Invalid Verb" message.

I know the access token is valid because the GET call works. The access token has the following scopes: wl.offline_access, wl.skydrive_update.

I've tried GETting, PUTting and POSTing to other folders and the GETs work but PUTs and POSTs fail. Before using the socket I tried PUTs and POSTs using Jersey's WebResource, java's HttpUrlConnection, apache's HttpClient, and curl. OneDrive responds with different error message based on the verb/library pairing.

I've sent requests to a local server that dumped the payload so I could verify that there's no problem with the socket code. It is sending the exact message from the doc, but with whitespace replaced with control characters.

import java.io.PrintWriter;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

public class OneDriveUploadTest {

    public static void main(String[] args) throws Exception {
        String accessToken = "...";

        // this works
        // String data = "GET https://apis.live.net/v5.0/me/skydrive/files?access_token="+accessToken+"\r\n\r\n";

        // this fails
        String data = "POST https://apis.live.net/v5.0/me/skydrive/files?access_token="+accessToken+"\r\n\r\n--A300x\r\nContent-Disposition: form-data; name=\"file\"; filename=\"HelloWorld.txt\"\r\n\nContent-Type: application/octet-stream\r\nHello, World!\r\n--A300x--\r\n\r\n";

        // this also fails
        // String data = "PUT https://apis.live.net/v5.0/me/skydrive/files/HelloWorld.txt?access_token="+accessToken+"\r\n\r\nHello, World!\r\n\r\n"; 

        SSLSocketFactory socketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
        Socket socket = (SSLSocket) socketFactory.createSocket("apis.live.net", 443);
        PrintWriter sout = new PrintWriter(socket.getOutputStream());
        sout.print(data); 
        sout.flush();
        InputStream sin = socket.getInputStream(); 
        BufferedReader rd = new BufferedReader(new InputStreamReader(sin));
        String line;
        while ((line = rd.readLine()) != null) {
            System.out.println("got: " + line);
        }
        sin.close();
        sout.close();
        socket.close();
    }
}

Solution

  • I hate to answer my own question but I found something that works. This code uploads a file to OneDrive with a PUT using Apache's HttpClient.

    import java.io.File;
    import org.apache.http.client.HttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.HttpResponse;
    import org.apache.http.entity.FileEntity;
    import org.apache.http.client.methods.HttpPut;
    
    public class OneDriveHttpClientUpload {
        public static void main(String[] args) throws Exception {
            String accessToken = "...";
            String url = "https://apis.live.net/v5.0/me/skydrive/files/HelloWorld.txt?access_token="+accessToken;
            HttpClient apClient = HttpClients.createDefault();
            HttpPut httpPut = new HttpPut(url);
            FileEntity file = new FileEntity(new File("testfile.txt"));
            httpPut.setEntity(file);
            HttpResponse ret = apClient.execute(httpPut);
        }
    }