code: public static SafeCloseable withHttpHeaders(Function<HttpHeaders, HttpHeaders> headerManipulator) {
          requireNonNull(headerManipulator, "headerManipulator");
          return withContextCustomizer(ctx -> {
              final HttpHeaders additionalHeaders = ctx.additionalRequestHeaders();
              final DefaultHttpHeaders headers = new DefaultHttpHeaders();
              if (!additionalHeaders.isEmpty()) {
                  headers.set(additionalHeaders);
              }
  
              final HttpHeaders manipulatedHeaders = headerManipulator.apply(headers);
              ctx.setAdditionalRequestHeaders(manipulatedHeaders);
          });
      }
docs: Sets the specified HTTP header manipulating function in a thread-local variable so that the manipulated
  headers are sent by the client call made from the current thread. Use the `try-with-resources` block
  with the returned {@link SafeCloseable} to unset the thread-local variable automatically:
  <pre>{@code
  import static com.linecorp.armeria.common.HttpHeaderNames.AUTHORIZATION;
  import static com.linecorp.armeria.common.HttpHeaderNames.USER_AGENT;
  
  try (SafeCloseable ignored = withHttpHeaders(headers -> {
  headers.set(HttpHeaders.AUTHORIZATION, myCredential)
  .set(HttpHeaders.USER_AGENT, myAgent);
  })) {
  client.executeSomething(..);
  }
  }</pre>
  You can also nest the header manipulation:
  <pre>{@code
  import static com.linecorp.armeria.common.HttpHeaderNames.AUTHORIZATION;
  import static com.linecorp.armeria.common.HttpHeaderNames.USER_AGENT;
  
  try (SafeCloseable ignored = withHttpHeaders(h -> h.set(USER_AGENT, myAgent))) {
  for (String secret : secrets) {
  try (SafeCloseable ignored2 = withHttpHeaders(h -> h.set(AUTHORIZATION, secret))) {
  // Both USER_AGENT and AUTHORIZATION will be set.
  client.executeSomething(..);
  }
  }
  }
  }</pre>
  
  @see #withHttpHeader(AsciiString, String)