ko-ws-tyrus/src/test/java/org/apidesign/html/wstyrus/TyrusDynamicHTTP.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sun, 25 Aug 2013 14:40:16 +0200
changeset 260 23e2ad7e6d23
child 358 80702021b851
permissions -rw-r--r--
Tyrus based implementation of WebSockets for JDK7
     1 /**
     2  * HTML via Java(tm) Language Bindings
     3  * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, version 2 of the License.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details. apidesign.org
    13  * designates this particular file as subject to the
    14  * "Classpath" exception as provided by apidesign.org
    15  * in the License file that accompanied this code.
    16  *
    17  * You should have received a copy of the GNU General Public License
    18  * along with this program. Look for COPYING file in the top folder.
    19  * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    20  */
    21 package org.apidesign.html.wstyrus;
    22 
    23 import java.io.ByteArrayInputStream;
    24 import java.io.ByteArrayOutputStream;
    25 import java.io.IOException;
    26 import java.io.InputStream;
    27 import java.io.OutputStream;
    28 import java.io.Reader;
    29 import java.net.URI;
    30 import java.net.URISyntaxException;
    31 import java.util.ArrayList;
    32 import java.util.List;
    33 import java.util.logging.Level;
    34 import java.util.logging.Logger;
    35 import org.glassfish.grizzly.PortRange;
    36 import org.glassfish.grizzly.http.server.HttpHandler;
    37 import org.glassfish.grizzly.http.server.HttpServer;
    38 import org.glassfish.grizzly.http.server.NetworkListener;
    39 import org.glassfish.grizzly.http.server.Request;
    40 import org.glassfish.grizzly.http.server.Response;
    41 import org.glassfish.grizzly.http.server.ServerConfiguration;
    42 import org.glassfish.grizzly.websockets.WebSocket;
    43 import org.glassfish.grizzly.websockets.WebSocketAddOn;
    44 import org.glassfish.grizzly.websockets.WebSocketApplication;
    45 import org.glassfish.grizzly.websockets.WebSocketEngine;
    46 
    47 /**
    48  *
    49  * @author Jaroslav Tulach <jtulach@netbeans.org>
    50  */
    51 final class TyrusDynamicHTTP extends HttpHandler {
    52     private static int resourcesCount;
    53     private static List<Resource> resources;
    54     private static ServerConfiguration conf;
    55     private static HttpServer server;
    56     
    57     private TyrusDynamicHTTP() {
    58     }
    59     
    60     static URI initServer() throws Exception {
    61         server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
    62         final WebSocketAddOn addon = new WebSocketAddOn();
    63         for (NetworkListener listener : server.getListeners()) {
    64             listener.registerAddOn(addon);
    65         }        
    66         resources = new ArrayList<Resource>();
    67 
    68         conf = server.getServerConfiguration();
    69         final TyrusDynamicHTTP dh = new TyrusDynamicHTTP();
    70 
    71         conf.addHttpHandler(dh, "/");
    72         
    73         server.start();
    74 
    75         return pageURL("http", server, "/test.html");
    76     }
    77     
    78     @Override
    79     public void service(Request request, Response response) throws Exception {
    80         if ("/test.html".equals(request.getRequestURI())) {
    81             response.setContentType("text/html");
    82             final InputStream is = TyrusDynamicHTTP.class.getResourceAsStream("test.html");
    83             copyStream(is, response.getOutputStream(), null);
    84             return;
    85         }
    86         if ("/dynamic".equals(request.getRequestURI())) {
    87             String mimeType = request.getParameter("mimeType");
    88             List<String> params = new ArrayList<String>();
    89             boolean webSocket = false;
    90             for (int i = 0;; i++) {
    91                 String p = request.getParameter("param" + i);
    92                 if (p == null) {
    93                     break;
    94                 }
    95                 if ("protocol:ws".equals(p)) {
    96                     webSocket = true;
    97                     continue;
    98                 }
    99                 params.add(p);
   100             }
   101             final String cnt = request.getParameter("content");
   102             String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
   103             ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
   104             URI url;
   105             final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
   106             if (webSocket) {
   107                 url = registerWebSocket(res);
   108             } else {
   109                 url = registerResource(res);
   110             }
   111             response.getWriter().write(url.toString());
   112             response.getWriter().write("\n");
   113             return;
   114         }
   115 
   116         for (Resource r : resources) {
   117             if (r.httpPath.equals(request.getRequestURI())) {
   118                 response.setContentType(r.httpType);
   119                 r.httpContent.reset();
   120                 String[] params = null;
   121                 if (r.parameters.length != 0) {
   122                     params = new String[r.parameters.length];
   123                     for (int i = 0; i < r.parameters.length; i++) {
   124                         params[i] = request.getParameter(r.parameters[i]);
   125                         if (params[i] == null) {
   126                             if ("http.method".equals(r.parameters[i])) {
   127                                 params[i] = request.getMethod().toString();
   128                             } else if ("http.requestBody".equals(r.parameters[i])) {
   129                                 Reader rdr = request.getReader();
   130                                 StringBuilder sb = new StringBuilder();
   131                                 for (;;) {
   132                                     int ch = rdr.read();
   133                                     if (ch == -1) {
   134                                         break;
   135                                     }
   136                                     sb.append((char) ch);
   137                                 }
   138                                 params[i] = sb.toString();
   139                             }
   140                         }
   141                         if (params[i] == null) {
   142                             params[i] = "null";
   143                         }
   144                     }
   145                 }
   146 
   147                 copyStream(r.httpContent, response.getOutputStream(), null, params);
   148             }
   149         }
   150     }
   151     
   152     private URI registerWebSocket(Resource r) {
   153         WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
   154         return pageURL("ws", server, r.httpPath);
   155     }
   156 
   157     private URI registerResource(Resource r) {
   158         if (!resources.contains(r)) {
   159             resources.add(r);
   160             conf.addHttpHandler(this, r.httpPath);
   161         }
   162         return pageURL("http", server, r.httpPath);
   163     }
   164     
   165     private static URI pageURL(String proto, HttpServer server, final String page) {
   166         NetworkListener listener = server.getListeners().iterator().next();
   167         int port = listener.getPort();
   168         try {
   169             return new URI(proto + "://localhost:" + port + page);
   170         } catch (URISyntaxException ex) {
   171             throw new IllegalStateException(ex);
   172         }
   173     }
   174     
   175     static final class Resource {
   176 
   177         final InputStream httpContent;
   178         final String httpType;
   179         final String httpPath;
   180         final String[] parameters;
   181 
   182         Resource(InputStream httpContent, String httpType, String httpPath,
   183             String[] parameters) {
   184             httpContent.mark(Integer.MAX_VALUE);
   185             this.httpContent = httpContent;
   186             this.httpType = httpType;
   187             this.httpPath = httpPath;
   188             this.parameters = parameters;
   189         }
   190     }
   191 
   192     static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
   193         for (;;) {
   194             int ch = is.read();
   195             if (ch == -1) {
   196                 break;
   197             }
   198             if (ch == '$' && params.length > 0) {
   199                 int cnt = is.read() - '0';
   200                 if (baseURL != null && cnt == 'U' - '0') {
   201                     os.write(baseURL.getBytes("UTF-8"));
   202                 } else {
   203                     if (cnt >= 0 && cnt < params.length) {
   204                         os.write(params[cnt].getBytes("UTF-8"));
   205                     } else {
   206                         os.write('$');
   207                         os.write(cnt + '0');
   208                     }
   209                 }
   210             } else {
   211                 os.write(ch);
   212             }
   213         }
   214     }
   215     
   216     private static class WS extends WebSocketApplication {
   217         private final Resource r;
   218 
   219         private WS(Resource r) {
   220             this.r = r;
   221         }
   222 
   223         @Override
   224         public void onMessage(WebSocket socket, String text) {
   225             try {
   226                 r.httpContent.reset();
   227                 ByteArrayOutputStream out = new ByteArrayOutputStream();
   228                 copyStream(r.httpContent, out, null, text);
   229                 String s = new String(out.toByteArray(), "UTF-8");
   230                 socket.send(s);
   231             } catch (IOException ex) {
   232                 LOG.log(Level.WARNING, null, ex);
   233             }
   234         }
   235         private static final Logger LOG = Logger.getLogger(WS.class.getName());
   236         
   237     }
   238 }