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