blob: 727f27f323b51f9de1199ad69f2fc72467093dae [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 IBM Corporation and Others
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Hisashi MIYASHITA - initial API and implementation
* Kentarou FUKUDA - initial API and implementation
*******************************************************************************/
package org.eclipse.actf.model.flash.bridge.proxy;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.actf.model.flash.bridge.impl.WaXcodingConfig;
import org.eclipse.actf.util.httpproxy.core.IHTTPHeader;
import org.eclipse.actf.util.httpproxy.core.IHTTPRequestMessage;
import org.eclipse.actf.util.httpproxy.core.IHTTPResponseMessage;
import org.eclipse.actf.util.httpproxy.core.IHTTPResponsePushbackMessage;
import org.eclipse.actf.util.httpproxy.core.IPushbackMessageBody;
import org.eclipse.actf.util.httpproxy.proxy.IClientStateManager;
import org.eclipse.actf.util.httpproxy.proxy.IHTTPSessionOverrider;
import org.eclipse.actf.util.httpproxy.util.CacheMap;
import org.eclipse.actf.util.httpproxy.util.HTTPUtil;
import org.eclipse.actf.util.httpproxy.util.Logger;
import org.eclipse.actf.util.httpproxy.util.ParseURI;
import org.eclipse.actf.util.httpproxy.util.TimeoutException;
public class SWFBootloader implements IHTTPSessionOverrider {
private static final Logger LOGGER = Logger.getLogger(SWFBootloader.class);
private static byte[] bootLoaderSWF;
private boolean replacedFlag;
private boolean bootloaderRequestingFlag;
private IHTTPRequestMessage sessionRequest;
/* (non-Javadoc)
* @see org.eclipse.actf.util.httpproxy.proxy.IHTTPSessionOverrider#getSessionRequest()
*/
public IHTTPRequestMessage getSessionRequest() {
return sessionRequest;
}
private IHTTPResponseMessage sessionResponse;
/* (non-Javadoc)
* @see org.eclipse.actf.util.httpproxy.proxy.IHTTPSessionOverrider#getSessionResponse()
*/
public IHTTPResponseMessage getSessionResponse() {
return sessionResponse;
}
private final int id;
public SWFBootloader(int id) {
this.id = id;
}
public static void setSWFBootLoaderFile(InputStream is) {
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
int b;
try {
while (true) {
b = is.read();
if (b < 0) break;
os.write(b);
}
} catch (IOException e) {
return;
}
bootLoaderSWF = os.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
}
private static class CachedSlot {
public final String uri;
public IHTTPRequestMessage request;
public IHTTPResponseMessage response;
CachedSlot(String uri) {
this.uri = uri;
}
}
private CachedSlot sessionCachedSlot;
private static final int MAX_REFERER_LENGTH = 128;
private Object getCachedObject(IClientStateManager csm, String uri) {
CacheMap map = (CacheMap) csm.get(SWFBootloader.class);
if (map == null) {
map = new CacheMap(128, 20);
csm.put(SWFBootloader.class, map);
}
if (uri.length() > MAX_REFERER_LENGTH) {
synchronized (map) {
return map.matchStartsWith(uri);
}
} else {
synchronized (map) {
return map.get(uri);
}
}
}
private void cacheObject(IClientStateManager csm, String uri, Object cobj) {
Map map = (Map) csm.get(SWFBootloader.class);
if (map == null) {
map = new HashMap();
csm.put(SWFBootloader.class, map);
}
synchronized (map) {
map.put(uri, cobj);
}
}
private static IHTTPResponseMessage bootloaderResponseMessage(IHTTPRequestMessage request) {
IHTTPResponseMessage msg = HTTPUtil.createHTTPResponseInMemoryMessage(request.getSerial(),
IHTTPHeader.HTTP_VERSION_1_0_A,
"200".getBytes(),
"OK".getBytes(),
bootLoaderSWF);
msg.setHeader(IHTTPHeader.CACHE_CONTROL_A, "must-revalidate".getBytes());
msg.setHeader(IHTTPHeader.CONTENT_TYPE_A, SWFUtil.MIME_TYPE_APPLICATION_X_SHOCKWAVE_FLASH_A);
return msg;
}
/* (non-Javadoc)
* @see org.eclipse.actf.util.httpproxy.proxy.IHTTPSessionOverrider#replaceRequest(org.eclipse.actf.util.httpproxy.proxy.ClientStateManager, org.eclipse.actf.util.httpproxy.core.HTTPRequestMessage)
*/
public boolean replaceRequest(IClientStateManager csm,
IHTTPRequestMessage request)
throws IOException {
replacedFlag = false;
if (!WaXcodingConfig.getInstance().getSWFBootloaderFlag()) return false;
if (request.getHeader(SWFUtil.X_FLASH_VERSION_A) == null) return false;
String uriStr = request.getRequestURIString();
String method = request.getMethodAsString();
Object cobj = getCachedObject(csm, uriStr);
if (LOGGER.isDebugEnabled()) {
StringBuffer buf = new StringBuffer("method:");
buf.append(method);
buf.append(' ');
buf.append(uriStr);
List l = request.getHeaders();
Iterator it = l.iterator();
while (it.hasNext()) {
IHTTPHeader h = (IHTTPHeader) it.next();
if (!h.isRemoved()) {
buf.append(h.toString());
buf.append('\n');
}
}
DEBUG(buf.toString());
}
if (IHTTPHeader.METHOD_POST.equals(method)) {
if (cobj instanceof CachedSlot) {
ByteArrayOutputStream ob = new ByteArrayOutputStream();
try {
request.write(0, ob);
} catch (TimeoutException e) {
}
if (LOGGER.isDebugEnabled()) {
DEBUG("Bootloader's POST command:" + new String(ob.toByteArray()));
}
INFO("bootloader requests the original SWF:" + uriStr);
this.replacedFlag = true;
this.bootloaderRequestingFlag = true;
this.sessionCachedSlot = (CachedSlot) cobj;
this.sessionRequest = sessionCachedSlot.request;
this.sessionRequest.removeHeader("If-Modified-Since".getBytes());
this.sessionResponse = sessionCachedSlot.response;
return true;
}
}
IHTTPHeader referer = request.getHeader(IHTTPHeader.REFERER_A);
if (referer != null) {
String refStrBase;
String uriStrBase;
if (false) {
refStrBase = ParseURI.eliminateQuery(new String(referer.getValue()).trim());
uriStrBase = ParseURI.eliminateQuery(uriStr);
System.err.println("R: " + refStrBase);
System.err.println("U: " + uriStrBase);
} else {
refStrBase = new String(referer.getValue()).trim();
uriStrBase = uriStr;
}
Object o = getCachedObject(csm, refStrBase);
if (o != null) {
INFO(uriStrBase + " seems to be referred by " + refStrBase);
if (cobj == null) {
cacheObject(csm, uriStrBase, Boolean.valueOf(true));
}
return false;
}
}
// send bootloader swf
CachedSlot cs;
if (cobj instanceof CachedSlot){
cs = (CachedSlot) cobj;
} else {
cs = new CachedSlot(uriStr);
if (false) {
cacheObject(csm, ParseURI.eliminateQuery(uriStr), cs);
} else {
cacheObject(csm, uriStr, cs);
}
}
cs.request = request;
/*
INFO("bootloader is used for " + uriStr);
HTTPResponseMessage msg = bootloaderResponseMessage(request);
this.sessionResponse = msg;
*/
this.replacedFlag = true;
this.bootloaderRequestingFlag = false;
this.sessionCachedSlot = cs;
this.sessionRequest = request;
this.sessionRequest.removeHeader(IHTTPHeader.IF_MODIFIED_SINCE_A);
this.sessionRequest.removeHeader(IHTTPHeader.ACCEPT_ENCODING_A);
this.sessionResponse = null;
return true;
}
/* (non-Javadoc)
* @see org.eclipse.actf.util.httpproxy.proxy.IHTTPSessionOverrider#replaceResponse(org.eclipse.actf.util.httpproxy.proxy.ClientStateManager, org.eclipse.actf.util.httpproxy.core.HTTPRequestMessage, org.eclipse.actf.util.httpproxy.core.HTTPResponseMessage, int)
*/
public boolean replaceResponse(IClientStateManager csm,
IHTTPRequestMessage request,
IHTTPResponseMessage response,
int timeout) throws IOException, TimeoutException {
if (!replacedFlag) return false;
if (bootloaderRequestingFlag) {
if (response != sessionCachedSlot.response) {
response.setHeader(IHTTPHeader.CACHE_CONTROL_A, "no-cache".getBytes());
}
this.sessionResponse = response;
return true;
} else {
if (!SWFUtil.isPossiblySWFContentType(response)) return false;
IHTTPResponsePushbackMessage newResponse = HTTPUtil.createHTTPResponsePushbackMessage(response,
SWFUtil.FLASH_MAGIC_NUMBER_SIZE);
IPushbackMessageBody body = newResponse.getPushbackMessageBody();
if (body == null) return false;
PushbackInputStream bodyInputStream = body.getMessageBodyPushBackInputStream();
int version = SWFUtil.isSWF(bodyInputStream);
INFO("The incoming SWF is version " + version);
String uriStr = request.getRequestURIString();
if ((version >= WaXcodingConfig.getInstance().getSWFTranscodingMinimumVersion())
// Flash Version 9 or later is not supported yet.
&& (version < 9)) {
INFO("bootloader is used for " + uriStr);
IHTTPResponseMessage msg = bootloaderResponseMessage(request);
this.sessionResponse = msg;
ByteArrayOutputStream os = new ByteArrayOutputStream();
newResponse.writeBody(timeout, body, os);
IHTTPResponseMessage cachedResponse = HTTPUtil.createHTTPResponseInMemoryMessage(response,
os.toByteArray());
cachedResponse.setHeader(IHTTPHeader.CACHE_CONTROL_A, "no-cache,must-revalidate".getBytes());
cachedResponse.setHeader(IHTTPHeader.PRAGMA_A, "no-cache".getBytes());
cachedResponse.setHeader(IHTTPHeader.EXPIRES_A, "-1".getBytes());
INFO("The original response is cached for later use.");
if (LOGGER.isDebugEnabled()) {
StringBuffer buf = new StringBuffer("CachedMessage: ");
List l = cachedResponse.getHeaders();
Iterator it = l.iterator();
while (it.hasNext()) {
IHTTPHeader h = (IHTTPHeader) it.next();
if (!h.isRemoved()) {
buf.append(h.toString());
buf.append('\n');
}
}
DEBUG(buf.toString());
}
sessionCachedSlot.response = cachedResponse;
return true;
} else {
INFO("Due to the unsupported version, the bootloader is not applied to " + uriStr);
this.sessionResponse = newResponse;
return true;
}
}
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("[id:").append(id).append("] SWFBootloader");
return sb.toString();
}
private final void INFO(String msg) {
// StringBuffer sb = new StringBuffer();
// sb.append(this.toString()).append(": ").append(msg);
// LOGGER.info(sb.toString());
LOGGER.info(msg);
}
private final void DEBUG(String msg) {
// StringBuffer sb = new StringBuffer();
// sb.append(this.toString()).append(": ").append(msg);
// LOGGER.debug(sb.toString());
LOGGER.debug(msg);
}
}