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: }