none
411 error when invoking ADLS Gen2 PUT rest call to create directory

    Question

  • When I am trying to create directory in ADLS Gen2 with the below code in Java

                  
    String accountName = "xxx";
    String accessKey = "yyy";		
    String uri = "https://"+accountName+".dfs.core.windows.net/datalakefs1/Test2";		
    
    Client client = ClientBuilder.newClient().property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
    		client.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
    WebTarget target = client.target(uri);
    		
    String gmDate1 = getStorageDate();
    
    String StringToSign = "PUT\n"
    		+ "\n" // content encoding
    		+ "\n" // content language
    		+ "0\n"// content length	
    		+ "\n" // content md5
    		+ "\n" // content type
    		+ "\n" // date
    		+ "\n" // if modified since
    		+ "\n" // if match
    		+ "\n" // if none match
    		+ "\n" // if unmodified since
    		+ "\n" // range
    		+ "x-ms-date:" + gmDate1 + "\n" + "x-ms-version:2018-11-09" + "\n" // headers
    		+ "/xxx/datalakefs1/Test2\nresource:directory"; // resources
    		
    String	authorizationStringToHash = getAuthenticationString(accountName, accessKey, StringToSign);
    		
    Response response  = target.queryParam("resource", "directory")
    				.request().header("Authorization", authorizationStringToHash)
    				.header("content-length", "0")
    				.header("x-ms-date", gmDate1)
    				.header("x-ms-version", "2018-11-09")
    				.method("PUT");

    I got the below error : 

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
    <HTML><HEAD><TITLE>Length Required</TITLE>
    <META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
    <BODY><h2>Length Required</h2>
    <hr><p>HTTP Error 411. The request must be chunked or have a content length.</p>
    </BODY></HTML>

    I am using below methods to get x-ms-date and authorization value :

    public static String getStorageDate() {
    		Date d = new Date(System.currentTimeMillis());
    		SimpleDateFormat f = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
    		f.setTimeZone(TimeZone.getTimeZone("GMT"));
    		String dateString = f.format(d);
    		return dateString;
    	}
    
    	private static String getAuthenticationString(String accountName, String accessKey, String stringToSign)
    			throws Exception {
    		Mac mac = Mac.getInstance("HmacSHA256");
    		mac.init(new SecretKeySpec(Base64.decodeBase64(accessKey), "HmacSHA256"));
    		String authKey = new String(Base64.encodeBase64(mac.doFinal(stringToSign.getBytes("UTF-8"))));
    		String auth = "SharedKey " + accountName + ":" + authKey;
    		return auth;
    	}

    Please let me know the issue with this code.

    Thanks,

    Ramya


    • Edited by Ramya_K Tuesday, April 23, 2019 9:49 AM
    Tuesday, April 23, 2019 9:47 AM

Answers

  • Issue is resolved after removing the content-length (from the header and the signature) and setting the content-type.

    We cannot set the content-length as Jersey automatically sets the content-length header based on the payload data.

    Below is the updated code: 

    String StringToSign = "PUT\n"
    		+ "\n" // content encoding
    		+ "\n" // content language
    		+ "\n"// content length	
    		+ "\n" // content md5
    		+ "text/plain\n" // content type
    		+ "\n" // date
    		+ "\n" // if modified since
    		+ "\n" // if match
    		+ "\n" // if none match
    		+ "\n" // if unmodified since
    		+ "\n" // range
    		+ "x-ms-date:" + gmDate1 + "\n" + "x-ms-version:2018-11-09" + "\n" // headers
    		+ "/xxx/datalakefs1/Test2\nresource:directory"; // resources
    		
    String	authorizationStringToHash = getAuthenticationString(accountName, accessKey, StringToSign);
    
    Response response  = target.queryParam("resource", "directory")			
    				.request().header("Authorization", authorizationStringToHash)
    				.header("content-type","text/plain")
    				.header("x-ms-date", gmDate1)
    				.header("x-ms-version", "2018-11-09")								
    				.put(empty);
    

    • Marked as answer by Ramya_K Friday, April 26, 2019 6:48 AM
    Friday, April 26, 2019 6:30 AM

All replies

  • Hi Ramya,

    The URI should be in the format :

    https://<storage account name>.dfs.core.windows.net/<file system name>/<name of directory to be created>?resource=directory.

    Please try this out and let us know if it works. Else, we can continue to dive deeper into the issue.


    MSDN

    Wednesday, April 24, 2019 11:00 AM
    Moderator
  • Hi ChiragMishra,

    The URI is in the same format as you have mentioned.

    In the below line of code, datalakefs1 is the file system and Test2 is the directory to be created.

    String uri = "https://"+accountName+".dfs.core.windows.net/datalakefs1/Test2";	


    I have specified the query parameters as below :

    target.queryParam("resource", "directory")

    It is working in Postman, when I execute the PUT request with the same headers and query params.


    Thanks,

    Ramya

    Thursday, April 25, 2019 11:11 AM
  • Issue is resolved after removing the content-length (from the header and the signature) and setting the content-type.

    We cannot set the content-length as Jersey automatically sets the content-length header based on the payload data.

    Below is the updated code: 

    String StringToSign = "PUT\n"
    		+ "\n" // content encoding
    		+ "\n" // content language
    		+ "\n"// content length	
    		+ "\n" // content md5
    		+ "text/plain\n" // content type
    		+ "\n" // date
    		+ "\n" // if modified since
    		+ "\n" // if match
    		+ "\n" // if none match
    		+ "\n" // if unmodified since
    		+ "\n" // range
    		+ "x-ms-date:" + gmDate1 + "\n" + "x-ms-version:2018-11-09" + "\n" // headers
    		+ "/xxx/datalakefs1/Test2\nresource:directory"; // resources
    		
    String	authorizationStringToHash = getAuthenticationString(accountName, accessKey, StringToSign);
    
    Response response  = target.queryParam("resource", "directory")			
    				.request().header("Authorization", authorizationStringToHash)
    				.header("content-type","text/plain")
    				.header("x-ms-date", gmDate1)
    				.header("x-ms-version", "2018-11-09")								
    				.put(empty);
    

    • Marked as answer by Ramya_K Friday, April 26, 2019 6:48 AM
    Friday, April 26, 2019 6:30 AM
  • Hi Ramya,

    Glad to hear that the problem was resolved. Also, thanks for sharing the resoultion. It will be helpful for the community at large.


    MSDN

    Monday, April 29, 2019 9:24 AM
    Moderator