public void handle()

in runtime/runtime_impl_jetty9/src/main/java/com/google/apphosting/runtime/jetty9/RpcConnection.java [145:316]


  public void handle(final AppVersionKey appVersionKey) throws ServletException, IOException {

    HttpRequest rpc = endPoint.getUpRequest().getRequest();
    final byte[] postdata = rpc.getPostdata().toByteArray();
    final CountDownLatch blockEndRequest = new CountDownLatch(1);

    HttpChannel channel =
        new HttpChannel(connector, connector.getHttpConfiguration(), endPoint, this) {
          @Override
          protected void handleException(Throwable th) {
            boolean requestWasCommitted = isCommitted();
            super.handleException(th);
            if (requestWasCommitted) {
              // The response was already committed before Jetty handled the exception.
              // In order to preserve Jetty 6 behavior we clear out the
              // attribute that holds the exception to avoid rethrowing it further.
              getRequest().removeAttribute(RequestDispatcher.ERROR_EXCEPTION);
            }
            blockEndRequest.countDown();
          }

          @Override
          protected String formatAddrOrHost(String addr) {
            return NORMALIZE_INET_ADDR ? super.formatAddrOrHost(addr) : addr;
          }

          @Override
          public void onCompleted() {
            super.onCompleted();
            blockEndRequest.countDown();
          }
        };

    Request request = channel.getRequest();

    // Enable async via a property
    request.setAsyncSupported(Boolean.getBoolean(ASYNC_ENABLE_PPROPERTY), null);

    // pretend to parse the request line

    // LEGACY_MODE is case insensitive for known methods
    HttpMethod method = LEGACY_MODE
            ? HttpMethod.INSENSITIVE_CACHE.get(rpc.getProtocol())
            : HttpMethod.CACHE.get(rpc.getProtocol());
    String methodS = method != null ? method.asString() : rpc.getProtocol();

    try {
      String url = rpc.getUrl();
      HttpURI uri = new HttpURI(url);

      HttpVersion version = HttpVersion.CACHE.getBest(rpc.getHttpVersion());
      MetaData.Request requestData =
          new MetaData.Request(
              methodS, uri, version, new HttpFields(), postdata == null ? -1 : postdata.length);

      // pretend to parse the header fields
      boolean contentLength = false;

      for (ParsedHttpHeader header : rpc.getHeadersList()) {
        HttpField field = getField(header);

        // Handle LegacyMode Headers
        if (LEGACY_MODE && field.getHeader() != null) {
          switch (field.getHeader()) {
            case CONTENT_ENCODING:
              continue;
            case CONTENT_LENGTH:
              if (contentLength) {
                throw new BadMessageException("Duplicate Content-Length");
              }
              contentLength = true;
              break;
            default:
              break;
          }
        }

        requestData.getFields().add(field);
      }
      // end of headers. This should return true to indicate that we are good to continue handling
      channel.onRequest(requestData);
      // is this SSL
      if (rpc.getIsHttps()) {
        // the following code has to be done after the channel.onRequest(requestData) call
        // to avoid NPE.
        request.setScheme(HttpScheme.HTTPS.asString());
        request.setSecure(true);
      }

      // signal the end of the request
      channel.onRequestComplete();

      // Give the input any post content.
      if (postdata != null) {
        channel.getRequest().getHttpInput().addContent(new Content(BufferUtil.toBuffer(postdata)));
      }
    } catch (Exception t) {
      // Any exception at this stage is most likely due to a bad message
      // We cannot use response.sendError as it needs a validly initiated channel to work.
      upResponse.setHttpResponseCodeAndResponse(400, "");
      channel.getResponse().setStatus(400);
      return;
    }

    // Tell AppVersionHandlerMap which app version should handle this
    // request.
    request.setAttribute(AppEngineConstants.APP_VERSION_KEY_REQUEST_ATTR, appVersionKey);

    final boolean skipAdmin = hasSkipAdminCheck(endPoint.getUpRequest());
    // Translate the X-Google-Internal-SkipAdminCheck to a servlet attribute.
    if (skipAdmin) {
      request.setAttribute(SKIP_ADMIN_CHECK_ATTR, true);

      // N.B.: If SkipAdminCheck is set, we're actually lying
      // to Jetty here to tell it that HTTPS is in use when it may not
      // be.  This is useful because we want to bypass Jetty's
      // transport-guarantee checks (to match Python, which bypasses
      // handler_security: for these requests), but unlike
      // authentication SecurityHandler does not provide an easy way to
      // plug in custom logic here.  I do not believe that our lie is
      // user-visible (ServletRequest.getProtocol() is unchanged).
      request.setSecure(true);
    }

    Throwable exception = null;
    try {
      // This will invoke a servlet and mutate upResponse before returning.
      channel.handle();
      waitforAsyncDone(blockEndRequest);

      // If an exception occurred while running GenericServlet.service,
      // this attribute will either be thrown or set as an attribute for the WebAppContext's
      // ErrorHandler to be invoked.
      exception = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
    } catch (Exception ex) {
      exception = ex;
    }

    // TODO(b/263341977) this is a correct behavior, but customers depend on this bug, so we
    // enable it only for non java8 runtimes.
    if ((exception == null)
        && (abortedError != null)
        && !"java8".equals(GAE_RUNTIME)) {
        exception = abortedError;
      }


    if (exception != null) {
      Throwable cause = unwrap(exception);
      if (cause instanceof BadMessageException) {
        // Jetty bad messages exceptions are handled here to prevent
        // 4xx client issues being signalled as 5xx server issues
        BadMessageException bme = (BadMessageException) cause;
        upResponse.clearHttpResponse();
        upResponse.setError(UPResponse.ERROR.OK_VALUE);
        upResponse.setHttpResponseCode(bme.getCode());
        upResponse.setErrorMessage(bme.getReason());
      } else if (!hasExceptionHandledByErrorPage(request)) {
        // We will most likely have set something here, but the
        // AppServer will only do the right thing (print stack traces
        // for admins, generic Prometheus error message others) if this
        // is completely unset.
        upResponse.clearHttpResponse();

        if (exception instanceof ServletException) {
          throw (ServletException) exception;
        } else {
          throw new ServletException(exception);
        }
      }
    }
  }