jaroslav@260: /** jaroslav@260: * HTML via Java(tm) Language Bindings jaroslav@260: * Copyright (C) 2013 Jaroslav Tulach jaroslav@260: * jaroslav@260: * This program is free software: you can redistribute it and/or modify jaroslav@260: * it under the terms of the GNU General Public License as published by jaroslav@260: * the Free Software Foundation, version 2 of the License. jaroslav@260: * jaroslav@260: * This program is distributed in the hope that it will be useful, jaroslav@260: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@260: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@260: * GNU General Public License for more details. apidesign.org jaroslav@260: * designates this particular file as subject to the jaroslav@260: * "Classpath" exception as provided by apidesign.org jaroslav@260: * in the License file that accompanied this code. jaroslav@260: * jaroslav@260: * You should have received a copy of the GNU General Public License jaroslav@260: * along with this program. Look for COPYING file in the top folder. jaroslav@260: * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException jaroslav@260: */ jaroslav@260: package org.apidesign.html.wstyrus; 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: * jaroslav@260: * @author Jaroslav Tulach jaroslav@260: */ jaroslav@260: final class TyrusDynamicHTTP 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; jaroslav@260: jaroslav@260: private TyrusDynamicHTTP() { jaroslav@260: } jaroslav@260: 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); jaroslav@260: } jaroslav@260: resources = new ArrayList(); jaroslav@260: jaroslav@260: conf = server.getServerConfiguration(); jaroslav@260: final TyrusDynamicHTTP dh = new TyrusDynamicHTTP(); jaroslav@260: jaroslav@260: conf.addHttpHandler(dh, "/"); jaroslav@260: jaroslav@260: server.start(); jaroslav@260: jaroslav@260: return pageURL("http", server, "/test.html"); jaroslav@260: } jaroslav@260: 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"); jaroslav@260: final InputStream is = TyrusDynamicHTTP.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(); 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: } jaroslav@260: 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: } jaroslav@260: 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: } jaroslav@260: 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: } jaroslav@260: 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()); jaroslav@260: jaroslav@260: } jaroslav@260: }