Changeset 5072:7fa7e60a7f66 in nginx


Ignore:
Timestamp:
02/18/13 13:50:52 (5 years ago)
Author:
Maxim Dounin <mdounin@…>
Branch:
default
Convert:
svn:c3fe7df1-7212-e011-8a91-001109144009/trunk@5073
Message:

Proxy: support for connection upgrade (101 Switching Protocols).

This allows to proxy WebSockets? by using configuration like this:

location /chat/ {

proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

}

Connection upgrade is allowed as long as it was requested by a client
via the Upgrade request header.

Location:
src/http
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • src/http/modules/ngx_http_chunked_filter_module.c

    r4412 r5072  
    6363    if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED
    6464        || r->headers_out.status == NGX_HTTP_NO_CONTENT
     65        || r->headers_out.status < NGX_HTTP_OK
    6566        || r != r->main
    6667        || (r->method & NGX_HTTP_HEAD))
  • src/http/modules/ngx_http_proxy_module.c

    r5012 r5072  
    14731473            {
    14741474                u->keepalive = !u->headers_in.connection_close;
     1475            }
     1476
     1477            if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS) {
     1478                u->keepalive = 0;
     1479
     1480                if (r->headers_in.upgrade) {
     1481                    u->upgrade = 1;
     1482                }
    14751483            }
    14761484
  • src/http/ngx_http_header_filter_module.c

    r4920 r5072  
    380380    }
    381381
    382     if (r->keepalive) {
     382    if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
     383        len += sizeof("Connection: upgrade" CRLF) - 1;
     384
     385    } else if (r->keepalive) {
    383386        len += sizeof("Connection: keep-alive" CRLF) - 1;
    384387
     
    549552    }
    550553
    551     if (r->keepalive) {
     554    if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
     555        b->last = ngx_cpymem(b->last, "Connection: upgrade" CRLF,
     556                             sizeof("Connection: upgrade" CRLF) - 1);
     557
     558    } else if (r->keepalive) {
    552559        b->last = ngx_cpymem(b->last, "Connection: keep-alive" CRLF,
    553560                             sizeof("Connection: keep-alive" CRLF) - 1);
  • src/http/ngx_http_request.c

    r4930 r5072  
    130130                 offsetof(ngx_http_headers_in_t, expect),
    131131                 ngx_http_process_unique_header_line },
     132
     133    { ngx_string("Upgrade"),
     134                 offsetof(ngx_http_headers_in_t, upgrade),
     135                 ngx_http_process_header_line },
    132136
    133137#if (NGX_HTTP_GZIP)
  • src/http/ngx_http_request.h

    r4930 r5072  
    6565
    6666
     67#define NGX_HTTP_CONTINUE                  100
     68#define NGX_HTTP_SWITCHING_PROTOCOLS       101
     69#define NGX_HTTP_PROCESSING                102
     70
    6771#define NGX_HTTP_OK                        200
    6872#define NGX_HTTP_CREATED                   201
     
    185189    ngx_table_elt_t                  *transfer_encoding;
    186190    ngx_table_elt_t                  *expect;
     191    ngx_table_elt_t                  *upgrade;
    187192
    188193#if (NGX_HTTP_GZIP)
  • src/http/ngx_http_upstream.c

    r5008 r5072  
    4747static void ngx_http_upstream_send_response(ngx_http_request_t *r,
    4848    ngx_http_upstream_t *u);
     49static void ngx_http_upstream_upgrade(ngx_http_request_t *r,
     50    ngx_http_upstream_t *u);
     51static void ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r);
     52static void ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r);
     53static void ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,
     54    ngx_http_upstream_t *u);
     55static void ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,
     56    ngx_http_upstream_t *u);
     57static void ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
     58    ngx_uint_t from_upstream);
    4959static void
    5060    ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r);
     
    13281338
    13291339    u->keepalive = 0;
     1340    u->upgrade = 0;
    13301341
    13311342    ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
     
    20762087    if (rc == NGX_ERROR || rc > NGX_OK || r->post_action) {
    20772088        ngx_http_upstream_finalize_request(r, u, rc);
     2089        return;
     2090    }
     2091
     2092    if (u->upgrade) {
     2093        ngx_http_upstream_upgrade(r, u);
    20782094        return;
    20792095    }
     
    23622378
    23632379static void
     2380ngx_http_upstream_upgrade(ngx_http_request_t *r, ngx_http_upstream_t *u)
     2381{
     2382    int                        tcp_nodelay;
     2383    ngx_connection_t          *c;
     2384    ngx_http_core_loc_conf_t  *clcf;
     2385
     2386    c = r->connection;
     2387    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
     2388
     2389    /* TODO: prevent upgrade if not requested or not possible */
     2390
     2391    r->keepalive = 0;
     2392    c->log->action = "proxying upgraded connection";
     2393
     2394    u->read_event_handler = ngx_http_upstream_upgraded_read_upstream;
     2395    u->write_event_handler = ngx_http_upstream_upgraded_write_upstream;
     2396    r->read_event_handler = ngx_http_upstream_upgraded_read_downstream;
     2397    r->write_event_handler = ngx_http_upstream_upgraded_write_downstream;
     2398
     2399    if (clcf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
     2400        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp_nodelay");
     2401
     2402        tcp_nodelay = 1;
     2403
     2404        if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
     2405                       (const void *) &tcp_nodelay, sizeof(int)) == -1)
     2406        {
     2407            ngx_connection_error(c, ngx_socket_errno,
     2408                                 "setsockopt(TCP_NODELAY) failed");
     2409            ngx_http_upstream_finalize_request(r, u, 0);
     2410            return;
     2411        }
     2412
     2413        c->tcp_nodelay = NGX_TCP_NODELAY_SET;
     2414
     2415        if (setsockopt(u->peer.connection->fd, IPPROTO_TCP, TCP_NODELAY,
     2416                       (const void *) &tcp_nodelay, sizeof(int)) == -1)
     2417        {
     2418            ngx_connection_error(u->peer.connection, ngx_socket_errno,
     2419                                 "setsockopt(TCP_NODELAY) failed");
     2420            ngx_http_upstream_finalize_request(r, u, 0);
     2421            return;
     2422        }
     2423
     2424        u->peer.connection->tcp_nodelay = NGX_TCP_NODELAY_SET;
     2425    }
     2426
     2427    if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) {
     2428        ngx_http_upstream_finalize_request(r, u, 0);
     2429        return;
     2430    }
     2431
     2432    if (u->peer.connection->read->ready
     2433        || u->buffer.pos != u->buffer.last)
     2434    {
     2435        ngx_http_upstream_process_upgraded(r, 1);
     2436    }
     2437
     2438    if (c->read->ready
     2439        || r->header_in->pos != r->header_in->last)
     2440    {
     2441        ngx_http_upstream_process_upgraded(r, 0);
     2442    }
     2443}
     2444
     2445
     2446static void
     2447ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r)
     2448{
     2449    ngx_http_upstream_process_upgraded(r, 0);
     2450}
     2451
     2452
     2453static void
     2454ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r)
     2455{
     2456    ngx_http_upstream_process_upgraded(r, 1);
     2457}
     2458
     2459
     2460static void
     2461ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,
     2462    ngx_http_upstream_t *u)
     2463{
     2464    ngx_http_upstream_process_upgraded(r, 1);
     2465}
     2466
     2467
     2468static void
     2469ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,
     2470    ngx_http_upstream_t *u)
     2471{
     2472    ngx_http_upstream_process_upgraded(r, 0);
     2473}
     2474
     2475
     2476static void
     2477ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
     2478    ngx_uint_t from_upstream)
     2479{
     2480    size_t                     size;
     2481    ssize_t                    n;
     2482    ngx_buf_t                 *b;
     2483    ngx_uint_t                 do_write;
     2484    ngx_connection_t          *c, *downstream, *upstream, *dst, *src;
     2485    ngx_http_upstream_t       *u;
     2486    ngx_http_core_loc_conf_t  *clcf;
     2487
     2488    c = r->connection;
     2489    u = r->upstream;
     2490
     2491    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
     2492                   "http upstream process upgraded, fu:%ui", from_upstream);
     2493
     2494    downstream = c;
     2495    upstream = u->peer.connection;
     2496
     2497    if (downstream->write->timedout) {
     2498        c->timedout = 1;
     2499        ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
     2500        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT);
     2501        return;
     2502    }
     2503
     2504    if (upstream->read->timedout || upstream->write->timedout) {
     2505        ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
     2506        ngx_http_upstream_finalize_request(r, u, 0);
     2507        return;
     2508    }
     2509
     2510    if (from_upstream) {
     2511        src = upstream;
     2512        dst = downstream;
     2513        b = &u->buffer;
     2514
     2515    } else {
     2516        src = downstream;
     2517        dst = upstream;
     2518        b = &u->from_client;
     2519
     2520        if (r->header_in->last > r->header_in->pos) {
     2521            b = r->header_in;
     2522            b->end = b->last;
     2523            do_write = 1;
     2524        }
     2525
     2526        if (b->start == NULL) {
     2527            b->start = ngx_palloc(r->pool, u->conf->buffer_size);
     2528            if (b->start == NULL) {
     2529                ngx_http_upstream_finalize_request(r, u, 0);
     2530                return;
     2531            }
     2532
     2533            b->pos = b->start;
     2534            b->last = b->start;
     2535            b->end = b->start + u->conf->buffer_size;
     2536            b->temporary = 1;
     2537            b->tag = u->output.tag;
     2538        }
     2539    }
     2540
     2541    for ( ;; ) {
     2542
     2543        if (do_write) {
     2544
     2545            size = b->last - b->pos;
     2546
     2547            if (size && dst->write->ready) {
     2548
     2549                n = dst->send(dst, b->pos, size);
     2550
     2551                if (n == NGX_ERROR) {
     2552                    ngx_http_upstream_finalize_request(r, u, 0);
     2553                    return;
     2554                }
     2555
     2556                if (n > 0) {
     2557                    b->pos += n;
     2558
     2559                    if (b->pos == b->last) {
     2560                        b->pos = b->start;
     2561                        b->last = b->start;
     2562                    }
     2563                }
     2564            }
     2565        }
     2566
     2567        size = b->end - b->last;
     2568
     2569        if (size && src->read->ready) {
     2570
     2571            n = src->recv(src, b->last, size);
     2572
     2573            if (n == NGX_AGAIN || n == 0) {
     2574                break;
     2575            }
     2576
     2577            if (n > 0) {
     2578                do_write = 1;
     2579                b->last += n;
     2580
     2581                continue;
     2582            }
     2583
     2584            if (n == NGX_ERROR) {
     2585                src->read->eof = 1;
     2586            }
     2587        }
     2588
     2589        break;
     2590    }
     2591
     2592    if ((upstream->read->eof && u->buffer.pos == u->buffer.last)
     2593        || (downstream->read->eof && u->from_client.pos == u->from_client.last)
     2594        || (downstream->read->eof && upstream->read->eof))
     2595    {
     2596        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
     2597                       "http upstream upgraded done");
     2598        ngx_http_upstream_finalize_request(r, u, 0);
     2599        return;
     2600    }
     2601
     2602    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
     2603
     2604    if (ngx_handle_write_event(upstream->write, u->conf->send_lowat)
     2605        != NGX_OK)
     2606    {
     2607        ngx_http_upstream_finalize_request(r, u, 0);
     2608        return;
     2609    }
     2610
     2611    if (upstream->write->active && !upstream->write->ready) {
     2612        ngx_add_timer(upstream->write, u->conf->send_timeout);
     2613
     2614    } else if (upstream->write->timer_set) {
     2615        ngx_del_timer(upstream->write);
     2616    }
     2617
     2618    if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) {
     2619        ngx_http_upstream_finalize_request(r, u, 0);
     2620        return;
     2621    }
     2622
     2623    if (upstream->read->active && !upstream->read->ready) {
     2624        ngx_add_timer(upstream->read, u->conf->read_timeout);
     2625
     2626    } else if (upstream->read->timer_set) {
     2627        ngx_del_timer(upstream->read);
     2628    }
     2629
     2630    if (ngx_handle_write_event(downstream->write, clcf->send_lowat)
     2631        != NGX_OK)
     2632    {
     2633        ngx_http_upstream_finalize_request(r, u, 0);
     2634        return;
     2635    }
     2636
     2637    if (ngx_handle_read_event(downstream->read, 0) != NGX_OK) {
     2638        ngx_http_upstream_finalize_request(r, u, 0);
     2639        return;
     2640    }
     2641
     2642    if (downstream->write->active && !downstream->write->ready) {
     2643        ngx_add_timer(downstream->write, clcf->send_timeout);
     2644
     2645    } else if (downstream->write->timer_set) {
     2646        ngx_del_timer(downstream->write);
     2647    }
     2648}
     2649
     2650
     2651static void
    23642652ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r)
    23652653{
  • src/http/ngx_http_upstream.h

    r5008 r5072  
    285285    ngx_http_upstream_resolved_t    *resolved;
    286286
     287    ngx_buf_t                        from_client;
     288
    287289    ngx_buf_t                        buffer;
    288290    off_t                            length;
     
    330332    unsigned                         buffering:1;
    331333    unsigned                         keepalive:1;
     334    unsigned                         upgrade:1;
    332335
    333336    unsigned                         request_sent:1;
  • src/http/ngx_http_variables.c

    r5010 r5072  
    17481748    char    *p;
    17491749
    1750     if (r->keepalive) {
     1750    if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
     1751        len = sizeof("upgrade") - 1;
     1752        p = "upgrade";
     1753
     1754    } else if (r->keepalive) {
    17511755        len = sizeof("keep-alive") - 1;
    17521756        p = "keep-alive";
Note: See TracChangeset for help on using the changeset viewer.