static void decodeRequest(
      final Buffer requestContent, final AjpHttpRequest req, final boolean tomcatAuthentication)
      throws IOException {
    // FORWARD_REQUEST handler

    int offset = requestContent.position();

    // Translate the HTTP method code to a String.
    byte methodCode = requestContent.get(offset++);
    if (methodCode != AjpConstants.SC_M_JK_STORED) {
      String mName = AjpConstants.methodTransArray[(int) methodCode - 1];
      req.getMethodDC().setString(mName);
    }

    offset = getBytesToDataChunk(requestContent, offset, req.getProtocolDC());
    final int requestURILen = readShort(requestContent, offset);
    if (!isNullLength(requestURILen)) {
      req.getRequestURIRef().init(requestContent, offset + 2, offset + 2 + requestURILen);
    }
    // Don't forget to skip the terminating \0 (that's why "+ 1")
    offset += 2 + requestURILen + 1;

    offset = getBytesToDataChunk(requestContent, offset, req.remoteAddr());

    offset = getBytesToDataChunk(requestContent, offset, req.remoteHostRaw());

    offset = getBytesToDataChunk(requestContent, offset, req.localName());

    req.setLocalPort(readShort(requestContent, offset));
    offset += 2;

    final boolean isSSL = requestContent.get(offset++) != 0;
    req.setSecure(isSSL);
    req.getResponse().setSecure(isSSL);

    offset = decodeHeaders(requestContent, offset, req);

    decodeAttributes(requestContent, offset, req, tomcatAuthentication);

    req.setUnparsedHostHeader(req.getHeaders().getValue("host"));
  }
  private static int decodeAttributes(
      final Buffer requestContent,
      int offset,
      final AjpHttpRequest req,
      final boolean tomcatAuthentication) {

    final DataChunk tmpDataChunk = req.tmpDataChunk;

    boolean moreAttr = true;

    while (moreAttr) {
      final byte attributeCode = requestContent.get(offset++);
      if (attributeCode == AjpConstants.SC_A_ARE_DONE) {
        return offset;
      }

      /* Special case ( XXX in future API make it separate type !)
       */
      if (attributeCode == AjpConstants.SC_A_SSL_KEY_SIZE) {
        // Bug 1326: it's an Integer.
        req.setAttribute(SSLSupport.KEY_SIZE_KEY, readShort(requestContent, offset));
        offset += 2;
      }

      if (attributeCode == AjpConstants.SC_A_REQ_ATTRIBUTE) {
        // 2 strings ???...
        offset = setStringAttribute(req, requestContent, offset);
      }

      // 1 string attributes
      switch (attributeCode) {
        case AjpConstants.SC_A_CONTEXT:
          // nothing
          offset = skipBytes(requestContent, offset);
          break;

        case AjpConstants.SC_A_REMOTE_USER:
          if (tomcatAuthentication) {
            // ignore server
            offset = skipBytes(requestContent, offset);
          } else {
            offset = getBytesToDataChunk(requestContent, offset, req.remoteUser());
          }
          break;

        case AjpConstants.SC_A_AUTH_TYPE:
          if (tomcatAuthentication) {
            // ignore server
            offset = skipBytes(requestContent, offset);
          } else {
            offset = getBytesToDataChunk(requestContent, offset, req.authType());
          }
          break;

        case AjpConstants.SC_A_QUERY_STRING:
          offset = getBytesToDataChunk(requestContent, offset, req.getQueryStringDC());
          break;

        case AjpConstants.SC_A_JVM_ROUTE:
          offset = getBytesToDataChunk(requestContent, offset, req.instanceId());
          break;

        case AjpConstants.SC_A_SSL_CERT:
          req.setSecure(true);
          // SSL certificate extraction is costy, initialize on demand
          offset = getBytesToDataChunk(requestContent, offset, req.sslCert());
          break;

        case AjpConstants.SC_A_SSL_CIPHER:
          req.setSecure(true);
          offset =
              setStringAttributeValue(req, SSLSupport.CIPHER_SUITE_KEY, requestContent, offset);
          break;

        case AjpConstants.SC_A_SSL_SESSION:
          req.setSecure(true);
          offset = setStringAttributeValue(req, SSLSupport.SESSION_ID_KEY, requestContent, offset);
          break;

        case AjpConstants.SC_A_SECRET:
          offset = getBytesToDataChunk(requestContent, offset, tmpDataChunk);

          req.setSecret(tmpDataChunk.toString());
          tmpDataChunk.recycle();

          break;

        case AjpConstants.SC_A_STORED_METHOD:
          offset = getBytesToDataChunk(requestContent, offset, req.getMethodDC());
          break;

        default:
          break; // ignore, we don't know about it - backward compat
      }
    }

    return offset;
  }