/*
* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.webkit.network.data;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.Base64;
import java.util.LinkedList;
Implementation of the data: protocol handler as specified by RFC 2397.
Examples:
data:image/gif;base64,R0lGODdhM....
data:text/plain;charset=iso-8859-7,%be%f9%be
data:text/css,.picture%20%7B%20background%3A%20none%3B%20%7D
data:application/x-unknown,ERROR
Author: jccollet
/**
* Implementation of the data: protocol handler as specified by RFC 2397.
* Examples:
* data:image/gif;base64,R0lGODdhM....
* data:text/plain;charset=iso-8859-7,%be%f9%be
* data:text/css,.picture%20%7B%20background%3A%20none%3B%20%7D
* data:application/x-unknown,ERROR
*
* @author jccollet
*/
final class DataURLConnection extends URLConnection {
private static final Charset US_ASCII = Charset.forName("US-ASCII");
private final String mediaType;
private final byte[] data;
private final InputStream inputStream;
DataURLConnection(URL url) throws IOException {
super(url);
String content = url.toString();
content = content.substring(content.indexOf(':') + 1);
int commaPosition = content.indexOf(',');
if (commaPosition < 0) {
throw new ProtocolException(
"Invalid URL, ',' not found in: " + getURL());
}
String metadata = content.substring(0, commaPosition);
String dataString = content.substring(commaPosition + 1);
String mimeType = null;
LinkedList<String> parameters = new LinkedList<String>();
Charset charset = null;
boolean base64 = false;
String[] components = metadata.split(";", -1);
for (int i = 0; i < components.length; i++) {
String component = components[i];
if (component.equalsIgnoreCase("base64")) {
base64 = true;
} else {
if (i == 0 && !component.contains("=")) {
mimeType = component;
} else {
parameters.add(component);
if (component.toLowerCase().startsWith("charset=")) {
try {
charset = Charset.forName(component.substring(8));
} catch (IllegalArgumentException ex) {
UnsupportedEncodingException ex2 =
new UnsupportedEncodingException();
ex2.initCause(ex);
throw ex2;
}
}
}
}
}
if (mimeType == null || mimeType.isEmpty()) {
mimeType = "text/plain";
}
if (charset == null) {
charset = US_ASCII;
if (mimeType.toLowerCase().startsWith("text/")) {
parameters.addFirst("charset=" + charset.name());
}
}
StringBuilder mediaTypeBuilder = new StringBuilder();
mediaTypeBuilder.append(mimeType);
for (String parameter : parameters) {
mediaTypeBuilder.append(';').append(parameter);
}
mediaType = mediaTypeBuilder.toString();
if (base64) {
String s = urlDecode(dataString, US_ASCII);
s = s.replaceAll("\\s+", "");
data = Base64.getMimeDecoder().decode(s);
} else {
String s = urlDecode(dataString, charset);
data = s.getBytes(charset);
}
inputStream = new ByteArrayInputStream(data);
}
@Override
public void connect() {
connected = true;
}
@Override
public InputStream getInputStream() {
return inputStream;
}
@Override
public String getContentType() {
return mediaType;
}
@Override
public String getContentEncoding() {
return null;
}
@Override
public int getContentLength() {
// -1 means 'unknown length'
return data != null ? data.length : -1;
}
private static String urlDecode(String str, Charset charset) {
int length = str.length();
StringBuilder sb = new StringBuilder(length);
byte[] bytes = null;
int i = 0;
while (i < length) {
char c = str.charAt(i);
if (c == '%') {
// parse the series of contiguous %xy sequences
if (bytes == null) {
bytes = new byte[(length - i) / 3];
}
int count = 0;
int proceedTo = i;
for ( ; i < length; i += 3) {
c = str.charAt(i);
if (c != '%') {
// end of the series
break;
}
if (i + 2 >= length) {
// not enough characters to complete the current
// %xy sequence: terminate the series and append
// the remaining characters without decoding
proceedTo = length;
break;
}
byte b;
try {
b = (byte) Integer.parseInt(
str.substring(i + 1, i + 3), 16);
} catch(NumberFormatException ex) {
// illegal %xy sequence: terminate the series
// and append the %xy sequence without decoding
proceedTo = i + 3;
break;
}
bytes[count++] = b;
}
if (count > 0) {
sb.append(new String(bytes, 0, count, charset));
}
while (i < proceedTo) {
sb.append(str.charAt(i++));
}
} else {
sb.append(c);
i++;
}
}
return sb.toString();
}
}