httpWriteResponse(http_t        *http,	/* I - HTTP connection */
		  http_status_t status)	/* I - Status code */
{
  http_encoding_t	old_encoding;	/* Old data_encoding value */
  off_t			old_remaining;	/* Old data_remaining value */


 /*
  * Range check input...
  */

  DEBUG_printf(("httpWriteResponse(http=%p, status=%d)", (void *)http, status));

  if (!http || status < HTTP_STATUS_CONTINUE)
  {
    DEBUG_puts("1httpWriteResponse: Bad input.");
    return (-1);
  }

 /*
  * Set the various standard fields if they aren't already...
  */

  if (!http->fields[HTTP_FIELD_DATE][0])
    httpSetField(http, HTTP_FIELD_DATE, httpGetDateString(time(NULL)));

  if (status >= HTTP_STATUS_BAD_REQUEST && http->keep_alive)
  {
    http->keep_alive = HTTP_KEEPALIVE_OFF;
    httpSetField(http, HTTP_FIELD_KEEP_ALIVE, "");
  }

  if (http->version == HTTP_VERSION_1_1)
  {
    if (!http->fields[HTTP_FIELD_CONNECTION][0])
    {
      if (http->keep_alive)
	httpSetField(http, HTTP_FIELD_CONNECTION, "Keep-Alive");
      else
	httpSetField(http, HTTP_FIELD_CONNECTION, "close");
    }

    if (http->keep_alive && !http->fields[HTTP_FIELD_KEEP_ALIVE][0])
      httpSetField(http, HTTP_FIELD_KEEP_ALIVE, "timeout=10");
  }

#ifdef HAVE_SSL
  if (status == HTTP_STATUS_UPGRADE_REQUIRED ||
      status == HTTP_STATUS_SWITCHING_PROTOCOLS)
  {
    if (!http->fields[HTTP_FIELD_CONNECTION][0])
      httpSetField(http, HTTP_FIELD_CONNECTION, "Upgrade");

    if (!http->fields[HTTP_FIELD_UPGRADE][0])
      httpSetField(http, HTTP_FIELD_UPGRADE, "TLS/1.2,TLS/1.1,TLS/1.0");

    if (!http->fields[HTTP_FIELD_CONTENT_LENGTH][0])
      httpSetField(http, HTTP_FIELD_CONTENT_LENGTH, "0");
  }
#endif /* HAVE_SSL */

  if (!http->server)
    httpSetField(http, HTTP_FIELD_SERVER,
                 http->default_server ? http->default_server : CUPS_MINIMAL);

 /*
  * Set the Accept-Encoding field if it isn't already...
  */

  if (!http->accept_encoding)
    httpSetField(http, HTTP_FIELD_ACCEPT_ENCODING,
                 http->default_accept_encoding ? http->default_accept_encoding :
#ifdef HAVE_LIBZ
                                                 "gzip, deflate, identity");
#else
                                                 "identity");
#endif /* HAVE_LIBZ */

 /*
  * Send the response header...
  */

  old_encoding        = http->data_encoding;
  old_remaining       = http->data_remaining;
  http->data_encoding = HTTP_ENCODING_FIELDS;

  if (httpPrintf(http, "HTTP/%d.%d %d %s\r\n", http->version / 100,
                 http->version % 100, (int)status, httpStatus(status)) < 0)
  {
    http->status = HTTP_STATUS_ERROR;
    return (-1);
  }

  if (status != HTTP_STATUS_CONTINUE)
  {
   /*
    * 100 Continue doesn't have the rest of the response headers...
    */

    int		i;			/* Looping var */
    const char	*value;			/* Field value */

    for (i = 0; i < HTTP_FIELD_MAX; i ++)
    {
      if ((value = httpGetField(http, i)) != NULL && *value)
      {
	if (httpPrintf(http, "%s: %s\r\n", http_fields[i], value) < 1)
	{
	  http->status = HTTP_STATUS_ERROR;
	  return (-1);
	}
      }
    }

    if (http->cookie)
    {
      if (strchr(http->cookie, ';'))
      {
        if (httpPrintf(http, "Set-Cookie: %s\r\n", http->cookie) < 1)
	{
	  http->status = HTTP_STATUS_ERROR;
	  return (-1);
	}
      }
      else if (httpPrintf(http, "Set-Cookie: %s; path=/; httponly;%s\r\n", http->cookie, http->tls ? " secure;" : "") < 1)
      {
	http->status = HTTP_STATUS_ERROR;
	return (-1);
      }
    }

   /*
    * "Click-jacking" defense (STR #4492)...
    */

    if (httpPrintf(http, "X-Frame-Options: DENY\r\n"
                         "Content-Security-Policy: frame-ancestors 'none'\r\n") < 1)
    {
      http->status = HTTP_STATUS_ERROR;
      return (-1);
    }
  }

  if (httpWrite2(http, "\r\n", 2) < 2)
  {
    http->status = HTTP_STATUS_ERROR;
    return (-1);
  }

  if (httpFlushWrite(http) < 0)
  {
    http->status = HTTP_STATUS_ERROR;
    return (-1);
  }

  if (status == HTTP_STATUS_CONTINUE ||
      status == HTTP_STATUS_SWITCHING_PROTOCOLS)
  {
   /*
    * Restore the old data_encoding and data_length values...
    */

    http->data_encoding  = old_encoding;
    http->data_remaining = old_remaining;

    if (old_remaining <= INT_MAX)
      http->_data_remaining = (int)old_remaining;
    else
      http->_data_remaining = INT_MAX;
  }
  else if (http->state == HTTP_STATE_OPTIONS ||
           http->state == HTTP_STATE_HEAD ||
           http->state == HTTP_STATE_PUT ||
           http->state == HTTP_STATE_TRACE ||
           http->state == HTTP_STATE_CONNECT ||
           http->state == HTTP_STATE_STATUS)
  {
    DEBUG_printf(("1httpWriteResponse: Resetting state to HTTP_STATE_WAITING, "
                  "was %s.", httpStateString(http->state)));
    http->state = HTTP_STATE_WAITING;
  }
  else
  {
   /*
    * Force data_encoding and data_length to be set according to the response
    * headers...
    */

    http_set_length(http);

    if (http->data_encoding == HTTP_ENCODING_LENGTH && http->data_remaining == 0)
    {
      DEBUG_printf(("1httpWriteResponse: Resetting state to HTTP_STATE_WAITING, "
                    "was %s.", httpStateString(http->state)));
      http->state = HTTP_STATE_WAITING;
      return (0);
    }

    if (http->state == HTTP_STATE_POST_RECV || http->state == HTTP_STATE_GET)
      http->state ++;

#ifdef HAVE_LIBZ
   /*
    * Then start any content encoding...
    */

    DEBUG_puts("1httpWriteResponse: Calling http_content_coding_start.");
    http_content_coding_start(http,
			      httpGetField(http, HTTP_FIELD_CONTENT_ENCODING));
#endif /* HAVE_LIBZ */

  }

  return (0);
}