jaroslav@260: /** jaroslav@358: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. jaroslav@260: * jaroslav@551: * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. jaroslav@260: * jaroslav@358: * Oracle and Java are registered trademarks of Oracle and/or its affiliates. jaroslav@358: * Other names may be trademarks of their respective owners. jaroslav@260: * jaroslav@358: * The contents of this file are subject to the terms of either the GNU jaroslav@358: * General Public License Version 2 only ("GPL") or the Common jaroslav@358: * Development and Distribution License("CDDL") (collectively, the jaroslav@358: * "License"). You may not use this file except in compliance with the jaroslav@358: * License. You can obtain a copy of the License at jaroslav@358: * http://www.netbeans.org/cddl-gplv2.html jaroslav@358: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the jaroslav@358: * specific language governing permissions and limitations under the jaroslav@358: * License. When distributing the software, include this License Header jaroslav@358: * Notice in each file and include the License file at jaroslav@358: * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this jaroslav@358: * particular file as subject to the "Classpath" exception as provided jaroslav@358: * by Oracle in the GPL Version 2 section of the License file that jaroslav@358: * accompanied this code. If applicable, add the following below the jaroslav@358: * License Header, with the fields enclosed by brackets [] replaced by jaroslav@358: * your own identifying information: jaroslav@358: * "Portions Copyrighted [year] [name of copyright owner]" jaroslav@358: * jaroslav@358: * Contributor(s): jaroslav@358: * jaroslav@358: * The Original Software is NetBeans. The Initial Developer of the Original jaroslav@551: * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved. jaroslav@358: * jaroslav@358: * If you wish your version of this file to be governed by only the CDDL jaroslav@358: * or only the GPL Version 2, indicate your decision by adding jaroslav@358: * "[Contributor] elects to include this software in this distribution jaroslav@358: * under the [CDDL or GPL Version 2] license." If you do not indicate a jaroslav@358: * single choice of license, a recipient has the option to distribute jaroslav@358: * your version of this file under either the CDDL, the GPL Version 2 or jaroslav@358: * to extend the choice of license to its licensees as provided above. jaroslav@358: * However, if you add GPL Version 2 code and therefore, elected the GPL jaroslav@358: * Version 2 license, then the option applies only if the new code is jaroslav@358: * made subject to such option by the copyright holder. jaroslav@260: */ jtulach@1057: package org.netbeans.html.xhr4j; jaroslav@260: jaroslav@260: import java.io.ByteArrayInputStream; jaroslav@260: import java.io.ByteArrayOutputStream; jaroslav@260: import java.io.IOException; jaroslav@260: import java.io.InputStream; jaroslav@260: import java.io.OutputStream; jaroslav@260: import java.io.Reader; jaroslav@260: import java.net.URI; jaroslav@260: import java.net.URISyntaxException; jaroslav@260: import java.util.ArrayList; jaroslav@260: import java.util.List; jaroslav@260: import java.util.logging.Level; jaroslav@260: import java.util.logging.Logger; jaroslav@260: import org.glassfish.grizzly.PortRange; jaroslav@260: import org.glassfish.grizzly.http.server.HttpHandler; jaroslav@260: import org.glassfish.grizzly.http.server.HttpServer; jaroslav@260: import org.glassfish.grizzly.http.server.NetworkListener; jaroslav@260: import org.glassfish.grizzly.http.server.Request; jaroslav@260: import org.glassfish.grizzly.http.server.Response; jaroslav@260: import org.glassfish.grizzly.http.server.ServerConfiguration; jaroslav@260: import org.glassfish.grizzly.websockets.WebSocket; jaroslav@260: import org.glassfish.grizzly.websockets.WebSocketAddOn; jaroslav@260: import org.glassfish.grizzly.websockets.WebSocketApplication; jaroslav@260: import org.glassfish.grizzly.websockets.WebSocketEngine; jaroslav@260: jaroslav@260: /** jaroslav@260: * jtulach@790: * @author Jaroslav Tulach jaroslav@260: */ jtulach@1057: final class JsonDynamicHTTP extends HttpHandler { jaroslav@260: private static int resourcesCount; jaroslav@260: private static List resources; jaroslav@260: private static ServerConfiguration conf; jaroslav@260: private static HttpServer server; jtulach@940: jtulach@1057: private JsonDynamicHTTP() { jaroslav@260: } jtulach@940: jaroslav@260: static URI initServer() throws Exception { jaroslav@260: server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535)); jaroslav@260: final WebSocketAddOn addon = new WebSocketAddOn(); jaroslav@260: for (NetworkListener listener : server.getListeners()) { jaroslav@260: listener.registerAddOn(addon); jtulach@940: } jaroslav@260: resources = new ArrayList(); jaroslav@260: jaroslav@260: conf = server.getServerConfiguration(); jtulach@1057: final JsonDynamicHTTP dh = new JsonDynamicHTTP(); jaroslav@260: jaroslav@260: conf.addHttpHandler(dh, "/"); jtulach@940: jaroslav@260: server.start(); jaroslav@260: jaroslav@260: return pageURL("http", server, "/test.html"); jaroslav@260: } jtulach@940: jaroslav@260: @Override jaroslav@260: public void service(Request request, Response response) throws Exception { jaroslav@260: if ("/test.html".equals(request.getRequestURI())) { jaroslav@260: response.setContentType("text/html"); jtulach@1057: final InputStream is = JsonDynamicHTTP.class.getResourceAsStream("test.html"); jaroslav@260: copyStream(is, response.getOutputStream(), null); jaroslav@260: return; jaroslav@260: } jaroslav@260: if ("/dynamic".equals(request.getRequestURI())) { jaroslav@260: String mimeType = request.getParameter("mimeType"); jaroslav@260: List params = new ArrayList(); jaroslav@260: boolean webSocket = false; jaroslav@260: for (int i = 0;; i++) { jaroslav@260: String p = request.getParameter("param" + i); jaroslav@260: if (p == null) { jaroslav@260: break; jaroslav@260: } jaroslav@260: if ("protocol:ws".equals(p)) { jaroslav@260: webSocket = true; jaroslav@260: continue; jaroslav@260: } jaroslav@260: params.add(p); jaroslav@260: } jaroslav@260: final String cnt = request.getParameter("content"); jaroslav@260: String mangle = cnt.replace("%20", " ").replace("%0A", "\n"); jaroslav@260: ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8")); jaroslav@260: URI url; jaroslav@260: final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()])); jaroslav@260: if (webSocket) { jaroslav@260: url = registerWebSocket(res); jaroslav@260: } else { jaroslav@260: url = registerResource(res); jaroslav@260: } jaroslav@260: response.getWriter().write(url.toString()); jaroslav@260: response.getWriter().write("\n"); jaroslav@260: return; jaroslav@260: } jaroslav@260: jaroslav@260: for (Resource r : resources) { jaroslav@260: if (r.httpPath.equals(request.getRequestURI())) { jaroslav@260: response.setContentType(r.httpType); jaroslav@260: r.httpContent.reset(); jaroslav@260: String[] params = null; jaroslav@260: if (r.parameters.length != 0) { jaroslav@260: params = new String[r.parameters.length]; jaroslav@260: for (int i = 0; i < r.parameters.length; i++) { jaroslav@260: params[i] = request.getParameter(r.parameters[i]); jaroslav@260: if (params[i] == null) { jaroslav@260: if ("http.method".equals(r.parameters[i])) { jaroslav@260: params[i] = request.getMethod().toString(); jaroslav@260: } else if ("http.requestBody".equals(r.parameters[i])) { jaroslav@260: Reader rdr = request.getReader(); jaroslav@260: StringBuilder sb = new StringBuilder(); jaroslav@260: for (;;) { jaroslav@260: int ch = rdr.read(); jaroslav@260: if (ch == -1) { jaroslav@260: break; jaroslav@260: } jaroslav@260: sb.append((char) ch); jaroslav@260: } jaroslav@260: params[i] = sb.toString(); jtulach@940: } else if (r.parameters[i].startsWith("http.header.")) { jtulach@940: params[i] = request.getHeader(r.parameters[i].substring(12)); jaroslav@260: } jaroslav@260: } jaroslav@260: if (params[i] == null) { jaroslav@260: params[i] = "null"; jaroslav@260: } jaroslav@260: } jaroslav@260: } jaroslav@260: jaroslav@260: copyStream(r.httpContent, response.getOutputStream(), null, params); jaroslav@260: } jaroslav@260: } jaroslav@260: } jtulach@940: jaroslav@260: private URI registerWebSocket(Resource r) { jaroslav@260: WebSocketEngine.getEngine().register("", r.httpPath, new WS(r)); jaroslav@260: return pageURL("ws", server, r.httpPath); jaroslav@260: } jaroslav@260: jaroslav@260: private URI registerResource(Resource r) { jaroslav@260: if (!resources.contains(r)) { jaroslav@260: resources.add(r); jaroslav@260: conf.addHttpHandler(this, r.httpPath); jaroslav@260: } jaroslav@260: return pageURL("http", server, r.httpPath); jaroslav@260: } jtulach@940: jaroslav@260: private static URI pageURL(String proto, HttpServer server, final String page) { jaroslav@260: NetworkListener listener = server.getListeners().iterator().next(); jaroslav@260: int port = listener.getPort(); jaroslav@260: try { jaroslav@260: return new URI(proto + "://localhost:" + port + page); jaroslav@260: } catch (URISyntaxException ex) { jaroslav@260: throw new IllegalStateException(ex); jaroslav@260: } jaroslav@260: } jtulach@940: jaroslav@260: static final class Resource { jaroslav@260: jaroslav@260: final InputStream httpContent; jaroslav@260: final String httpType; jaroslav@260: final String httpPath; jaroslav@260: final String[] parameters; jaroslav@260: jaroslav@260: Resource(InputStream httpContent, String httpType, String httpPath, jaroslav@260: String[] parameters) { jaroslav@260: httpContent.mark(Integer.MAX_VALUE); jaroslav@260: this.httpContent = httpContent; jaroslav@260: this.httpType = httpType; jaroslav@260: this.httpPath = httpPath; jaroslav@260: this.parameters = parameters; jaroslav@260: } jaroslav@260: } jaroslav@260: jaroslav@260: static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException { jaroslav@260: for (;;) { jaroslav@260: int ch = is.read(); jaroslav@260: if (ch == -1) { jaroslav@260: break; jaroslav@260: } jaroslav@260: if (ch == '$' && params.length > 0) { jaroslav@260: int cnt = is.read() - '0'; jaroslav@260: if (baseURL != null && cnt == 'U' - '0') { jaroslav@260: os.write(baseURL.getBytes("UTF-8")); jaroslav@260: } else { jaroslav@260: if (cnt >= 0 && cnt < params.length) { jaroslav@260: os.write(params[cnt].getBytes("UTF-8")); jaroslav@260: } else { jaroslav@260: os.write('$'); jaroslav@260: os.write(cnt + '0'); jaroslav@260: } jaroslav@260: } jaroslav@260: } else { jaroslav@260: os.write(ch); jaroslav@260: } jaroslav@260: } jaroslav@260: } jtulach@940: jaroslav@260: private static class WS extends WebSocketApplication { jaroslav@260: private final Resource r; jaroslav@260: jaroslav@260: private WS(Resource r) { jaroslav@260: this.r = r; jaroslav@260: } jaroslav@260: jaroslav@260: @Override jaroslav@260: public void onMessage(WebSocket socket, String text) { jaroslav@260: try { jaroslav@260: r.httpContent.reset(); jaroslav@260: ByteArrayOutputStream out = new ByteArrayOutputStream(); jaroslav@260: copyStream(r.httpContent, out, null, text); jaroslav@260: String s = new String(out.toByteArray(), "UTF-8"); jaroslav@260: socket.send(s); jaroslav@260: } catch (IOException ex) { jaroslav@260: LOG.log(Level.WARNING, null, ex); jaroslav@260: } jaroslav@260: } jaroslav@260: private static final Logger LOG = Logger.getLogger(WS.class.getName()); jtulach@940: jaroslav@260: } jaroslav@260: }