Java HttpUrlConnection response code when connection closed by server

1.2k views Asked by At

I am uploading file from android to server using HttpUrlConnection. In normal case file upload successful and correct HTTP response code (200) and message captured. But server also validates the file being uploaded and can close connection anytime while uploading which prohibits client to upload unnecessary data to server.

In brief, Server has following validation:

1. File MimeType check: If file MimeType isn't any image MimeType then close connection immediately throwing HTTP response code 415 and message "Unsupported Media Type". This check done immediately after request arrived in server.

2. File size check: While upload is being progressed as stream, if file size more then 5 MB then server close connection by throwing HTTP response code 413 and message "Request entity too large"

Current status:

  1. If file is image type AND less then 5 MB upload successful.
  2. If file is not image type OR more then 5 MB unable to get HTTP response code (413 or 415) and message from HttpUrlConnection object. SocketException or IOException occurs and then invoking connection.getResponseCode() throws another exception. Why not connection object holds response code and message sent from server ?

Code:

package com.example.mahbub.fileuploadrnd;

import android.util.Log;

import com.example.mahbub.fileuploadrnd.util.MimeTypesUtil;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.SocketException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class FileUploadRequest {
    private static final String DEBUG_TAG = "FileUploadRequest";

    public static final int ERROR_OUT_OF_MEMORY = 0x00;
    public static final int ERROR_UNDEFINED = 0x01;
    public static final int ERROR_HTTP_ERROR = 0x02;

    /**
     * The pool of ASCII chars to be used for generating a multipart boundary.
     */
    private final static char[] MULTIPART_CHARS =
        "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
    private static final String LINE_END = "\r\n";
    private static final String TWO_HYPHENS = "--";

    String url;
    Map<String,String> mStringParts;
    String filePartName;
    String filePath;
    UploadListener mListener;

    public FileUploadRequest(String url, String filePartName, String filePath, UploadListener listener) {
        this.url = url;
        this.mStringParts = new HashMap<>();
        this.filePartName = filePartName;
        this.filePath = filePath;
        this.mListener = listener;
    }

    public void execute() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                executeRequest();
            }
        }).start();
    }

    public void addStringPart(String name, String value) {
        this.mStringParts.put(name, value);
    }

    public void addStringParts(Map<String, String> parts) {
        this.mStringParts.putAll(parts);
    }

    public interface UploadListener {
        void onSuccess(int responseCode, String response);
        void onError(int errorCode);
        void transferred(long transferred, double progress);
    }

    private void executeRequest() {
        HttpURLConnection connection = null;
        DataOutputStream outputStream = null;

        String boundary = generateBoundary();

        try {
            URL url = new URL(this.url);
            connection = (HttpURLConnection) url.openConnection();

            // Allow Inputs & Outputs
            connection.setDoInput(true);
            connection.setDoOutput(true);
            connection.setUseCaches(false);
            connection.setChunkedStreamingMode(4 * 1024);
            // Enable POST method
            connection.setRequestMethod("POST");

            // Set header field value
            connection.setRequestProperty("Connection", "Keep-Alive");
            connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);

            outputStream = new DataOutputStream(connection.getOutputStream());
            outputStream.writeBytes(TWO_HYPHENS + boundary + LINE_END);

            // At first write all string parts
            if(mStringParts.size() > 0) {
                for (Map.Entry<String, String> entry : mStringParts.entrySet()) {
                    //entity.addTextBody(entry.getKey(), entry.getValue());
                    String partData = String.format("Content-Disposition: form-data; name=\"%s\"", entry.getKey()) + LINE_END + LINE_END;
                    partData += entry.getValue() + LINE_END;
                    partData += (TWO_HYPHENS + boundary + LINE_END);
                    outputStream.writeBytes(partData);
                }
            }

            // Write file data
            File file = new File(filePath);
            FileInputStream fileInputStream = new FileInputStream(file);

            String partData = String.format("Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"", filePartName, file.getName()) + LINE_END;
            partData += String.format("Content-Type: %s", getMimeType(file.getName()));
            partData += LINE_END + LINE_END;
            outputStream.writeBytes(partData);

            // Input stream read buffer
            byte[] buffer;
            // Max possible buffer size
            int maxBufferSize = 5 * 1024;   // 5KB

            // Bytes available to write in input stream
            int bytesAvailable = fileInputStream.available();
            Log.d(DEBUG_TAG, "File size: " + bytesAvailable);
            // Buffer size
            int bufferSize = Math.min(bytesAvailable, maxBufferSize);
            // Number of bytes read per read operation
            int bytesRead;

            // Allocate buffer
            buffer = new byte[bufferSize];

            bytesRead = fileInputStream.read(buffer, 0, bufferSize);

            boolean errorOccured = false;

            try {
                while(bytesRead > 0) {
                    try {
                        outputStream.write(buffer, 0, bufferSize);
                    } catch (OutOfMemoryError e) {
                        Log.d(DEBUG_TAG, "OutOfMemoryError occurred");
                        errorOccured = true;
                        e.printStackTrace();
                        if(null != mListener) mListener.onError(ERROR_OUT_OF_MEMORY);
                        break;
                    } catch (SocketException e) {
                        Log.d(DEBUG_TAG, "SocketException occurred");
                        errorOccured = true;
                        e.printStackTrace();
                        if(null != mListener) mListener.onError(ERROR_OUT_OF_MEMORY);
                        break;
                    } catch (IOException e) {
                        Log.d(DEBUG_TAG, "IOException occurred");
                        errorOccured = true;
                        e.printStackTrace();
                        if(null != mListener) mListener.onError(ERROR_OUT_OF_MEMORY);
                        break;
                    }

                    bytesAvailable = fileInputStream.available();
                    bufferSize = Math.min(bytesAvailable, maxBufferSize);
                    bytesRead = fileInputStream.read(buffer, 0, bufferSize);

                    double progress = ((double)(file.length()-bytesAvailable) / file.length());
                    Log.d(DEBUG_TAG, "Progress: " + progress);
                    if(null != mListener) mListener.transferred(file.length()-bytesAvailable, progress);
                }
            } catch (Exception e) {
                e.printStackTrace();
                if(null != mListener) mListener.onError(ERROR_UNDEFINED);
            }
            if(!errorOccured) {
                outputStream.writeBytes(LINE_END);
                outputStream.writeBytes(TWO_HYPHENS + boundary + TWO_HYPHENS + LINE_END);
            }

            // Responses from the server (code and message)
            int responseCode = connection.getResponseCode();
            String response = connection.getResponseMessage();

            Log.d(DEBUG_TAG, "Server Response Code: "  + responseCode);
            Log.d(DEBUG_TAG, "Server Response Message: " + response);

            InputStream inStream = null;
            if(responseCode >= 200 && responseCode < 400) inStream = connection.getInputStream();
            else inStream = connection.getErrorStream();
            String responseString = readStream(inStream);
            Log.d(DEBUG_TAG, "responseString: " + responseString);
            if(responseCode >= 200 && responseCode < 400) {
                if(null != mListener) mListener.onSuccess(responseCode, responseString);
            } else {
                if(null != mListener) mListener.onError(ERROR_HTTP_ERROR);
            }

            fileInputStream.close();
            outputStream.flush();
            outputStream.close();
            outputStream = null;
            inStream.close();
        } catch (Exception e) {
            e.printStackTrace();
            if(null != mListener) mListener.onError(ERROR_UNDEFINED);
        }
    }

    private String readStream(InputStream iStream) throws IOException {
        //build a Stream Reader, it can read char by char
        InputStreamReader iStreamReader = new InputStreamReader(iStream);
        //build a buffered Reader, so that i can read whole line at once
        BufferedReader bReader = new BufferedReader(iStreamReader);
        String line = null;
        StringBuilder builder = new StringBuilder();
        while((line = bReader.readLine()) != null) {  //Read till end
            builder.append(line);
        }
        bReader.close();         //close all opened stuff
        iStreamReader.close();
        //iStream.close(); //EDIT: Let the creator of the stream close it!
        // some readers may auto close the inner stream
        return builder.toString();
    }

    private String generateBoundary() {
        final StringBuilder buffer = new StringBuilder();
        final Random rand = new Random();
        final int count = rand.nextInt(11) + 30; // a random size from 30 to 40
        for (int i = 0; i < count; i++) {
            buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
        }
        return buffer.toString();
    }


    /**
     * Get mimeType based on file extension
     * @param fileName
     * @return
     */
    private String getMimeType(String fileName) {
        String[] parts = fileName.split("\\.");
        if(parts.length <= 1) return null;

        return MimeTypesUtil.getMimeType(parts[parts.length-1]);
    }
}
0

There are 0 answers