/**
   * Returns the client-specific information.
   *
   * @return The client-specific information.
   */
  @Override
  public ClientInfo getClientInfo() {
    ClientInfo result = super.getClientInfo();

    if (!this.clientAdded) {
      if (getHeaders() != null) {
        // Extract the header values
        String acceptMediaType = getHeaders().getValues(HeaderConstants.HEADER_ACCEPT);
        String acceptCharset = getHeaders().getValues(HeaderConstants.HEADER_ACCEPT_CHARSET);
        String acceptEncoding = getHeaders().getValues(HeaderConstants.HEADER_ACCEPT_ENCODING);
        String acceptLanguage = getHeaders().getValues(HeaderConstants.HEADER_ACCEPT_LANGUAGE);
        String acceptPatch = getHeaders().getValues(HeaderConstants.HEADER_ACCEPT_PATCH);
        String expect = getHeaders().getValues(HeaderConstants.HEADER_EXPECT);

        // Parse the headers and update the call preferences

        // Parse the Accept* headers. If an error occurs during the
        // parsing
        // of each header, the error is traced and we keep on with the
        // other
        // headers.
        try {
          PreferenceReader.addCharacterSets(acceptCharset, result);
        } catch (Exception e) {
          this.context.getLogger().log(Level.INFO, e.getMessage());
        }

        try {
          PreferenceReader.addEncodings(acceptEncoding, result);
        } catch (Exception e) {
          this.context.getLogger().log(Level.INFO, e.getMessage());
        }

        try {
          PreferenceReader.addLanguages(acceptLanguage, result);
        } catch (Exception e) {
          this.context.getLogger().log(Level.INFO, e.getMessage());
        }

        try {
          PreferenceReader.addMediaTypes(acceptMediaType, result);
        } catch (Exception e) {
          this.context.getLogger().log(Level.INFO, e.getMessage());
        }

        try {
          PreferenceReader.addPatches(acceptPatch, result);
        } catch (Exception e) {
          this.context.getLogger().log(Level.INFO, e.getMessage());
        }

        try {
          ExpectationReader.addValues(expect, result);
        } catch (Exception e) {
          this.context.getLogger().log(Level.INFO, e.getMessage());
        }

        // Set other properties
        result.setAgent(getHeaders().getValues(HeaderConstants.HEADER_USER_AGENT));
        result.setFrom(getHeaders().getFirstValue(HeaderConstants.HEADER_FROM, true));
        result.setAddress(getConnection().getAddress());
        result.setPort(getConnection().getPort());

        if (userPrincipal != null) {
          result.getPrincipals().add(userPrincipal);
        }

        if (this.context != null) {
          // Special handling for the non standard but common
          // "X-Forwarded-For" header.
          final boolean useForwardedForHeader =
              Boolean.parseBoolean(
                  this.context.getParameters().getFirstValue("useForwardedForHeader", false));
          if (useForwardedForHeader) {
            // Lookup the "X-Forwarded-For" header supported by
            // popular
            // proxies and caches.
            final String header = getHeaders().getValues(HeaderConstants.HEADER_X_FORWARDED_FOR);
            if (header != null) {
              final String[] addresses = header.split(",");
              for (int i = 0; i < addresses.length; i++) {
                String address = addresses[i].trim();
                result.getForwardedAddresses().add(address);
              }
            }
          }
        }
      }

      this.clientAdded = true;
    }

    return result;
  }