static int decodeHeaders(final Buffer requestContent, int offset, final AjpHttpRequest req) {
    // Decode headers
    final MimeHeaders headers = req.getHeaders();

    final int hCount = readShort(requestContent, offset);
    offset += 2;

    for (int i = 0; i < hCount; i++) {
      String hName;

      // Header names are encoded as either an integer code starting
      // with 0xA0, or as a normal string (in which case the first
      // two bytes are the length).
      int isc = readShort(requestContent, offset);
      int hId = isc & 0xFF;

      DataChunk valueDC;
      isc &= 0xFF00;
      if (0xA000 == isc) {
        offset += 2;
        hName = AjpConstants.headerTransArray[hId - 1];
        valueDC = headers.addValue(hName);
      } else {
        // reset hId -- if the header currently being read
        // happens to be 7 or 8 bytes long, the code below
        // will think it's the content-type header or the
        // content-length header - SC_REQ_CONTENT_TYPE=7,
        // SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
        // behaviour.  see bug 5861 for more information.
        hId = -1;

        final int headerNameLen = readShort(requestContent, offset);
        offset += 2;
        valueDC = headers.addValue(requestContent, offset, headerNameLen);
        // Don't forget to skip the terminating \0 (that's why "+ 1")
        offset += headerNameLen + 1;
      }

      offset = getBytesToDataChunk(requestContent, offset, valueDC);

      // Get the last added header name (the one we need)
      final DataChunk headerNameDC = headers.getName(headers.size() - 1);

      if (hId == AjpConstants.SC_REQ_CONTENT_LENGTH
          || (hId == -1 && headerNameDC.equalsIgnoreCase("Content-Length"))) {
        // just read the content-length header, so set it
        final long cl = Ascii.parseLong(valueDC);
        if (cl < Integer.MAX_VALUE) {
          req.setContentLength((int) cl);
        }
      } else if (hId == AjpConstants.SC_REQ_CONTENT_TYPE
          || (hId == -1 && headerNameDC.equalsIgnoreCase("Content-Type"))) {
        // just read the content-type header, so set it
        req.setContentType(valueDC.toString());
      }
    }

    return offset;
  }
  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"));
  }