private void writeResponse(
      ChannelHandlerContext ctx, MessageEvent e, RequestV2 request, InetAddress ia) {
    // Decide whether to close the connection or not.
    boolean close =
        HttpHeaders.Values.CLOSE.equalsIgnoreCase(
                nettyRequest.headers().get(HttpHeaders.Names.CONNECTION))
            || nettyRequest.getProtocolVersion().equals(HttpVersion.HTTP_1_0)
                && !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(
                    nettyRequest.headers().get(HttpHeaders.Names.CONNECTION));

    // Build the response object.
    HttpResponse response;
    if (request.getLowRange() != 0 || request.getHighRange() != 0) {
      response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.PARTIAL_CONTENT);
    } else {
      String soapAction = nettyRequest.headers().get("SOAPACTION");

      if (soapAction != null && soapAction.contains("X_GetFeatureList")) {
        LOGGER.debug("Invalid action in SOAPACTION: " + soapAction);
        response =
            new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR);
      } else {
        response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
      }
    }

    StartStopListenerDelegate startStopListenerDelegate =
        new StartStopListenerDelegate(ia.getHostAddress());
    // Attach it to the context so it can be invoked if connection is reset unexpectedly
    ctx.setAttachment(startStopListenerDelegate);

    try {
      request.answer(response, e, close, startStopListenerDelegate);
    } catch (IOException e1) {
      LOGGER.trace("HTTP request V2 IO error: " + e1.getMessage());
      // note: we don't call stop() here in a finally block as
      // answer() is non-blocking. we only (may) need to call it
      // here in the case of an exception. it's a no-op if it's
      // already been called
      startStopListenerDelegate.stop();
    }
  }