private boolean exitAfterHandler(
      Channel channel,
      NettyResponseFuture<?> future,
      HttpResponse response,
      AsyncHandler<?> handler,
      NettyResponseStatus status,
      HttpRequest httpRequest,
      HttpResponseHeaders responseHeaders)
      throws IOException, Exception {

    boolean exit =
        exitAfterHandlingStatus(channel, future, response, handler, status, httpRequest)
            || //
            exitAfterHandlingHeaders(
                channel, future, response, handler, responseHeaders, httpRequest)
            || //
            exitAfterHandlingReactiveStreams(channel, future, response, handler, httpRequest);

    if (exit)
      finishUpdate(
          future,
          channel,
          HttpHeaders.isTransferEncodingChunked(httpRequest)
              || HttpHeaders.isTransferEncodingChunked(response));

    return exit;
  }
 private boolean handleHanderAndExit(
     Channel channel,
     NettyResponseFuture<?> future,
     AsyncHandler<?> handler,
     HttpResponseStatus status,
     HttpResponseHeaders responseHeaders,
     HttpResponse response)
     throws Exception {
   if (!future.getAndSetStatusReceived(true)
       && (handler.onStatusReceived(status) != STATE.CONTINUE
           || handler.onHeadersReceived(responseHeaders) != STATE.CONTINUE)) {
     finishUpdate(future, channel, HttpHeaders.isTransferEncodingChunked(response));
     return true;
   }
   return false;
 }
  @Override
  public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
    if (msg instanceof HttpResponse) {
      HttpResponse response = (HttpResponse) msg;

      System.out.println("STATUS: " + response.getStatus());
      System.out.println("VERSION: " + response.getProtocolVersion());
      System.out.println();

      if (!response.headers().isEmpty()) {
        for (String name : response.headers().names()) {
          for (String value : response.headers().getAll(name)) {
            System.out.println("HEADER: " + name + " = " + value);
          }
        }
        System.out.println();
      }

      if (HttpHeaders.isTransferEncodingChunked(response)) {
        System.out.println("CHUNKED CONTENT {");
      } else {
        System.out.println("CONTENT {");
      }
    }
    if (msg instanceof HttpContent) {
      HttpContent content = (HttpContent) msg;

      System.out.print(content.content().toString(CharsetUtil.UTF_8));
      System.out.flush();

      if (content instanceof LastHttpContent) {
        System.out.println("} END OF CONTENT");
        queue.add(ctx.channel().newSucceededFuture());
      }
    }
  }
  @Override
  protected void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {
    ByteBuf buf = null;
    if (msg instanceof HttpMessage) {
      if (state != ST_INIT) {
        throw new IllegalStateException(
            "unexpected message type: " + StringUtil.simpleClassName(msg));
      }

      @SuppressWarnings({"unchecked", "CastConflictsWithInstanceof"})
      H m = (H) msg;

      buf = ctx.alloc().buffer();
      // Encode the message.
      encodeInitialLine(buf, m);
      HttpHeaders.encode(m.headers(), buf);
      buf.writeBytes(CRLF);
      state = HttpHeaders.isTransferEncodingChunked(m) ? ST_CONTENT_CHUNK : ST_CONTENT_NON_CHUNK;
    }
    if (msg instanceof HttpContent || msg instanceof ByteBuf || msg instanceof FileRegion) {
      if (state == ST_INIT) {
        throw new IllegalStateException(
            "unexpected message type: " + StringUtil.simpleClassName(msg));
      }

      int contentLength = contentLength(msg);
      if (state == ST_CONTENT_NON_CHUNK) {
        if (contentLength > 0) {
          if (buf != null && buf.writableBytes() >= contentLength && msg instanceof HttpContent) {
            // merge into other buffer for performance reasons
            buf.writeBytes(((HttpContent) msg).content());
            out.add(buf);
          } else {
            if (buf != null) {
              out.add(buf);
            }
            out.add(encodeAndRetain(msg));
          }
        } else {
          if (buf != null) {
            out.add(buf);
          } else {
            // Need to produce some output otherwise an
            // IllegalStateException will be thrown
            out.add(EMPTY_BUFFER);
          }
        }

        if (msg instanceof LastHttpContent) {
          state = ST_INIT;
        }
      } else if (state == ST_CONTENT_CHUNK) {
        if (buf != null) {
          out.add(buf);
        }
        encodeChunkedContent(ctx, msg, contentLength, out);
      } else {
        throw new Error();
      }
    } else {
      if (buf != null) {
        out.add(buf);
      }
    }
  }
  private boolean exitAfterHandling407( //
      Channel channel, //
      NettyResponseFuture<?> future, //
      HttpResponse response, //
      Request request, //
      int statusCode, //
      ProxyServer proxyServer, //
      HttpRequest httpRequest) {

    if (future.getInProxyAuth().getAndSet(true)) {
      logger.info("Can't handle 407 as auth was already performed");
      return false;
    }

    Realm proxyRealm = future.getProxyRealm();

    if (proxyRealm == null) {
      logger.info("Can't handle 407 as there's no proxyRealm");
      return false;
    }

    List<String> proxyAuthHeaders = response.headers().getAll(HttpHeaders.Names.PROXY_AUTHENTICATE);

    if (proxyAuthHeaders.isEmpty()) {
      logger.info("Can't handle 407 as response doesn't contain Proxy-Authenticate headers");
      return false;
    }

    // FIXME what's this???
    future.setChannelState(ChannelState.NEW);
    HttpHeaders requestHeaders = new DefaultHttpHeaders(false).add(request.getHeaders());

    switch (proxyRealm.getScheme()) {
      case BASIC:
        if (getHeaderWithPrefix(proxyAuthHeaders, "Basic") == null) {
          logger.info(
              "Can't handle 407 with Basic realm as Proxy-Authenticate headers don't match");
          return false;
        }

        if (proxyRealm.isUsePreemptiveAuth()) {
          // FIXME do we need this, as future.getAndSetAuth
          // was tested above?
          // auth was already performed, most likely auth
          // failed
          logger.info(
              "Can't handle 407 with Basic realm as auth was preemptive and already performed");
          return false;
        }

        // FIXME do we want to update the realm, or directly
        // set the header?
        Realm newBasicRealm =
            realm(proxyRealm) //
                .setUsePreemptiveAuth(true) //
                .build();
        future.setProxyRealm(newBasicRealm);
        break;

      case DIGEST:
        String digestHeader = getHeaderWithPrefix(proxyAuthHeaders, "Digest");
        if (digestHeader == null) {
          logger.info(
              "Can't handle 407 with Digest realm as Proxy-Authenticate headers don't match");
          return false;
        }
        Realm newDigestRealm =
            realm(proxyRealm) //
                .setUri(request.getUri()) //
                .setMethodName(request.getMethod()) //
                .setUsePreemptiveAuth(true) //
                .parseProxyAuthenticateHeader(digestHeader) //
                .build();
        future.setProxyRealm(newDigestRealm);
        break;

      case NTLM:
        String ntlmHeader = getHeaderWithPrefix(proxyAuthHeaders, "NTLM");
        if (ntlmHeader == null) {
          logger.info("Can't handle 407 with NTLM realm as Proxy-Authenticate headers don't match");
          return false;
        }
        ntlmProxyChallenge(ntlmHeader, request, proxyRealm, requestHeaders, future);
        Realm newNtlmRealm =
            realm(proxyRealm) //
                .setUsePreemptiveAuth(true) //
                .build();
        future.setProxyRealm(newNtlmRealm);
        break;

      case KERBEROS:
      case SPNEGO:
        if (getHeaderWithPrefix(proxyAuthHeaders, "Negociate") == null) {
          logger.info(
              "Can't handle 407 with Kerberos or Spnego realm as Proxy-Authenticate headers don't match");
          return false;
        }
        try {
          kerberosProxyChallenge(
              channel, proxyAuthHeaders, request, proxyServer, proxyRealm, requestHeaders, future);

        } catch (SpnegoEngineException e) {
          // FIXME
          String ntlmHeader2 = getHeaderWithPrefix(proxyAuthHeaders, "NTLM");
          if (ntlmHeader2 != null) {
            logger.warn("Kerberos/Spnego proxy auth failed, proceeding with NTLM");
            ntlmChallenge(ntlmHeader2, request, requestHeaders, proxyRealm, future);
            Realm newNtlmRealm2 =
                realm(proxyRealm) //
                    .setScheme(AuthScheme.NTLM) //
                    .setUsePreemptiveAuth(true) //
                    .build();
            future.setProxyRealm(newNtlmRealm2);
          } else {
            requestSender.abort(channel, future, e);
            return false;
          }
        }
        break;
      default:
        throw new IllegalStateException("Invalid Authentication scheme " + proxyRealm.getScheme());
    }

    RequestBuilder nextRequestBuilder =
        new RequestBuilder(future.getCurrentRequest()).setHeaders(requestHeaders);
    if (future.getCurrentRequest().getUri().isSecured()) {
      nextRequestBuilder.setMethod(CONNECT);
    }
    final Request nextRequest = nextRequestBuilder.build();

    logger.debug("Sending proxy authentication to {}", request.getUri());
    if (future.isKeepAlive() //
        && HttpHeaders.isKeepAlive(httpRequest) //
        && HttpHeaders.isKeepAlive(response) //
        // support broken Proxy-Connection
        && !response.headers().contains("Proxy-Connection", HttpHeaders.Values.CLOSE, true) //
        && !HttpHeaders.isTransferEncodingChunked(httpRequest) //
        && !HttpHeaders.isTransferEncodingChunked(response)) {
      future.setConnectAllowed(true);
      future.setReuseChannel(true);
      requestSender.drainChannelAndExecuteNextRequest(channel, future, nextRequest);
    } else {
      channelManager.closeChannel(channel);
      requestSender.sendNextRequest(nextRequest, future);
    }

    return true;
  }
  private boolean handleUnauthorizedAndExit(
      int statusCode,
      Realm realm,
      final Request request,
      HttpResponse response,
      final NettyResponseFuture<?> future,
      ProxyServer proxyServer,
      final Channel channel)
      throws Exception {
    if (statusCode == UNAUTHORIZED.code() && realm != null) {

      List<String> authenticateHeaders =
          response.headers().getAll(HttpHeaders.Names.WWW_AUTHENTICATE);

      if (!authenticateHeaders.isEmpty() && !future.getAndSetAuth(true)) {
        future.setState(NettyResponseFuture.STATE.NEW);
        Realm newRealm = null;
        // NTLM
        boolean negociate = authenticateHeaders.contains("Negotiate");
        if (!authenticateHeaders.contains("Kerberos")
            && (isNTLM(authenticateHeaders) || negociate)) {
          newRealm =
              ntlmChallenge(
                  authenticateHeaders, request, proxyServer, request.getHeaders(), realm, future);
          // SPNEGO KERBEROS
        } else if (negociate) {
          newRealm =
              kerberosChallenge(
                  authenticateHeaders, request, proxyServer, request.getHeaders(), realm, future);
          if (newRealm == null) {
            return true;
          }
        } else {
          newRealm =
              new Realm.RealmBuilder()
                  .clone(realm)
                  .setScheme(realm.getAuthScheme())
                  .setUri(request.getURI().getPath())
                  .setMethodName(request.getMethod())
                  .setUsePreemptiveAuth(true)
                  .parseWWWAuthenticateHeader(authenticateHeaders.get(0))
                  .build();
        }

        Realm nr =
            new Realm.RealmBuilder()
                .clone(newRealm)
                .setUri(URI.create(request.getUrl()).getPath())
                .build();
        final Request nextRequest =
            new RequestBuilder(future.getRequest())
                .setHeaders(request.getHeaders())
                .setRealm(nr)
                .build();

        LOGGER.debug("Sending authentication to {}", request.getUrl());
        Callback callback =
            new Callback(future) {
              public void call() throws Exception {
                channels.drainChannel(channel, future);
                requestSender.sendNextRequest(nextRequest, future);
              }
            };

        if (future.isKeepAlive() && HttpHeaders.isTransferEncodingChunked(response)) {
          // We must make sure there is no bytes left
          // before executing the next request.
          Channels.setDefaultAttribute(channel, callback);
        } else {
          callback.call();
        }

        return true;
      }
    }

    return false;
  }
  @Override
  public void messageReceived(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
    if (msg instanceof HttpRequest) {
      HttpRequest request = this.request = (HttpRequest) msg;
      URI uri = new URI(request.getUri());
      if (!uri.getPath().startsWith("/form")) {
        // Write Menu
        writeMenu(ctx);
        return;
      }
      responseContent.setLength(0);
      responseContent.append("WELCOME TO THE WILD WILD WEB SERVER\r\n");
      responseContent.append("===================================\r\n");

      responseContent.append("VERSION: " + request.getProtocolVersion().text() + "\r\n");

      responseContent.append("REQUEST_URI: " + request.getUri() + "\r\n\r\n");
      responseContent.append("\r\n\r\n");

      // new getMethod
      for (Entry<String, String> entry : request.headers()) {
        responseContent.append("HEADER: " + entry.getKey() + '=' + entry.getValue() + "\r\n");
      }
      responseContent.append("\r\n\r\n");

      // new getMethod
      Set<Cookie> cookies;
      String value = request.headers().get(COOKIE);
      if (value == null) {
        cookies = Collections.emptySet();
      } else {
        cookies = CookieDecoder.decode(value);
      }
      for (Cookie cookie : cookies) {
        responseContent.append("COOKIE: " + cookie + "\r\n");
      }
      responseContent.append("\r\n\r\n");

      QueryStringDecoder decoderQuery = new QueryStringDecoder(request.getUri());
      Map<String, List<String>> uriAttributes = decoderQuery.parameters();
      for (Entry<String, List<String>> attr : uriAttributes.entrySet()) {
        for (String attrVal : attr.getValue()) {
          responseContent.append("URI: " + attr.getKey() + '=' + attrVal + "\r\n");
        }
      }
      responseContent.append("\r\n\r\n");

      // if GET Method: should not try to create a HttpPostRequestDecoder
      if (request.getMethod().equals(HttpMethod.GET)) {
        // GET Method: should not try to create a HttpPostRequestDecoder
        // So stop here
        responseContent.append("\r\n\r\nEND OF GET CONTENT\r\n");
        writeResponse(ctx.channel());
        return;
      }
      try {
        decoder = new HttpPostRequestDecoder(factory, request);
      } catch (ErrorDataDecoderException e1) {
        e1.printStackTrace();
        responseContent.append(e1.getMessage());
        writeResponse(ctx.channel());
        ctx.channel().close();
        return;
      }

      readingChunks = HttpHeaders.isTransferEncodingChunked(request);
      responseContent.append("Is Chunked: " + readingChunks + "\r\n");
      responseContent.append("IsMultipart: " + decoder.isMultipart() + "\r\n");
      if (readingChunks) {
        // Chunk version
        responseContent.append("Chunks: ");
        readingChunks = true;
      }
    }

    // check if the decoder was constructed before
    // if not it handles the form get
    if (decoder != null) {
      if (msg instanceof HttpContent) {
        // New chunk is received
        HttpContent chunk = (HttpContent) msg;
        try {
          decoder.offer(chunk);
        } catch (ErrorDataDecoderException e1) {
          e1.printStackTrace();
          responseContent.append(e1.getMessage());
          writeResponse(ctx.channel());
          ctx.channel().close();
          return;
        }
        responseContent.append('o');
        // example of reading chunk by chunk (minimize memory usage due to
        // Factory)
        readHttpDataChunkByChunk();
        // example of reading only if at the end
        if (chunk instanceof LastHttpContent) {
          writeResponse(ctx.channel());
          readingChunks = false;

          reset();
        }
      }
    }
  }
Esempio n. 8
0
  @Override
  protected Object encode(ChannelHandlerContext ctx, HttpObject msg) throws Exception {

    if (msg instanceof HttpResponse && ((HttpResponse) msg).getStatus().code() == 100) {
      // 100-continue response must be passed through.
      return msg;
    }

    // handle the case of single complete message without content
    if (msg instanceof FullHttpMessage && !((FullHttpMessage) msg).data().isReadable()) {

      // Remove content encoding
      String acceptEncoding = acceptEncodingQueue.poll();
      if (acceptEncoding == null) {
        throw new IllegalStateException("cannot send more responses than requests");
      }

      return msg;
    }

    if (msg instanceof HttpMessage) {
      assert message == null;

      // check if this message is also of type HttpContent is such case just make a safe copy of the
      // headers
      // as the content will get handled later and this simplify the handling
      if (msg instanceof HttpContent) {
        if (msg instanceof HttpRequest) {
          HttpRequest req = (HttpRequest) msg;
          message = new DefaultHttpRequest(req.getProtocolVersion(), req.getMethod(), req.getUri());
          message.headers().set(req.headers());
        } else if (msg instanceof HttpResponse) {
          HttpResponse res = (HttpResponse) msg;
          message = new DefaultHttpResponse(res.getProtocolVersion(), res.getStatus());
          message.headers().set(res.headers());
        } else {
          return msg;
        }
      } else {
        message = (HttpMessage) msg;
      }

      cleanup();
    }

    if (msg instanceof HttpContent) {
      HttpContent c = (HttpContent) msg;

      if (!encodeStarted) {
        encodeStarted = true;
        HttpMessage message = this.message;
        HttpHeaders headers = message.headers();
        this.message = null;

        // Determine the content encoding.
        String acceptEncoding = acceptEncodingQueue.poll();
        if (acceptEncoding == null) {
          throw new IllegalStateException("cannot send more responses than requests");
        }

        Result result = beginEncode(message, c, acceptEncoding);

        if (result == null) {
          if (c instanceof LastHttpContent) {
            return new Object[] {message, new DefaultLastHttpContent(c.data().retain())};
          } else {
            return new Object[] {message, new DefaultHttpContent(c.data().retain())};
          }
        }

        encoder = result.contentEncoder();

        // Encode the content and remove or replace the existing headers
        // so that the message looks like a decoded message.
        headers.set(HttpHeaders.Names.CONTENT_ENCODING, result.targetContentEncoding());

        Object[] encoded = encodeContent(message, c);

        if (!HttpHeaders.isTransferEncodingChunked(message) && encoded.length == 3) {
          if (headers.contains(HttpHeaders.Names.CONTENT_LENGTH)) {
            long length =
                ((ByteBufHolder) encoded[1]).data().readableBytes()
                    + ((ByteBufHolder) encoded[2]).data().readableBytes();

            headers.set(HttpHeaders.Names.CONTENT_LENGTH, Long.toString(length));
          }
        }
        return encoded;
      }

      if (encoder != null) {
        return encodeContent(null, c);
      }

      c.retain();
      return c;
    }
    return null;
  }
  protected boolean exitAfterHandlingRedirect( //
      Channel channel, //
      NettyResponseFuture<?> future, //
      HttpResponse response, //
      Request request, //
      int statusCode)
      throws Exception {

    if (followRedirect(config, request) && REDIRECT_STATUSES.contains(statusCode)) {
      if (future.incrementAndGetCurrentRedirectCount() >= config.getMaxRedirects()) {
        throw new MaxRedirectException("Maximum redirect reached: " + config.getMaxRedirects());

      } else {
        // We must allow 401 handling again.
        future.getAndSetAuth(false);

        HttpHeaders responseHeaders = response.headers();
        String location = responseHeaders.get(HttpHeaders.Names.LOCATION);
        Uri uri = Uri.create(future.getUri(), location);

        if (!uri.equals(future.getUri())) {
          final RequestBuilder requestBuilder = new RequestBuilder(future.getRequest());

          if (!config.isRemoveQueryParamOnRedirect())
            requestBuilder.addQueryParams(future.getRequest().getQueryParams());

          // if we are to strictly handle 302, we should keep the original method (which browsers
          // don't)
          // 303 must force GET
          if ((statusCode == FOUND.code() && !config.isStrict302Handling())
              || statusCode == SEE_OTHER.code()) requestBuilder.setMethod("GET");

          // in case of a redirect from HTTP to HTTPS, future attributes might change
          final boolean initialConnectionKeepAlive = future.isKeepAlive();
          final String initialPoolKey = channelManager.getPartitionId(future);

          future.setUri(uri);
          String newUrl = uri.toUrl();
          if (request.getUri().getScheme().startsWith(WEBSOCKET)) {
            newUrl = newUrl.replaceFirst(HTTP, WEBSOCKET);
          }

          logger.debug("Redirecting to {}", newUrl);

          for (String cookieStr : responseHeaders.getAll(HttpHeaders.Names.SET_COOKIE)) {
            Cookie c = CookieDecoder.decode(cookieStr, timeConverter);
            if (c != null) requestBuilder.addOrReplaceCookie(c);
          }

          Callback callback =
              channelManager.newDrainCallback(
                  future, channel, initialConnectionKeepAlive, initialPoolKey);

          if (HttpHeaders.isTransferEncodingChunked(response)) {
            // We must make sure there is no bytes left before
            // executing the next request.
            // FIXME investigate this
            Channels.setAttribute(channel, callback);
          } else {
            // FIXME don't understand: this offers the connection to the pool, or even closes it,
            // while the
            // request has not been sent, right?
            callback.call();
          }

          Request redirectRequest = requestBuilder.setUrl(newUrl).build();
          // FIXME why not reuse the channel is same host?
          requestSender.sendNextRequest(redirectRequest, future);
          return true;
        }
      }
    }
    return false;
  }
Esempio n. 10
0
  @Override
  protected void encode(ChannelHandlerContext ctx, HttpObject msg, ByteBuf out) throws Exception {
    if (msg instanceof HttpMessage) {
      if (state != ST_INIT) {
        throw new IllegalStateException(
            "unexpected message type: " + msg.getClass().getSimpleName());
      }

      @SuppressWarnings({"unchecked", "CastConflictsWithInstanceof"})
      H m = (H) msg;

      // Encode the message.
      encodeInitialLine(out, m);
      encodeHeaders(out, m);
      out.writeByte(CR);
      out.writeByte(LF);

      state = HttpHeaders.isTransferEncodingChunked(m) ? ST_CONTENT_CHUNK : ST_CONTENT_NON_CHUNK;
    }

    if (msg instanceof HttpContent) {
      if (state == ST_INIT) {
        throw new IllegalStateException(
            "unexpected message type: " + msg.getClass().getSimpleName());
      }

      HttpContent chunk = (HttpContent) msg;
      ByteBuf content = chunk.content();
      int contentLength = content.readableBytes();

      if (state == ST_CONTENT_NON_CHUNK) {
        if (contentLength > 0) {
          out.writeBytes(content, content.readerIndex(), content.readableBytes());
        }

        if (chunk instanceof LastHttpContent) {
          state = ST_INIT;
        }
      } else if (state == ST_CONTENT_CHUNK) {
        if (contentLength > 0) {
          out.writeBytes(copiedBuffer(Integer.toHexString(contentLength), CharsetUtil.US_ASCII));
          out.writeByte(CR);
          out.writeByte(LF);
          out.writeBytes(content, content.readerIndex(), contentLength);
          out.writeByte(CR);
          out.writeByte(LF);
        }

        if (chunk instanceof LastHttpContent) {
          out.writeByte((byte) '0');
          out.writeByte(CR);
          out.writeByte(LF);
          encodeTrailingHeaders(out, (LastHttpContent) chunk);
          out.writeByte(CR);
          out.writeByte(LF);
          state = ST_INIT;
        }
      } else {
        throw new Error();
      }
    }
  }