@Override protected InputStream getBody() throws Exception {
        // create a cloned array of all the form parameters. This is necessary
        // because this method may return before the data for all the form
        // params are computed, and we don't want a race condition where
        // the params are modified externally after this call
        final Parameter[] params = getFormParameters();
        for (int i=0; i<params.length; i++) {
            params[i] = params[i].clone();
        }
        
        // if we have a multipart/form-data encoding, then create and return
        // a MultipartInputStream. This input stream is constructed from a series
        // of "chunks", each chunk being either a String or FormParameter
        // (representing a File to be uploaded). In this way, we have a reference
        // to the file to be uploaded embedded in the stream such that the
        // InputStream to the file data is only opened when necessary, when we
        // get to it, and only as much of it is read into memory as necessary.
        if (encoding == Encoding.MultipartFormData) {
            List<Object> chunks = new ArrayList<Object>();
            StringBuilder buffer = new StringBuilder();
            for (int index = 0; index < params.length; index++) {
                if (index > 0) {
                    buffer.append("\r\n");
                }
                buffer.append("--" + boundary);
                buffer.append("\r\n");
                
                Parameter param = params[index];
                buffer.append("Content-Disposition: form-data; name=\"" + param.getName() + "\"");
                if (param instanceof FileParameter) {
                    FileParameter fileParam = (FileParameter)params[index];
                    String filename = fileParam.getFilename();
                    if (filename == null) continue;
                    
                    buffer.append("; filename=\"" + filename + "\"");
                    buffer.append("\r\n");
                    buffer.append("Content-Type: " + fileParam.getContentType());
                    buffer.append("\r\n\r\n");
                    //clear the buffer
                    chunks.add(buffer.toString());
                    buffer.delete(0, buffer.length());
                    //write out the file
                    chunks.add(fileParam);
                } else {
                    buffer.append("\r\n\r\n");
                    buffer.append(param.getValue());
                }
            }

            buffer.append("\r\n");
            buffer.append("--" + boundary);
            buffer.append("--");
            chunks.add(buffer.toString());
            
            return new MultipartInputStream(chunks);
        } else {
            //default to UrlEncoded
            StringBuffer b = new StringBuffer();
            for (int i=0; i<params.length; i++) {
                if (i > 0) b.append("&");
                Parameter p = params[i];
                String name = URLEncoder.encode(p.getName(), "UTF-8");
                String value = URLEncoder.encode(p.getValue(), "UTF-8");
                b.append(name);
                b.append("=");
                b.append(value);
            }
            return new ByteArrayInputStream(b.toString().getBytes());
        }
    }