/* * Copyright 2009 Richard Zschech. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License 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 net.zschech.gwt.comet.server.impl; import java.io.IOException; import java.io.OutputStream; import java.io.Serializable; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.zschech.gwt.comet.server.CometServlet; import com.google.gwt.rpc.server.ClientOracle; import com.google.gwt.user.server.rpc.SerializationPolicy; /** * The CometServletResponse for the {@link IEHTMLFileCometTransport} * * @author Richard Zschech */ public class IEHTMLFileCometServletResponse extends ManagedStreamCometServletResponseImpl { // IE requires padding to start processing the page. private static final int PADDING_REQUIRED = 256; private static final String HEAD = "<html><body onload='parent.d()'><script>"; private static final String MID = "parent.c("; private static final String TAIL = ");var m=parent.m;var h=parent.h;</script>"; private static final String PADDING_STRING; static { // the required padding minus the length of the heading int capacity = PADDING_REQUIRED - HEAD.length() - MID.length() - TAIL.length(); char[] padding = new char[capacity]; for (int i = 0; i < capacity; i++) { padding[i] = ' '; } PADDING_STRING = new String(padding); } private int clientMemory; public IEHTMLFileCometServletResponse(HttpServletRequest request, HttpServletResponse response, SerializationPolicy serializationPolicy, ClientOracle clientOracle, CometServlet servlet, AsyncServlet async, int heartbeat) { super(request, response, serializationPolicy, clientOracle, servlet, async, heartbeat); } @Override protected void setupHeaders(HttpServletResponse response) { super.setupHeaders(response); response.setContentType("text/html"); response.setCharacterEncoding("UTF-8"); } @Override protected OutputStream getOutputStream(OutputStream outputStream) { return setupCountOutputStream(outputStream); } @Override protected int getPaddingRequired() { return PADDING_REQUIRED; } @Override protected void doInitiate(int heartbeat) throws IOException { writer.append(HEAD); String domain = getRequest().getParameter("d"); if (domain != null) { writer.append("document.domain='"); writer.append(domain); writer.append("';"); } writer.append(MID); writer.append(Integer.toString(heartbeat)); writer.append(TAIL); } @Override protected CharSequence getPadding(int padding) { if (padding > PADDING_STRING.length()) { StringBuilder result = new StringBuilder(padding); for (int i = 0; i < padding; i++) { result.append(' '); } return result; } else { return PADDING_STRING.substring(0, padding); } } @Override protected void doSendError(int statusCode, String message) throws IOException { writer.append("<html><script>parent.e(").append(Integer.toString(statusCode)); if (message != null) { writer.append(",'").append(escapeString(message)).append('\''); } writer.append(")</script></html>"); } @Override protected void doWrite(List<? extends Serializable> messages) throws IOException { clientMemory *= 2; writer.append("<script>m("); boolean first = true; for (Serializable message : messages) { CharSequence string; if (message instanceof CharSequence) { string = "]" + escapeString((CharSequence) message); } else { string = escapeObject(serialize(message)); } if (first) { first = false; } else { writer.append(','); } writer.append('\'').append(string).append('\''); clientMemory += string.length() + 1; } writer.append(")</script>"); } @Override protected void doHeartbeat() throws IOException { writer.append("<script>h();</script>"); } @Override protected void doTerminate() throws IOException { writer.append("<script>parent.t();</script>"); } @Override protected void doRefresh() throws IOException { writer.append("<script>parent.r();</script>"); } @Override protected boolean isOverTerminateLength(int written) { // if(System.currentTimeMillis() - start < 1000) { // return false; // } return clientMemory > 1024 * 1024; // return written > 4 * 1024 * 1024; } private CharSequence escapeString(CharSequence string) { int length = string.length(); int i = 0; loop: while (i < length) { char ch = string.charAt(i); switch (ch) { case '\'': case '\\': case '/': case '\b': case '\f': case '\n': case '\r': break loop; } i++; } if (i == length) { return string; } StringBuilder str = new StringBuilder(string.length() * 2); str.append(string, 0, i); while (i < length) { char ch = string.charAt(i); switch (ch) { case '\'': str.append("\\\'"); break; case '\\': str.append("\\\\"); break; case '/': str.append("\\/"); break; case '\b': str.append("\\b"); break; case '\f': str.append("\\f"); break; case '\n': str.append("\\n"); break; case '\r': str.append("\\r"); break; case '\t': str.append("\\t"); break; default: str.append(ch); } i++; } return str; } private CharSequence escapeObject(CharSequence string) { int length = string.length(); int i = 0; loop: while (i < length) { char ch = string.charAt(i); switch (ch) { case '\'': case '\\': case '/': break loop; } i++; } if (i == length) { return string; } StringBuilder str = new StringBuilder(string.length() * 2); str.append(string, 0, i); while (i < length) { char ch = string.charAt(i); switch (ch) { case '\'': str.append("\\\'"); break; case '\\': str.append("\\\\"); break; case '/': str.append("\\/"); break; default: str.append(ch); } i++; } return str; } }