1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.compatibility.common.util; 18 19 import java.io.ByteArrayOutputStream; 20 import java.io.IOException; 21 import java.io.InputStream; 22 import java.io.OutputStream; 23 import java.io.OutputStreamWriter; 24 import java.io.PrintWriter; 25 import java.net.HttpURLConnection; 26 import java.net.URL; 27 import java.util.HashMap; 28 import java.util.Map; 29 30 /** Builds a multipart form and submits it. */ 31 class MultipartForm { 32 33 private static final String FORM_DATA_BOUNDARY = "C75I55u3R3p0r73r"; 34 35 /* package */ final String mServerUrl; 36 /* package */ final Map<String, String> mFormValues = new HashMap<String, String>(); 37 /* package */ String mName; 38 /* package */ String mFileName; 39 /* package */ byte[] mData; 40 41 /** 42 * Creates a new multi-part form with the given serverUrl. 43 */ MultipartForm(String serverUrl)44 public MultipartForm(String serverUrl) { 45 mServerUrl = serverUrl; 46 } 47 48 /** 49 * Adds a key value attribute to the form. 50 * 51 * @param name the name of the attribute. 52 * @param value the attribute's value. 53 * @return the {@link MultipartForm} for easy chaining. 54 */ addFormValue(String name, String value)55 public MultipartForm addFormValue(String name, String value) { 56 mFormValues.put(name, value); 57 return this; 58 } 59 60 /** 61 * Adds the file as the payload of the form. 62 * 63 * @param name The name of attribute 64 * @param fileName The file's name 65 * @param data The file's data 66 * @return the {@link MultipartForm} for easy chaining. 67 */ addFormFile(String name, String fileName, byte[] data)68 public MultipartForm addFormFile(String name, String fileName, byte[] data) { 69 mName = name; 70 mFileName = fileName; 71 mData = data; 72 return this; 73 } 74 75 /** 76 * Submits the form to the server url. 77 * 78 * This will handle a redirection from the server. 79 * 80 * @return response code 81 * @throws IOException 82 */ submit()83 public int submit() throws IOException { 84 return submitForm(mServerUrl); 85 } 86 87 /** 88 * @param serverUrl to post the data to 89 * @return response code 90 * @throws IOException 91 */ submitForm(String serverUrl)92 private int submitForm(String serverUrl) throws IOException { 93 HttpURLConnection connection = null; 94 try { 95 URL url = new URL(serverUrl); 96 connection = (HttpURLConnection) url.openConnection(); 97 connection.setInstanceFollowRedirects(false); 98 connection.setRequestMethod("POST"); 99 connection.setDoOutput(true); 100 connection.setRequestProperty("Content-Type", 101 "multipart/form-data; boundary=" + FORM_DATA_BOUNDARY); 102 103 byte[] body = getContentBody(); 104 connection.setRequestProperty("Content-Length", Integer.toString(body.length)); 105 106 OutputStream output = connection.getOutputStream(); 107 try { 108 output.write(body); 109 } finally { 110 output.close(); 111 } 112 113 // Open the stream to get a response. Otherwise request will be cancelled. 114 InputStream input = connection.getInputStream(); 115 input.close(); 116 117 int response = connection.getResponseCode(); 118 if (response == 302) { 119 return submitForm(connection.getHeaderField("Location")); 120 } 121 return response; 122 } finally { 123 if (connection != null) { 124 connection.disconnect(); 125 } 126 } 127 } 128 getContentBody()129 /* package */ byte[] getContentBody() throws IOException { 130 ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); 131 PrintWriter writer = new PrintWriter(new OutputStreamWriter(byteOutput)); 132 writer.println(); 133 134 for (Map.Entry<String, String> formValue : mFormValues.entrySet()) { 135 writeFormField(writer, formValue.getKey(), formValue.getValue()); 136 } 137 138 if (mData != null) { 139 writeFormFileHeader(writer, mName, mFileName); 140 writer.flush(); // Must flush here before writing to the byte stream! 141 byteOutput.write(mData); 142 writer.println(); 143 } 144 writer.append("--").append(FORM_DATA_BOUNDARY).println("--"); 145 writer.flush(); 146 writer.close(); 147 return byteOutput.toByteArray(); 148 } 149 writeFormField(PrintWriter writer, String name, String value)150 private void writeFormField(PrintWriter writer, String name, String value) { 151 writer.append("--").println(FORM_DATA_BOUNDARY); 152 writer.append("Content-Disposition: form-data; name=\"").append(name).println("\""); 153 writer.println(); 154 writer.println(value); 155 } 156 writeFormFileHeader(PrintWriter writer, String name, String fileName)157 private void writeFormFileHeader(PrintWriter writer, String name, String fileName) { 158 writer.append("--").println(FORM_DATA_BOUNDARY); 159 writer.append("Content-Disposition: form-data; name=\"").append(name); 160 writer.append("\"; filename=\"").append(fileName).println("\""); 161 writer.println("Content-Type: application/x-gzip"); 162 writer.println("Content-Transfer-Encoding: binary"); 163 writer.println(); 164 } 165 } 166