com.emc.atmos.api.multipart.MultipartEntity Maven / Gradle / Ivy
/*
* Copyright 2013 EMC Corporation. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0.txt
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.emc.atmos.api.multipart;
import com.emc.atmos.api.Range;
import com.emc.util.StreamUtil;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Represents a multipart response entity.
*/
public class MultipartEntity extends ArrayList {
private static final long serialVersionUID = -4788353053749563899L;
private static final Pattern PATTERN_CONTENT_TYPE = Pattern.compile( "^Content-Type: (.+)$" );
private static final Pattern PATTERN_CONTENT_RANGE = Pattern.compile( "^Content-Range: bytes (\\d+)-(\\d+)/(\\d+)$" );
/**
* Parses a multipart response body provided by an InputStream. Returns an instance of this class that represents
* the response. boundary may start with "--" or omit it.
*/
public static MultipartEntity fromStream( InputStream is, String boundary ) throws IOException {
if ( boundary.startsWith( "--" ) ) boundary = boundary.substring( 2 );
List parts = new ArrayList();
try {
while ( true ) {
// first, we expect a boundary ( EOL + '--' + + EOL )
if ( !"".equals( StreamUtil.readLine( is ) ) )
throw new MultipartException( "Parse error: expected EOL before boundary" );
String line = StreamUtil.readLine( is );
// two dashes after the boundary means EOS
if ( ("--" + boundary + "--").equals( line ) ) break;
if ( !("--" + boundary).equals( line ) ) throw new MultipartException(
"Parse error: expected [--" + boundary + "], instead got [" + line + "]" );
Matcher matcher;
String contentType = null;
int start = -1, end = 0, length = 0;
while ( !"".equals( line = StreamUtil.readLine( is ) ) ) {
matcher = PATTERN_CONTENT_TYPE.matcher( line );
if ( matcher.find() ) {
contentType = matcher.group( 1 );
continue;
}
matcher = PATTERN_CONTENT_RANGE.matcher( line );
if ( matcher.find() ) {
start = Integer.parseInt( matcher.group( 1 ) );
end = Integer.parseInt( matcher.group( 2 ) );
length = end - start + 1;
// total = Integer.parseInt( matcher.group( 3 ) );
continue;
}
throw new MultipartException( "Unrecognized header line: " + line );
}
if ( contentType == null )
throw new MultipartException( "Parse error: No content-type specified in part" );
if ( start == -1 )
throw new MultipartException( "Parse error: No content-range specified in part" );
// then the data of the part
byte[] data = new byte[length];
int read, count = 0;
while ( count < length ) {
read = is.read( data, 0, length - count );
count += read;
}
parts.add( new MultipartPart( contentType, new Range( start, end ), data ) );
}
} finally {
is.close();
}
return new MultipartEntity( parts );
}
public MultipartEntity( List parts ) {
super( parts );
}
/**
* Convenience method that aggregates the bytes of all parts into one contiguous byte array.
*/
public byte[] aggregateBytes() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for ( MultipartPart part : this ) {
baos.write( part.getData() );
}
return baos.toByteArray();
} catch ( IOException e ) {
throw new RuntimeException( "Unexpected error", e ); // unrecoverable
}
}
}