protected static String output(String type, String key, String str) { StringBuffer buffer = new StringBuffer(); if (SimplePipeRequest.PIPE_TYPE_SCRIPT.equals(type)) { // iframe, so $ is a safe method identifier buffer.append("<script type=\"text/javascript\">$ (\""); } else if (SimplePipeRequest.PIPE_TYPE_XSS.equals(type)) { buffer.append("$p1p3p$ (\""); // $p1p3p$ } buffer.append(key); if (SimplePipeRequest.PIPE_TYPE_SCRIPT.equals(type) || SimplePipeRequest.PIPE_TYPE_XSS.equals(type)) { str = str.replaceAll("\\\\", "\\\\\\\\") .replaceAll("\r", "\\\\r") .replaceAll("\n", "\\\\n") .replaceAll("\"", "\\\\\""); if (SimplePipeRequest.PIPE_TYPE_SCRIPT.equals(type)) { str = str.replaceAll("<\\/script>", "<\\/scr\" + \"ipt>"); } } buffer.append(str); if (SimplePipeRequest.PIPE_TYPE_SCRIPT.equals(type)) { // iframe buffer.append("\");</script>\r\n"); } else if (SimplePipeRequest.PIPE_TYPE_XSS.equals(type)) { buffer.append("\");\r\n"); } return buffer.toString(); }
/** * continuum, query, script, xss * * <p>type = continuum The connection will be kept open. * * <p>type = query The connection will not be kept. Each request stands for a query * * <p>type = script IFRAME object is used to simulate kept-open-connection * * <p>type = xss Cross Site Script pipe type, SimpleSerializable instances are wrapped in * JavaScript string. * * <p>type = notify Notify that client (browser) still keeps the pipe connection. */ protected void doPipe(final HttpServletResponse resp, String key, String type, String domain) throws IOException { PrintWriter writer = null; resp.setHeader("Pragma", "no-cache"); resp.setHeader("Cache-Control", "no-cache"); resp.setDateHeader("Expires", 0); if (SimplePipeRequest.PIPE_TYPE_NOTIFY.equals(type)) { /* * Client send in "notify" request to execute #notifyPipeStatus, see below comments */ boolean updated = SimplePipeHelper.notifyPipeStatus(key, true); // update it! resp.setContentType("text/javascript; charset=utf-8"); writer = resp.getWriter(); writer.write("$p1p3b$ (\""); // $p1p3b$ = net.sf.j2s.ajax.SimplePipeRequest.pipeNotifyCallBack writer.write(key); writer.write("\", \""); writer.write(updated ? SimplePipeRequest.PIPE_STATUS_OK : SimplePipeRequest.PIPE_STATUS_LOST); writer.write("\");"); return; } if (SimplePipeRequest.PIPE_TYPE_SUBDOMAIN_QUERY.equals(type)) { // subdomain query resp.setContentType("text/html; charset=utf-8"); writer = resp.getWriter(); StringBuffer buffer = new StringBuffer(); buffer.append("<html><head><title></title></head><body>\r\n"); buffer.append("<script type=\"text/javascript\">"); buffer.append("p = new Object ();\r\n"); buffer.append("p.key = \"" + key + "\";\r\n"); buffer.append("p.originalDomain = document.domain;\r\n"); buffer.append("document.domain = \"" + domain + "\";\r\n"); buffer.append("var securityErrors = 0\r\n"); buffer.append("var lazyOnload = function () {\r\n"); buffer.append("try {\r\n"); buffer.append("var spr = window.parent.net.sf.j2s.ajax.SimplePipeRequest;\r\n"); buffer.append("eval (\"(\" + spr.subdomainInit + \") (p);\");\r\n"); buffer.append("eval (\"((\" + spr.subdomainLoopQuery + \") (p)) ();\");\r\n"); buffer.append("} catch (e) {\r\n"); buffer.append("securityErrors++;\r\n"); buffer.append("if (securityErrors < 100) {\r\n"); // 10s buffer.append("window.setTimeout (lazyOnload, 100);\r\n"); buffer.append("};\r\n"); // end of if buffer.append("};\r\n"); // end of catch buffer.append("};\r\n"); // end of function buffer.append("window.onload = lazyOnload;\r\n"); buffer.append("</script>\r\n"); buffer.append("</body></html>\r\n"); writer.write(buffer.toString()); return; } boolean isContinuum = SimplePipeRequest.PIPE_TYPE_CONTINUUM.equals(type); if (isContinuum) { resp.setHeader("Transfer-Encoding", "chunked"); } boolean isScripting = SimplePipeRequest.PIPE_TYPE_SCRIPT.equals(type); if (isScripting) { // iframe resp.setContentType("text/html; charset=utf-8"); writer = resp.getWriter(); StringBuffer buffer = new StringBuffer(); buffer.append("<html><head><title></title></head><body>\r\n"); buffer.append("<script type=\"text/javascript\">"); if (domain != null) { buffer.append("document.domain = \"" + domain + "\";\r\n"); } else { buffer.append("document.domain = document.domain;\r\n"); } buffer.append( "function $ (s) { if (window.parent) window.parent.net.sf.j2s.ajax.SimplePipeRequest.parseReceived (s); }"); buffer.append( "if (window.parent) eval (\"(\" + window.parent.net.sf.j2s.ajax.SimplePipeRequest.checkIFrameSrc + \") ();\");\r\n"); buffer.append("</script>\r\n"); writer.write(buffer.toString()); writer.flush(); } else { if (SimplePipeRequest.PIPE_TYPE_QUERY.equals(type) || isContinuum) { resp.setContentType("text/plain; charset=utf-8"); } else { resp.setContentType("text/javascript; charset=utf-8"); } writer = resp.getWriter(); } long lastPipeDataWritten = -1; long beforeLoop = System.currentTimeMillis(); int items = 0; if (SimplePipeHelper.notifyPipeStatus(key, true)) { // update it! List<SimpleSerializable> list = null; int priority = 0; long lastLiveDetected = System.currentTimeMillis(); SimplePipeRunnable pipe = SimplePipeHelper.getPipe(key); long waitClosingInterval = pipe == null ? 5000 : pipe.pipeWaitClosingInterval(); while ((list = SimplePipeHelper.getPipeDataList(key)) != null /* && SimplePipeHelper.isPipeLive(key) */ // check it! && !writer.checkError()) { StringBuffer buffer = new StringBuffer(); synchronized (list) { int size = list.size(); if (size > 0) { boolean live = SimplePipeHelper.isPipeLive(key); for (int i = 0; i < size; i++) { SimpleSerializable ss = null; ss = list.remove(0); if (ss == null) break; // terminating signal if (ss instanceof ISimpleCacheable) { ISimpleCacheable sc = (ISimpleCacheable) ss; sc.setCached(false); } buffer.append(output(type, key, ss.serialize())); items++; if (live && pipeMaxItemsPerQuery > 0 && items >= pipeMaxItemsPerQuery && !isContinuum) { break; } lastPipeDataWritten = System.currentTimeMillis(); if (ss instanceof ISimplePipePriority) { ISimplePipePriority spp = (ISimplePipePriority) ss; int p = spp.getPriority(); if (p <= 0) { p = ISimplePipePriority.IMPORTANT; } priority += p; } else { priority += ISimplePipePriority.IMPORTANT; } } } } if (buffer.length() > 0) { writer.write(buffer.toString()); } writer.flush(); if (!SimplePipeHelper.isPipeLive(key)) { if (System.currentTimeMillis() - lastLiveDetected > waitClosingInterval) { // break out while loop so pipe connection will be closed break; } else { // sleep 1s and continue to check pipe status again try { Thread.sleep(1000); } catch (InterruptedException e) { } continue; } } else { lastLiveDetected = System.currentTimeMillis(); } // Client should send in "notify" request to simulate the following #notifyPipeStatus // SimplePipeHelper.notifyPipeStatus(key, true); long now = System.currentTimeMillis(); if ((lastPipeDataWritten == -1 && now - beforeLoop >= pipeQueryTimeout) || (lastPipeDataWritten > 0 && now - lastPipeDataWritten >= pipeQueryTimeout && (isContinuum || isScripting))) { writer.write(output(type, key, SimplePipeRequest.PIPE_STATUS_OK)); lastPipeDataWritten = System.currentTimeMillis(); } now = System.currentTimeMillis(); if (SimplePipeHelper.getPipeDataList(key) != null // may be broken down already!! && (pipeMaxItemsPerQuery <= 0 || items < pipeMaxItemsPerQuery || isContinuum) && (isContinuum || (isScripting && now - beforeLoop < pipeScriptBreakout) || (priority < ISimplePipePriority.IMPORTANT && now - beforeLoop < pipeQueryTimeout))) { synchronized (pipe) { try { pipe.wait(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } else { break; } } // end of while } // else pips is already closed or in other statuses if (SimplePipeHelper.getPipeDataList(key) == null || !SimplePipeHelper.isPipeLive(key)) { // pipe is tore down! // SimplePipeHelper.notifyPipeStatus(key, false); // Leave for pipe monitor to destroy it SimplePipeHelper.removePipe(key); try { writer.write(output(type, key, SimplePipeRequest.PIPE_STATUS_DESTROYED)); lastPipeDataWritten = System.currentTimeMillis(); } catch (Exception e) { // HTTP connection may be closed already! } } else if (isScripting && (System.currentTimeMillis() - beforeLoop >= pipeScriptBreakout || (pipeMaxItemsPerQuery > 0 && items >= pipeMaxItemsPerQuery))) { try { writer.write(output(type, key, SimplePipeRequest.PIPE_STATUS_CONTINUE)); lastPipeDataWritten = System.currentTimeMillis(); } catch (Exception e) { // HTTP connection may be closed already! } } if (lastPipeDataWritten == -1) { writer.write(output(type, key, SimplePipeRequest.PIPE_STATUS_OK)); } if (isScripting) { // iframe try { writer.write("</body></html>\r\n"); } catch (Exception e) { // HTTP connection may be closed already! } } }