Android DefaultHttpClient POST method Can't set Content Length, getting Http Error 411

5.2k Views Asked by At

I am having problem with an HttpPost on Android 4.2. I am trying to call an authentication service that is hosted in a .NET WebAPI service.

The service requires that the request be made as a POST method, and that several custom request headers be supplied. The body of the request sends Json values to the service.

Here is how I am composing my request;

HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://api.fekke.com/api/account/login");
httppost.addHeader(HTTP.TARGET_HOST, "api.fekke.com");
httppost.addHeader("Accept", "application/json");
httppost.addHeader("Fekke-AccessKey", "some-access-key");
httppost.addHeader("Date", dateString);
httppost.addHeader("Fekke-Signature", "some-encoded-value");
httppost.addHeader("Content-Type", "application/json; charset=utf-8");

String jsonmessage = "{\"Username\": \"myusername\", \"Password\": \"mypassword987\"}";
httppost.setEntity(new StringEntity(jsonmessage, HTTP.UTF_8));

HttpResponse response = httpclient.execute(httppost);

I have tried to call this with a "Content-Length" header, and it throws an exception saying that the header already exists in the request.

I have also tried calling this using an HttpURLConnection object, which what Google proposes using from this point forward, but this also runs into the same problem.

I have been able to make this request call from the iOS and .NET without a problem, so I know it is not the service.

UPDATE #1

I ran the following example through a local version of the service, and I was able to isolate the error to when I pass a hashed value in one of the http headers. I am using a JWT security token that uses the HMAC SHA-256 hashing algorithm to set a value used by the service. This hashed value causes the execution of the request to blow up.

UPDATE #2

I was finally able to resolve the problem. The problem was caused by the Base64 encodeToString method adding an invisble character to the value. It was forcing a corruption in the header. I was using the default setting of 0 or Base64.DEFAULT. I changed the value to Base64.NO_WRAP, and it resolved the issue.

1

There are 1 best solutions below

4
On

you should not worry about setting 'content-length' header on a post if you are using the correct methods. With Post, you usually use some overloaded version of 'SetEntity($Type-stream/string/encodedArray...)'

look at this source( search on length) to see examples of how the API takes care of the length for you on a post.

You should evaluate what lib you will use for http and find POST examples showing methods like 'setEntity' to set the body of the post. that way , you will not get errors related to content-length. see example

Sample code "HttpConnection" class - using links from my comment...

For JSON OR Bitmap:

try {                       
    HttpResponse response = null;
    switch (method) {
    case GET:
        HttpGet httpGet = new HttpGet(url);             
        response = httpClient.execute(httpGet);
        break;
    //Note on NIO - using a FileChannel and  mappedByteBuffer may be NO Faster
    // than using std httpcore  http.entity.FileEntity
    case POST:
        //TODO bmp to stream to bytearray for data entity
        HttpPost httpPost = new HttpPost(url); //urlends "audio OR "pic" 
        if (  !url.contains("class") ){                 
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            //can alter belo for smaller uploads to parse .JPG , 40,strm
            bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);               
            httpPost.setEntity(new ByteArrayEntity(stream.toByteArray()));

        }else if(data != null && (url.contains("class"))){
            //bug data is blank
            httpPost.setEntity(new StringEntity(data));
            Log.d(TAG, "DATA in POST run-setup " +data);
        }

        response = httpClient.execute(httpPost);
        break;
    case PUT:
        HttpPut httpPut = new HttpPut(url);
        httpPut.setEntity(new StringEntity(data));
        response = httpClient.execute(httpPut);
        break;
    case DELETE:
        response = httpClient.execute(new HttpDelete(url));
        break;
    case BITMAP:
        response = httpClient.execute(new HttpGet(url));
        processBitmapEntity(response.getEntity());
        break;
    }
    if (method < BITMAP)
        processEntity(response.getEntity());
} catch (Exception e) {
    handler.sendMessage(Message.obtain(handler,
            HttpConnection.DID_ERROR, e));
}

For XML body in Post:

try {
    HttpResponse response = null;
    switch (method) {

    case POST:
        HttpPost httpPost = new HttpPost(url);
        if (data != null){
            System.out.println(" post data not null ");
            httpPost.setEntity(new StringEntity(data));
        }
        if (entry != null){
            ContentProducer cp = new ContentProducer() {
                public void writeTo(OutputStream outstream) throws IOException {

                     ExtensionProfile ep = new ExtensionProfile();
                     ep.addDeclarations(entry);
                     XmlWriter xmlWriter = new XmlWriter(new OutputStreamWriter(outstream, "UTF-8"));
                     entry.generate(xmlWriter, ep);
                     xmlWriter.flush();
                }
            };
            httpPost.setEntity(new EntityTemplate(cp));
        }
        httpPost.addHeader("GData-Version", "2");
        httpPost.addHeader("X-HTTP-Method-Override", "PATCH");
        httpPost.addHeader("If-Match", "*");
        httpPost.addHeader("Content-Type", "application/xml");
        response = httpClient.execute(httpPost);

        break;
    }
    if (method < BITMAP)
        processEntity(response.getEntity());
} catch (Exception e) {
    handler.sendMessage(Message.obtain(handler,
            HttpConnection.DID_ERROR, e));
}