@Override
  public void formatResponse(
      ChallengeWriter cw,
      ChallengeResponse challenge,
      Request request,
      Series<Header> httpHeaders) {

    if (challenge.getIdentifier() != null) {
      cw.appendQuotedChallengeParameter("username", challenge.getIdentifier());
    }

    if (challenge.getRealm() != null) {
      cw.appendQuotedChallengeParameter("realm", challenge.getRealm());
    }

    if (challenge.getServerNonce() != null) {
      cw.appendQuotedChallengeParameter("nonce", challenge.getServerNonce());
    }

    if (challenge.getDigestRef() != null) {
      challenge.setDigestRef(new Reference(request.getResourceRef().getPath()));
      cw.appendQuotedChallengeParameter("uri", challenge.getDigestRef().toString());
    }

    char[] responseDigest = formatResponseDigest(challenge, request);

    if (responseDigest != null) {
      cw.appendQuotedChallengeParameter("response", new String(responseDigest));
    }

    if ((challenge.getDigestAlgorithm() != null)
        && !Digest.ALGORITHM_MD5.equals(challenge.getDigestAlgorithm())) {
      cw.appendChallengeParameter("algorithm", challenge.getDigestAlgorithm());
    }

    if (challenge.getClientNonce() != null) {
      cw.appendQuotedChallengeParameter("cnonce", challenge.getClientNonce());
    }

    if (challenge.getOpaque() != null) {
      cw.appendQuotedChallengeParameter("opaque", challenge.getOpaque());
    }

    if (challenge.getQuality() != null) {
      cw.appendChallengeParameter("qop", challenge.getQuality());
    }

    if ((challenge.getQuality() != null) && (challenge.getServerNounceCount() > 0)) {
      cw.appendChallengeParameter("nc", challenge.getServerNounceCountAsHex());
    }

    for (Parameter param : challenge.getParameters()) {
      if (HeaderUtils.isToken(param.getValue())) {
        cw.appendChallengeParameter(param);
      } else {
        cw.appendQuotedChallengeParameter(param);
      }
    }
  }
  /**
   * Drains the buffer into a line builder (start line or header line).
   *
   * @param lineBuilder The line builder to fill.
   * @param builderState The builder state.
   * @return The new builder state.
   * @throws IOException
   */
  public BufferState drain(StringBuilder lineBuilder, BufferState builderState) throws IOException {
    int next;

    if (builderState == BufferState.IDLE) {
      builderState = BufferState.FILLING;
    }

    while ((builderState != BufferState.DRAINING) && getBytes().hasRemaining()) {
      next = (int) getBytes().get();

      switch (builderState) {
        case FILLING:
          if (HeaderUtils.isCarriageReturn(next)) {
            builderState = BufferState.FILLED;
          } else {
            lineBuilder.append((char) next);
          }

          break;

        case FILLED:
          if (HeaderUtils.isLineFeed(next)) {
            builderState = BufferState.DRAINING;
          } else {
            throw new IOException(
                "Missing line feed character at the end of the line. Found character \""
                    + (char) next
                    + "\" ("
                    + next
                    + ") instead");
          }

          break;
      }
    }

    return builderState;
  }
  @Override
  public void write(OutputStream outputStream) throws IOException {
    if (isMultipart()) {
      for (FormData data : getEntries()) {
        // Write the boundary line
        outputStream.write(("--" + getMultipartBoundary()).getBytes());
        HeaderUtils.writeCRLF(outputStream);

        // Write the optional content type header line
        if (MediaType.TEXT_PLAIN.equals(data.getMediaType())) {
          // Write the content disposition header line
          String line = "Content-Disposition: form-data; name=\"" + data.getName() + "\"";
          outputStream.write(line.getBytes());
          HeaderUtils.writeCRLF(outputStream);
        } else {
          // Write the content disposition header line
          String line =
              "Content-Disposition: form-data; name=\""
                  + data.getName()
                  + "\"; filename=\""
                  + data.getFilename()
                  + "\"";
          outputStream.write(line.getBytes());
          HeaderUtils.writeCRLF(outputStream);

          // Write the content type header line
          line = "Content-Type: " + ContentType.writeHeader(data.getValueRepresentation());
          outputStream.write(line.getBytes());
          HeaderUtils.writeCRLF(outputStream);
        }

        // Write the data content
        HeaderUtils.writeCRLF(outputStream);
        data.getValueRepresentation().write(outputStream);
        HeaderUtils.writeCRLF(outputStream);
      }

      // Write the final boundary line
      outputStream.write(("--" + getMultipartBoundary() + "--").getBytes());
      HeaderUtils.writeCRLF(outputStream);
    } else {
      Representation formRep =
          new StringRepresentation(
              getQueryString(), MediaType.APPLICATION_WWW_FORM, null, CharacterSet.UTF_8);
      formRep.write(outputStream);
    }
  }
  @Override
  public ContactInfo readValue() throws IOException {
    ContactInfo result = null;

    skipSpaces();
    if (peek() != -1) {
      result = new ContactInfo();
      if (peek() == '"') {
        result.setDisplayName(readQuotedString());
        skipSpaces();
        result.setReference(new Reference(readReference()));
      } else if (peek() == '<') {
        result.setReference(new Reference(readReference()));
      } else if (HeaderUtils.isTokenChar(peek())) {
        // Read value until end or value or parameter separator
        StringBuilder sb = null;
        int next = read();

        while ((next != -1) && !isComma(next) && !isSemiColon(next)) {
          if (sb == null) {
            sb = new StringBuilder();
          }

          sb.append((char) next);
          next = read();
        }

        // Remove trailing spaces
        if (sb != null) {
          for (int i = sb.length() - 1; (i >= 0) && isLinearWhiteSpace(sb.charAt(i)); i--) {
            sb.deleteCharAt(i);
          }
        }

        // Unread the separator
        if (isComma(next) || isSemiColon(next)) {
          unread();
        }

        // The last token is the reference
        int index = sb.lastIndexOf(" ");
        if (index != -1) {
          if (sb.charAt(index + 1) == '<') {
            if (sb.charAt(sb.length() - 1) == '>') {
              result.setReference(new Reference(sb.substring(index + 2, sb.length() - 1)));
            } else {
              throw new IOException("Unexpected end of reference. Please check your value");
            }
          }
          result.setDisplayName(sb.substring(0, index).trim());
        } else {
          result.setReference(new Reference(sb.toString()));
        }
      }
    }

    // Read address parameters.
    if (skipParameterSeparator()) {
      Parameter param = readParameter();

      while (param != null) {
        if ("q".equals(param.getName())) {
          result.setQuality(PreferenceReader.readQuality(param.getValue()));
        } else if ("expires".equals(param.getName())) {
          result.setExpires(param.getValue());
        } else {
          addParameter(result, param);
        }

        if (skipParameterSeparator()) {
          param = readParameter();
        } else {
          param = null;
        }
      }
    }

    return result;
  }
 /**
  * Adds the request headers.
  *
  * @param headers The headers series to update.
  */
 protected void addRequestHeaders(Series<Header> headers) {
   HeaderUtils.addRequestHeaders(getMessage().getRequest(), headers);
 }
  @Override
  public void formatRequest(
      ChallengeWriter cw, ChallengeRequest challenge, Response response, Series<Header> httpHeaders)
      throws IOException {

    if (challenge.getRealm() != null) {
      cw.appendQuotedChallengeParameter("realm", challenge.getRealm());
    }

    if (!challenge.getDomainRefs().isEmpty()) {
      cw.append(", domain=\"");

      for (int i = 0; i < challenge.getDomainRefs().size(); i++) {
        if (i > 0) {
          cw.append(' ');
        }

        cw.append(challenge.getDomainRefs().get(i).toString());
      }

      cw.append('"');
    }

    if (challenge.getServerNonce() != null) {
      cw.appendQuotedChallengeParameter("nonce", challenge.getServerNonce());
    }

    if (challenge.getOpaque() != null) {
      cw.appendQuotedChallengeParameter("opaque", challenge.getOpaque());
    }

    if (challenge.isStale()) {
      cw.appendChallengeParameter("stale", "true");
    }

    if (challenge.getDigestAlgorithm() != null) {
      cw.appendChallengeParameter("algorithm", challenge.getDigestAlgorithm());
    }

    if (!challenge.getQualityOptions().isEmpty()) {
      cw.append(", qop=\"");

      for (int i = 0; i < challenge.getQualityOptions().size(); i++) {
        if (i > 0) {
          cw.append(',');
        }

        cw.appendToken(challenge.getQualityOptions().get(i).toString());
      }

      cw.append('"');
    }

    for (Parameter param : challenge.getParameters()) {
      if (HeaderUtils.isToken(param.getValue())) {
        cw.appendChallengeParameter(param);
      } else {
        cw.appendQuotedChallengeParameter(param);
      }
    }
  }