コード例 #1
0
  /**
   * Check if the if-modified-since condition is satisfied.
   *
   * @return {@link HttpStatus} if the decision has been made and the response status has been
   *     defined, or <tt>null</tt> otherwise
   */
  private HttpStatus checkIfModifiedSince(
      final FileCacheEntry entry, final HttpRequestPacket request) throws IOException {
    try {
      final String reqModified = request.getHeader(Header.IfModifiedSince);
      if (reqModified != null) {
        // optimization - assume the String value sent in the
        // client's If-Modified-Since header is the same as what
        // was originally sent
        if (reqModified.equals(entry.lastModifiedHeader)) {
          return HttpStatus.NOT_MODIFIED_304;
        }
        long headerValue = convertToLong(reqModified);
        if (headerValue != -1) {
          long lastModified = entry.lastModified;
          // If an If-None-Match header has been specified,
          // If-Modified-Since is ignored.
          if ((request.getHeader(Header.IfNoneMatch) == null)
              && (lastModified - headerValue <= 1000)) {
            // The entity has not been modified since the date
            // specified by the client. This is not an error case.
            return HttpStatus.NOT_MODIFIED_304;
          }
        }
      }
    } catch (IllegalArgumentException illegalArgument) {
      notifyProbesError(this, illegalArgument);
    }

    return null;
  }
コード例 #2
0
  private void addGeneralHeaders(final Request request, final HttpRequestPacket requestPacket) {

    if (request.hasHeaders()) {
      final FluentCaseInsensitiveStringsMap map = request.getHeaders();
      for (final Map.Entry<String, List<String>> entry : map.entrySet()) {
        final String headerName = entry.getKey();
        final List<String> headerValues = entry.getValue();
        if (isNonEmpty(headerValues)) {
          for (int i = 0, len = headerValues.size(); i < len; i++) {
            requestPacket.addHeader(headerName, headerValues.get(i));
          }
        }
      }
    }

    final MimeHeaders headers = requestPacket.getHeaders();
    if (!headers.contains(Header.Connection)) {
      // final boolean canCache = context.provider.clientConfig.getAllowPoolingConnection();
      requestPacket.addHeader(Header.Connection, /*(canCache ? */ "keep-alive" /*: "close")*/);
    }

    if (!headers.contains(Header.Accept)) {
      requestPacket.addHeader(Header.Accept, "*/*");
    }

    if (!headers.contains(Header.UserAgent)) {
      requestPacket.addHeader(Header.UserAgent, config.getUserAgent());
    }
  }
コード例 #3
0
 private static void recycleRequestResponsePackets(
     final Connection c, final HttpResponsePacket response) {
   if (!Utils.isSpdyConnection(c)) {
     HttpRequestPacket request = response.getRequest();
     request.setExpectContent(false);
     response.recycle();
     request.recycle();
   }
 }
コード例 #4
0
      private HttpRequestPacket createRequest() {

        HttpRequestPacket.Builder builder = HttpRequestPacket.builder();
        builder.method("POST");
        builder.protocol("HTTP/1.1");
        builder.uri("/echo");
        builder.chunked(true);
        HttpRequestPacket packet = builder.build();
        packet.addHeader(Header.Host, HOST_HEADER_VALUE);
        return packet;
      }
コード例 #5
0
  /**
   * Notify registered {@link FileCacheProbe}s about the "entry missed" event.
   *
   * @param fileCache the <tt>FileCache</tt> event occurred on.
   * @param request HTTP request.
   */
  protected static void notifyProbesEntryMissed(
      final FileCache fileCache, final HttpRequestPacket request) {

    final FileCacheProbe[] probes = fileCache.monitoringConfig.getProbesUnsafe();
    if (probes != null && probes.length > 0) {
      for (FileCacheProbe probe : probes) {
        probe.onEntryMissedEvent(
            fileCache, request.getHeader(Header.Host), request.getRequestURI());
      }
    }
  }
コード例 #6
0
 private static void addHostHeader(
     final Request request, final URI uri, final HttpRequestPacket requestPacket) {
   String host = request.getVirtualHost();
   if (host != null) {
     requestPacket.addHeader(Header.Host, host);
   } else {
     if (uri.getPort() == -1) {
       requestPacket.addHeader(Header.Host, uri.getHost());
     } else {
       requestPacket.addHeader(Header.Host, uri.getHost() + ':' + uri.getPort());
     }
   }
 }
コード例 #7
0
  private static void addQueryString(final Request request, final HttpRequestPacket requestPacket) {

    final FluentStringsMap map = request.getQueryParams();
    if (isNonEmpty(map)) {
      StringBuilder sb = new StringBuilder(128);
      for (final Map.Entry<String, List<String>> entry : map.entrySet()) {
        final String name = entry.getKey();
        final List<String> values = entry.getValue();
        if (isNonEmpty(values)) {
          try {
            for (int i = 0, len = values.size(); i < len; i++) {
              final String value = values.get(i);
              if (isNonEmpty(value)) {
                sb.append(URLEncoder.encode(name, "UTF-8"))
                    .append('=')
                    .append(URLEncoder.encode(values.get(i), "UTF-8"))
                    .append('&');
              } else {
                sb.append(URLEncoder.encode(name, "UTF-8")).append('&');
              }
            }
          } catch (UnsupportedEncodingException ignored) {
          }
        }
      }
      sb.setLength(sb.length() - 1);
      String queryString = sb.toString();

      requestPacket.setQueryString(queryString);
    }
  }
コード例 #8
0
  /**
   * Check if the if-match condition is satisfied.
   *
   * @param request The servlet request we are processing
   * @param entry the FileCacheEntry to validate
   * @return {@link HttpStatus} if the decision has been made and the response status has been
   *     defined, or <tt>null</tt> otherwise
   */
  private HttpStatus checkIfMatch(final FileCacheEntry entry, final HttpRequestPacket request)
      throws IOException {

    String headerValue = request.getHeader(Header.IfMatch);
    if (headerValue != null) {
      if (headerValue.indexOf('*') == -1) {
        String eTag = entry.Etag;

        StringTokenizer commaTokenizer = new StringTokenizer(headerValue, ",");
        boolean conditionSatisfied = false;

        while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
          String currentToken = commaTokenizer.nextToken();
          if (currentToken.trim().equals(eTag)) {
            conditionSatisfied = true;
          }
        }

        // If none of the given ETags match, 412 Precondition failed is
        // sent back
        if (!conditionSatisfied) {
          return HttpStatus.PRECONDITION_FAILED_412;
        }
      }
    }

    return null;
  }
コード例 #9
0
  /**
   * Check if the if-unmodified-since condition is satisfied.
   *
   * @return {@link HttpStatus} if the decision has been made and the response status has been
   *     defined, or <tt>null</tt> otherwise
   */
  private HttpStatus checkIfUnmodifiedSince(
      final FileCacheEntry entry, final HttpRequestPacket request) throws IOException {

    try {
      long lastModified = entry.lastModified;
      String h = request.getHeader(Header.IfUnmodifiedSince);
      if (h != null) {
        // optimization - assume the String value sent in the
        // client's If-Unmodified-Since header is the same as what
        // was originally sent
        if (h.equals(entry.lastModifiedHeader)) {
          // The entity has not been modified since the date
          // specified by the client. This is not an error case.
          return HttpStatus.PRECONDITION_FAILED_412;
        }
        long headerValue = convertToLong(h);
        if (headerValue != -1) {
          if (headerValue - lastModified <= 1000) {
            // The entity has not been modified since the date
            // specified by the client. This is not an error case.
            return HttpStatus.PRECONDITION_FAILED_412;
          }
        }
      }
    } catch (IllegalArgumentException illegalArgument) {
      notifyProbesError(this, illegalArgument);
    }

    return null;
  }
コード例 #10
0
  /**
   * Returns {@link FileCacheEntry}. If {@link FileCacheEntry} has been found - this method also
   * sets correspondent {@link HttpResponsePacket} status code and reason phrase.
   */
  public FileCacheEntry get(final HttpRequestPacket request) {
    // It should be faster than calculating the key hash code
    if (cacheSize.get() == 0) return null;

    final LazyFileCacheKey key = LazyFileCacheKey.create(request);
    final FileCacheEntry entry = fileCacheMap.get(key);
    key.recycle();
    try {
      if (entry != null && entry != NULL_CACHE_ENTRY) {
        // determine if we need to send the cache entry bytes
        // to the user-agent
        final HttpStatus httpStatus = checkIfHeaders(entry, request);

        final boolean flushBody = (httpStatus == null);
        if (flushBody && entry.type == CacheType.TIMESTAMP) {
          return null; // this will cause control to be passed to the static handler
        }

        request.getResponse().setStatus(httpStatus != null ? httpStatus : HttpStatus.OK_200);

        notifyProbesEntryHit(this, entry);
        return entry;
      }

      notifyProbesEntryMissed(this, request);
    } catch (Exception e) {
      notifyProbesError(this, e);
      // If an unexpected exception occurs, try to serve the page
      // as if it wasn't in a cache.
      LOGGER.log(
          Level.WARNING, LogMessages.WARNING_GRIZZLY_HTTP_SERVER_FILECACHE_GENERAL_ERROR(), e);
    }

    return null;
  }
コード例 #11
0
  /**
   * Check if the if-none-match condition is satisfied.
   *
   * @return {@link HttpStatus} if the decision has been made and the response status has been
   *     defined, or <tt>null</tt> otherwise
   */
  private HttpStatus checkIfNoneMatch(final FileCacheEntry entry, final HttpRequestPacket request)
      throws IOException {

    String headerValue = request.getHeader(Header.IfNoneMatch);
    if (headerValue != null) {
      String eTag = entry.Etag;

      boolean conditionSatisfied = false;

      if (!headerValue.equals("*")) {

        StringTokenizer commaTokenizer = new StringTokenizer(headerValue, ",");

        while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
          String currentToken = commaTokenizer.nextToken();
          if (currentToken.trim().equals(eTag)) {
            conditionSatisfied = true;
          }
        }

      } else {
        conditionSatisfied = true;
      }
      if (conditionSatisfied) {

        // For GET and HEAD, we should respond with
        // 304 Not Modified.
        // For every other method, 412 Precondition Failed is sent
        // back.
        final Method method = request.getMethod();
        if (Method.GET.equals(method) || Method.HEAD.equals(method)) {
          return HttpStatus.NOT_MODIFIED_304;
        } else {
          return HttpStatus.PRECONDITION_FAILED_412;
        }
      }
    }

    return null;
  }
コード例 #12
0
  private void addCookies(final Request request, final HttpRequestPacket requestPacket) {

    final Collection<Cookie> cookies = request.getCookies();
    if (isNonEmpty(cookies)) {
      StringBuilder sb = new StringBuilder(128);
      org.glassfish.grizzly.http.Cookie[] gCookies =
          new org.glassfish.grizzly.http.Cookie[cookies.size()];
      convertCookies(cookies, gCookies);
      CookieSerializerUtils.serializeClientCookies(
          sb, false, config.isRfc6265CookieEncoding(), gCookies);
      requestPacket.addHeader(Header.Cookie, sb.toString());
    }
  }
コード例 #13
0
      @SuppressWarnings({"unchecked"})
      @Override
      public NextAction handleConnect(FilterChainContext ctx) throws IOException {
        System.out.println("\nClient connected!\n");

        HttpRequestPacket request = createRequest();
        System.out.println("Writing request:\n");
        System.out.println(request.toString());
        ctx.write(request); // write the request

        // for each of the content parts in CONTENT, wrap in a Buffer,
        // create the HttpContent to wrap the buffer and write the
        // content.
        MemoryManager mm = ctx.getMemoryManager();
        for (int i = 0, len = CONTENT.length; i < len; i++) {
          HttpContent.Builder contentBuilder = request.httpContentBuilder();
          Buffer b = Buffers.wrap(mm, CONTENT[i]);
          contentBuilder.content(b);
          HttpContent content = contentBuilder.build();
          System.out.printf("(Client writing: %s)\n", b.toStringContent());
          ctx.write(content);
          try {
            Thread.sleep(2000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }

        // since the request created by createRequest() is chunked,
        // we need to write the trailer to signify the end of the
        // POST data
        ctx.write(request.httpTrailerBuilder().build());

        System.out.println("\n");

        return ctx.getStopAction(); // discontinue filter chain execution
      }
コード例 #14
0
  @SuppressWarnings("unchecked")
  public boolean sendRequest(
      final FilterChainContext ctx, final Request request, final HttpRequestPacket requestPacket)
      throws IOException {

    boolean isWriteComplete = true;

    if (Utils.requestHasEntityBody(request)) {
      final HttpTxContext context = HttpTxContext.get(ctx);
      BodyHandler handler = bodyHandlerFactory.getBodyHandler(request);
      if (requestPacket.getHeaders().contains(Header.Expect)
          && requestPacket.getHeaders().getValue(1).equalsIgnoreCase("100-Continue")) {
        // We have to set the content-length now as the headers will be flushed
        // before the FileBodyHandler is invoked.  If we don't do it here, and
        // the user didn't explicitly set the length, then the transfer-encoding
        // will be chunked and zero-copy file transfer will not occur.
        final File f = request.getFile();
        if (f != null) {
          requestPacket.setContentLengthLong(f.length());
        }
        handler = new ExpectHandler(handler);
      }
      context.setBodyHandler(handler);
      if (logger.isDebugEnabled()) {
        logger.debug("REQUEST: {}", requestPacket);
      }
      isWriteComplete = handler.doHandle(ctx, request, requestPacket);
    } else {
      HttpContent content = HttpContent.builder(requestPacket).last(true).build();
      if (logger.isDebugEnabled()) {
        logger.debug("REQUEST: {}", requestPacket);
      }
      ctx.write(content, ctx.getTransportContext().getCompletionHandler());
    }

    return isWriteComplete;
  }
コード例 #15
0
ファイル: GrizzlyModProxy.java プロジェクト: joggienl/tyrus
  /**
   * Method invoked when a first massage (Assumed to be HTTP) is received. Normally this would be
   * HTTP CONNECT and this method processes it and opens a connection to the destination (the server
   * that the client wants to access).
   *
   * <p>This method can be overridden to provide a test-specific handling of the CONNECT method.
   */
  protected NextAction handleConnect(FilterChainContext ctx, HttpContent content) {
    System.out.println("Handle CONNECT start . . .");
    HttpHeader httpHeader = content.getHttpHeader();
    HttpRequestPacket requestPacket = (HttpRequestPacket) httpHeader.getHttpHeader();

    if (!requestPacket.getMethod().matchesMethod("CONNECT")) {
      System.out.println("Received method is not CONNECT");
      writeHttpResponse(ctx, 400);
      return ctx.getStopAction();
    }

    String destinationUri = requestPacket.getRequestURI();

    // We expect URI in form host:port, this is not flexible, but we use it only to test our clients
    int colonIdx = destinationUri.indexOf(':');

    if (colonIdx == -1) {
      System.out.println("Destination URI not in host:port format: " + destinationUri);
      writeHttpResponse(ctx, 400);
      return ctx.getStopAction();
    }

    String hostName = destinationUri.substring(0, colonIdx);
    String portStr = destinationUri.substring(colonIdx + 1);

    int port;
    try {
      port = Integer.parseInt(portStr);
    } catch (Throwable t) {
      System.out.println("Could not parse destination port: " + portStr);
      writeHttpResponse(ctx, 400);
      return ctx.getStopAction();
    }

    try {
      Socket tunnelSocket = new Socket(hostName, port);

      Connection grizzlyConnection = ctx.getConnection();
      tunnelSockets.set(grizzlyConnection, tunnelSocket);

      TunnelSocketReader tunnelSocketReader =
          new TunnelSocketReader(tunnelSocket, grizzlyConnection);
      executorService.submit(tunnelSocketReader::read);
    } catch (IOException e) {
      writeHttpResponse(ctx, 400);
      return ctx.getStopAction();
    }

    // Grizzly does not like CONNECT method and sets "keep alive" to false, if it is present
    // This hacks Grizzly, so it will keep the connection open
    HttpRequestPacket request = getHttpRequest(ctx);
    request.getResponse().getProcessingState().setKeepAlive(true);
    request.getResponse().setContentLength(0);
    request.setMethod("GET");
    // end of hack

    writeHttpResponse(ctx, 200);

    System.out.println("Connection to proxy established.");

    return ctx.getStopAction();
  }
コード例 #16
0
  /** Parse host. */
  static void parseHost(
      final DataChunk hostDC, final DataChunk serverNameDC, final HttpRequestPacket request) {

    if (hostDC == null) {
      // HTTP/1.0
      // Default is what the socket tells us. Overriden if a host is
      // found/parsed
      request.setServerPort(request.getLocalPort());
      serverNameDC.setString(request.getLocalName());
      return;
    }

    final BufferChunk valueBC = hostDC.getBufferChunk();
    final int valueS = valueBC.getStart();
    final int valueL = valueBC.getEnd() - valueS;
    int colonPos = -1;

    final Buffer valueB = valueBC.getBuffer();
    final boolean ipv6 = (valueB.get(valueS) == '[');
    boolean bracketClosed = false;
    for (int i = 0; i < valueL; i++) {
      final byte b = valueB.get(i + valueS);
      if (b == ']') {
        bracketClosed = true;
      } else if (b == ':') {
        if (!ipv6 || bracketClosed) {
          colonPos = i;
          break;
        }
      }
    }

    if (colonPos < 0) {
      if (!request.isSecure()) {
        // 80 - Default HTTTP port
        request.setServerPort(80);
      } else {
        // 443 - Default HTTPS port
        request.setServerPort(443);
      }
      serverNameDC.setBuffer(valueB, valueS, valueS + valueL);
    } else {
      serverNameDC.setBuffer(valueB, valueS, valueS + colonPos);

      int port = 0;
      int mult = 1;
      for (int i = valueL - 1; i > colonPos; i--) {
        int charValue = DEC[(int) valueB.get(i + valueS)];
        if (charValue == -1) {
          // Invalid character
          throw new IllegalStateException(
              String.format(
                  "Host header %s contained a non-decimal value in the port definition.",
                  hostDC.toString()));
        }
        port = port + (charValue * mult);
        mult = 10 * mult;
      }
      request.setServerPort(port);
    }
  }
コード例 #17
0
  /** Add a resource to the cache. */
  protected CacheResult add(
      final HttpRequestPacket request, final File cacheFile, final long lastModified) {

    final String requestURI = request.getRequestURI();

    if (requestURI == null) {
      return CacheResult.FAILED;
    }

    final String host = request.getHeader(Header.Host);
    final FileCacheKey key = new FileCacheKey(host, requestURI);
    if (fileCacheMap.putIfAbsent(key, NULL_CACHE_ENTRY) != null) {
      key.recycle();
      return CacheResult.FAILED_ENTRY_EXISTS;
    }

    final int size = cacheSize.incrementAndGet();
    // cache is full.
    if (size > getMaxCacheEntries()) {
      cacheSize.decrementAndGet();
      fileCacheMap.remove(key);
      key.recycle();
      return CacheResult.FAILED_CACHE_FULL;
    }

    final HttpResponsePacket response = request.getResponse();
    final MimeHeaders headers = response.getHeaders();

    final String contentType = response.getContentType();

    final FileCacheEntry entry;
    if (cacheFile != null) { // If we have a file - try to create File-aware cache resource
      entry = createEntry(cacheFile);
      entry.setCanBeCompressed(canBeCompressed(cacheFile, contentType));
    } else {
      entry = new FileCacheEntry(this);
      entry.type = CacheType.TIMESTAMP;
    }

    entry.key = key;
    entry.requestURI = requestURI;

    entry.lastModified = lastModified;
    entry.contentType = ContentType.newContentType(contentType);
    entry.xPoweredBy = headers.getHeader(Header.XPoweredBy);
    entry.date = headers.getHeader(Header.Date);
    entry.lastModifiedHeader = headers.getHeader(Header.LastModified);
    entry.host = host;
    entry.Etag = headers.getHeader(Header.ETag);
    entry.server = headers.getHeader(Header.Server);

    fileCacheMap.put(key, entry);

    notifyProbesEntryAdded(this, entry);

    final int secondsMaxAgeLocal = getSecondsMaxAge();
    if (secondsMaxAgeLocal > 0) {
      delayQueue.add(entry, secondsMaxAgeLocal, TimeUnit.SECONDS);
    }

    return ((entry.type == CacheType.TIMESTAMP)
        ? CacheResult.OK_CACHED_TIMESTAMP
        : CacheResult.OK_CACHED);
  }
コード例 #18
0
  private boolean sendAsGrizzlyRequest(
      final RequestInfoHolder requestInfoHolder, final FilterChainContext ctx) throws IOException {

    HttpTxContext httpTxContext = requestInfoHolder.getHttpTxContext();
    if (httpTxContext == null) {
      httpTxContext = HttpTxContext.create(requestInfoHolder);
    }

    if (checkProxyAuthFailure(ctx, httpTxContext)) {
      return true;
    }

    final Request request = httpTxContext.getRequest();
    final URI uri = request.isUseRawUrl() ? request.getRawURI() : request.getURI();
    boolean secure = Utils.isSecure(uri);

    // If the request is secure, check to see if an error occurred during
    // the handshake.  We have to do this here, as the error would occur
    // out of the scope of a HttpTxContext so there would be
    // no good way to communicate the problem to the caller.
    if (secure && checkHandshakeError(ctx, httpTxContext)) {
      return true;
    }

    if (isUpgradeRequest(httpTxContext.getHandler())
        && isWSRequest(httpTxContext.getRequestUrl())) {
      httpTxContext.setWSRequest(true);
      convertToUpgradeRequest(httpTxContext);
    }

    HttpRequestPacket requestPacket = requestCache.poll();
    if (requestPacket == null) {
      requestPacket = new HttpRequestPacketImpl();
    }
    requestPacket.setMethod(request.getMethod());
    requestPacket.setProtocol(Protocol.HTTP_1_1);

    // Special handling for CONNECT.
    if (Method.CONNECT.matchesMethod(request.getMethod())) {
      final int port = uri.getPort();
      requestPacket.setRequestURI(uri.getHost() + ':' + (port == -1 ? 443 : port));
    } else {
      requestPacket.setRequestURI(uri.getPath());
    }

    if (Utils.requestHasEntityBody(request)) {
      final long contentLength = request.getContentLength();
      if (contentLength >= 0) {
        requestPacket.setContentLengthLong(contentLength);
        requestPacket.setChunked(false);
      } else {
        requestPacket.setChunked(true);
      }
    }

    if (httpTxContext.isWSRequest()) {
      try {
        final URI wsURI = new URI(httpTxContext.getWsRequestURI());
        httpTxContext.setProtocolHandler(Version.RFC6455.createHandler(true));
        httpTxContext.setHandshake(httpTxContext.getProtocolHandler().createHandShake(wsURI));
        requestPacket =
            (HttpRequestPacket) httpTxContext.getHandshake().composeHeaders().getHttpHeader();
      } catch (URISyntaxException e) {
        throw new IllegalArgumentException("Invalid WS URI: " + httpTxContext.getWsRequestURI());
      }
    }

    requestPacket.setSecure(secure);
    addQueryString(request, requestPacket);
    addHostHeader(request, uri, requestPacket);
    addGeneralHeaders(request, requestPacket);
    addCookies(request, requestPacket);

    initTransferCompletionHandler(request, httpTxContext.getHandler());

    final HttpRequestPacket requestPacketLocal = requestPacket;
    FilterChainContext sendingCtx = ctx;

    if (secure) {
      // Check to see if the ProtocolNegotiator has given
      // us a different FilterChain to use.  If so, we need
      // use a different FilterChainContext when invoking sendRequest().
      sendingCtx = checkAndHandleFilterChainUpdate(ctx, sendingCtx);
    }
    final Connection c = ctx.getConnection();
    if (!Utils.isSpdyConnection(c)) {
      HttpContext.newInstance(ctx, c, c, c);
    } else {
      SpdySession session = SpdySession.get(c);
      final Lock lock = session.getNewClientStreamLock();
      try {
        lock.lock();
        SpdyStream stream =
            session.openStream(
                requestPacketLocal,
                session.getNextLocalStreamId(),
                0,
                0,
                0,
                false,
                !requestPacketLocal.isExpectContent());
        HttpContext.newInstance(ctx, stream, stream, stream);
      } finally {
        lock.unlock();
      }
    }
    HttpTxContext.set(ctx, httpTxContext);
    return sendRequest(sendingCtx, request, requestPacketLocal);
  }
コード例 #19
0
 @Override
 public void recycle() {
   super.recycle();
   processingState.recycle();
   requestCache.add(this);
 }
コード例 #20
0
 /* (non-Javadoc)
  * @see org.glassfish.grizzly.websockets.WebSocketApplication#isApplicationRequest(org.glassfish.grizzly.http.HttpRequestPacket)
  */
 @Override
 public boolean isApplicationRequest(HttpRequestPacket request) {
   LOGGER.debug("isApplicationRequest: {}", request);
   return request.getRequestURI().toString().endsWith("/logviewer/websocket");
 }