Description

/*
 * HTTP does not define the code for the case when a client closed
 * the connection while we are processing its request so we introduce
 * own code to log such situation when a client has closed the connection
 * before we even try to send the HTTP header to it
 */
#define NGX_HTTP_CLIENT_CLOSED_REQUEST     499

While connecting to upstream

In ngx_http_upstream_connect(),

    u->write_event_handler = ngx_http_upstream_send_request_handler;
    u->read_event_handler = ngx_http_upstream_process_header;

When ngx_http_upstream_next() is called,

    if (r->connection->error) {
        ngx_http_upstream_finalize_request(r, u,
                                           NGX_HTTP_CLIENT_CLOSED_REQUEST);
        return;
    }

In ngx_http_upstream_finalize_request(),

    if (!u->header_sent
        || rc == NGX_HTTP_REQUEST_TIME_OUT
        || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST)
    {
        ngx_http_finalize_request(r, rc);
        return;
    }

In ngx_http_finalize_request(),

    if (rc == NGX_ERROR
        || rc == NGX_HTTP_REQUEST_TIME_OUT
        || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST
        || c->error)
    {
        if (ngx_http_post_action(r) == NGX_OK) {
            return;
        }
 
        ngx_http_terminate_request(r, rc);
        return;
    }

ngx_http_terminate_request() then calls ngx_http_close_request(), which finally calls ngx_http_free_request() and ngx_http_close_connection(). The first logs the request, and the second calls ngx_close_connection(), which leads us to ngx_close_socket(), i.e. close().

Some other cases

ngx_http_upstream_init_request() first sets both event handlers to check for broken connections.

    if (... && !u->conf->ignore_client_abort) {
        ...
 
        r->read_event_handler = ngx_http_upstream_rd_check_broken_connection;
        r->write_event_handler = ngx_http_upstream_wr_check_broken_connection;
    }

If request body is not buffered, ngx_http_upstream_send_request_body() sets the read_event_handler to ngx_http_upstream_read_request_handler, but restores it to ngx_http_upstream_rd_check_broken_connection() once the client request body is fully read.

Both check functions call ngx_http_upstream_check_broken_connection(), which would finalize the client-side connection in the same way.

        if (!u->cacheable) {
            ngx_http_upstream_finalize_request(r, u,
                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);
        }