├── .github └── workflows │ └── ci.yml ├── LICENSE ├── README.md ├── ci_build.sh ├── config └── src ├── ngx_srt.c ├── ngx_srt.h ├── ngx_srt_connection.c ├── ngx_srt_connection.h ├── ngx_srt_core_module.c ├── ngx_srt_log_module.c ├── ngx_srt_map_module.c ├── ngx_srt_proxy_module.c ├── ngx_srt_script.c ├── ngx_srt_script.h ├── ngx_srt_set_misc_module.c ├── ngx_srt_stream.c ├── ngx_srt_stream.h ├── ngx_srt_upstream.c ├── ngx_srt_upstream.h ├── ngx_srt_variables.c ├── ngx_srt_variables.h ├── ngx_srt_write_filter_module.c └── ngx_stream_srt_proxy_module.c /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: GitHub CI 2 | 3 | on: [push, pull_request] 4 | 5 | defaults: 6 | run: 7 | shell: 'bash -Eeuo pipefail -x {0}' 8 | 9 | env: 10 | MODULE_NAME: nginx-srt-module 11 | jobs: 12 | build-and-test: 13 | name: Build and Test 14 | runs-on: ubuntu-22.04 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | compiler: [clang-14, gcc-10] 19 | confargs: ["--add-module=./$MODULE_NAME", '--add-dynamic-module=./$MODULE_NAME'] 20 | env: 21 | CC: ${{ matrix.compiler }} 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Install Dependencies 25 | run: | 26 | sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 27 | sudo apt-get update -qq 28 | sudo apt-get install -y \ 29 | build-essential \ 30 | clang-14 \ 31 | gcc-11 \ 32 | libssl-dev \ 33 | libpcre3-dev \ 34 | libxml2-dev \ 35 | wget \ 36 | libsrt-openssl-dev \ 37 | unzip 38 | - name : Build 39 | run: ./ci_build.sh ${{ matrix.confargs }} 40 | - name : Install 41 | run : | 42 | cd /tmp/builddir/nginx 43 | sudo make install 44 | - name : Test smoke 45 | run : /sbin/nginx -V 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nginx SRT Module 2 | 3 | Haivision SRT (Secure Reliable Transfer) / TCP gateway. 4 | Supports both SRT to TCP and TCP to SRT, including bidirectional data transfer. 5 | 6 | The implementation uses libsrt for SRT communication. 7 | The libsrt code executes on a side thread, eventfd notifications are used in order to communicate 8 | with the main nginx thread. 9 | 10 | ## Build 11 | 12 | ![Build Status](https://github.com/kaltura/nginx-srt-module/actions/workflows/ci.yml/badge.svg) 13 | 14 | To link statically against nginx, cd to nginx source directory and execute: 15 | 16 | ./configure --add-module=/path/to/nginx-srt-module --with-stream --with-threads 17 | 18 | To compile as a dynamic module (nginx 1.9.11+), use: 19 | 20 | ./configure --add-dynamic-module=/path/to/nginx-srt-module --with-stream --with-threads 21 | 22 | In this case, the `load_module` directive should be used in nginx.conf to load the module. 23 | 24 | ## Configuration 25 | 26 | ### Sample configuration 27 | 28 | ``` 29 | # SRT -> TCP proxy 30 | srt { 31 | server { 32 | listen 4321; 33 | proxy_pass tcp://127.0.0.1:5678; 34 | } 35 | } 36 | 37 | # TCP -> SRT proxy 38 | stream { 39 | server { 40 | listen 5432; 41 | srt_proxy_pass srt://127.0.0.1:4321; 42 | } 43 | } 44 | ``` 45 | 46 | ### srt core directives 47 | 48 | #### srt 49 | * **syntax**: `srt { ... }` 50 | * **default**: `-` 51 | * **context**: `main` 52 | 53 | Provides the configuration file context in which the srt `server` directives are specified. 54 | 55 | #### server 56 | * **syntax**: `server { ... }` 57 | * **default**: `-` 58 | * **context**: `srt` 59 | 60 | Sets the configuration for a server. 61 | 62 | #### listen 63 | * **syntax**: `listen address:port [backlog=number] [bind] [ipv6only=on|off] [reuseport];` 64 | * **default**: `-` 65 | * **context**: `server` 66 | 67 | Sets the address and port for the UDP socket on which the server will accept connections. 68 | 69 | See the documentation of the `listen` directive of the nginx `stream` module for more details on the optional parameters supported by this directive. 70 | 71 | #### variables_hash_max_size 72 | * **syntax**: `variables_hash_max_size size;` 73 | * **default**: `1024` 74 | * **context**: `srt` 75 | 76 | Sets the maximum size of the variables hash table. 77 | 78 | #### variables_hash_bucket_size 79 | * **syntax**: `variables_hash_bucket_size size;` 80 | * **default**: `64` 81 | * **context**: `srt` 82 | 83 | Sets the bucket size for the variables hash table. 84 | 85 | #### error_log 86 | * **syntax**: `error_log file [level];` 87 | * **default**: `logs/error.log error` 88 | * **context**: `srt, server` 89 | 90 | Configures logging, see the documentation of the nginx core `error_log` directive for more details. 91 | 92 | #### fc_pkts 93 | * **syntax**: `fc_pkts number;` 94 | * **default**: `25600` 95 | * **context**: `srt, server` 96 | 97 | Sets the maximum number of "in flight" packets (packets that were sent, but not yet acknowledged). 98 | 99 | See the libsrt documentation of the `SRTO_FC` option for more details. 100 | 101 | #### mss 102 | * **syntax**: `mss size;` 103 | * **default**: `1500` 104 | * **context**: `srt, server` 105 | 106 | Maximum segment size, in bytes. 107 | 108 | See the libsrt documentation of the `SRTO_MSS` option for more details. 109 | 110 | #### recv_buf 111 | * **syntax**: `recv_buf size;` 112 | * **default**: `8192 buffers` 113 | * **context**: `srt, server` 114 | 115 | Receive buffer size, in bytes. 116 | 117 | See the libsrt documentation of the `SRTO_RCVBUF` option for more details. 118 | 119 | #### recv_udp_buf 120 | * **syntax**: `recv_udp_buf size;` 121 | * **default**: `8192 buffers` 122 | * **context**: `srt, server` 123 | 124 | UDP socket receive buffer size, in bytes. 125 | 126 | See the libsrt documentation of the `SRTO_UDP_RCVBUF` option for more details. 127 | 128 | #### recv_latency 129 | * **syntax**: `recv_latency size;` 130 | * **default**: `120ms` 131 | * **context**: `srt, server` 132 | 133 | The latency on the receiving side, in milliseconds. 134 | 135 | See the libsrt documentation of the `SRTO_RCVLATENCY` option for more details. 136 | 137 | #### send_buf 138 | * **syntax**: `send_buf size;` 139 | * **default**: `8192 buffers` 140 | * **context**: `srt, server` 141 | 142 | Send buffer size, in bytes. 143 | 144 | See the libsrt documentation of the `SRTO_SNDBUF` option for more details. 145 | 146 | #### send_udp_buf 147 | * **syntax**: `send_udp_buf size;` 148 | * **default**: `65536` 149 | * **context**: `srt, server` 150 | 151 | UDP socket send buffer size, in bytes. 152 | 153 | See the libsrt documentation of the `SRTO_UDP_SNDBUF` option for more details. 154 | 155 | #### send_latency 156 | * **syntax**: `send_latency size;` 157 | * **default**: `120ms` 158 | * **context**: `srt, server` 159 | 160 | The minimum receiving latency, provided by the sender. 161 | 162 | See the libsrt documentation of the `SRTO_PEERLATENCY` option for more details. 163 | 164 | #### passphrase 165 | * **syntax**: `passphrase expr;` 166 | * **default**: `` 167 | * **context**: `srt, server` 168 | 169 | Sets a passphrase for encryption, see the libsrt documentation of the `SRTO_PASSPHRASE` option for more details. 170 | 171 | The parameter value can contain variables. 172 | 173 | #### in_buf_size 174 | * **syntax**: `in_buf_size size;` 175 | * **default**: `64k` 176 | * **context**: `srt, server` 177 | 178 | Sets the size of the buffer used for reading data from the client. 179 | 180 | ### srt map directives 181 | 182 | #### map 183 | * **syntax**: `map string $variable { ... }` 184 | * **default**: `` 185 | * **context**: `srt` 186 | 187 | Creates a new variable whose value depends on values of one or more of the source variables specified in the first parameter. 188 | 189 | See the documentation of the `map` directive of the nginx `stream` module for more details. 190 | 191 | #### map_hash_max_size 192 | * **syntax**: `map_hash_max_size size;` 193 | * **default**: `2048` 194 | * **context**: `srt` 195 | 196 | Sets the maximum size of the map variables hash table. 197 | 198 | #### map_hash_bucket_size 199 | * **syntax**: `map_hash_bucket_size size;` 200 | * **default**: `32|64|128` 201 | * **context**: `srt` 202 | 203 | Sets the bucket size for the map variables hash table. 204 | 205 | ### srt log directives 206 | 207 | #### access_log 208 | * **syntax**: `access_log path format [buffer=size] [gzip[=level]] [flush=time] [if=condition];` 209 | * **default**: `off` 210 | * **context**: `srt, server` 211 | 212 | Sets the path, format, and configuration for a buffered log write. 213 | 214 | See the documentation of the `access_log` directive of the nginx `stream` module for more details. 215 | 216 | #### log_format 217 | * **syntax**: `log_format name [escape=default|json|none] string ...;` 218 | * **default**: `` 219 | * **context**: `srt` 220 | 221 | Defines a log format. 222 | 223 | See the documentation of the `log_format` directive of the nginx `stream` module for more details. 224 | 225 | #### open_log_file_cache 226 | * **syntax**: `open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];` 227 | * **default**: `off` 228 | * **context**: `srt, server` 229 | 230 | Defines a cache that stores the file descriptors of frequently used logs whose names contain variables. 231 | 232 | See the documentation of the `open_log_file_cache` directive of the nginx `stream` module for more details. 233 | 234 | ### srt proxy directives 235 | 236 | #### proxy_pass 237 | * **syntax**: `proxy_pass address;` 238 | * **default**: `` 239 | * **context**: `srt, server` 240 | 241 | Sets the address of the proxied server. 242 | 243 | #### proxy_connect_timeout 244 | * **syntax**: `proxy_connect_timeout timeout;` 245 | * **default**: `60s` 246 | * **context**: `srt, server` 247 | 248 | Defines a timeout for establishing a connection with a proxied server. 249 | 250 | #### proxy_timeout 251 | * **syntax**: `proxy_timeout timeout;` 252 | * **default**: `10m` 253 | * **context**: `srt, server` 254 | 255 | Sets the timeout between two successive read or write operations on client or proxied server connections. 256 | If no data is transmitted within this time, the connection is closed. 257 | 258 | #### proxy_buffer_size 259 | * **syntax**: `proxy_buffer_size size;` 260 | * **default**: `64k` 261 | * **context**: `srt, server` 262 | 263 | Sets the size of the buffer used for reading data from the proxied server. 264 | 265 | #### proxy_protocol 266 | * **syntax**: `proxy_protocol on | off;` 267 | * **default**: `off` 268 | * **context**: `srt, server` 269 | 270 | Enables the PROXY protocol for connections to a proxied server. 271 | 272 | #### proxy_header 273 | * **syntax**: `proxy_header expr;` 274 | * **default**: `` 275 | * **context**: `srt, server` 276 | 277 | Defines a string that is sent to the proxied server before any data received over SRT. 278 | 279 | The parameter value can contain variables. 280 | 281 | ### srt set misc directives 282 | 283 | #### set_decode_base64 284 | * **syntax**: `set_decode_base64 $dst src;` 285 | * **default**: `` 286 | * **context**: `srt` 287 | 288 | Performs base64 decode of the value of the second argument, and assigns the result to the variable specified in the first argument. 289 | 290 | #### set_decode_base64url 291 | * **syntax**: `set_decode_base64url $dst src;` 292 | * **default**: `` 293 | * **context**: `srt` 294 | 295 | Performs url-safe-base64 decode of the value of the second argument, and assigns the result to the variable specified in the first argument. 296 | 297 | #### set_aes_decrypt 298 | * **syntax**: `set_aes_decrypt $dst base64_key base64_iv src;` 299 | * **default**: `` 300 | * **context**: `srt` 301 | 302 | Performs AES-256-CBC decryption of the value of the last argument, using the supplied key/iv, and assigns the result to the variable specified in the first argument. 303 | 304 | ### stream srt proxy directives 305 | 306 | #### srt_proxy_pass 307 | * **syntax**: `srt_proxy_pass address;` 308 | * **default**: `` 309 | * **context**: `stream, server` 310 | 311 | Sets the address of the proxied server. 312 | 313 | #### srt_proxy_connect_timeout 314 | * **syntax**: `srt_proxy_connect_timeout timeout;` 315 | * **default**: `60s` 316 | * **context**: `srt, server` 317 | 318 | Defines a timeout for establishing a connection with a proxied server. 319 | 320 | #### srt_proxy_timeout 321 | * **syntax**: `srt_proxy_timeout timeout;` 322 | * **default**: `10m` 323 | * **context**: `srt, server` 324 | 325 | Sets the timeout between two successive read or write operations on client or proxied server connections. 326 | If no data is transmitted within this time, the connection is closed. 327 | 328 | #### srt_proxy_buffer_size 329 | * **syntax**: `srt_proxy_buffer_size size;` 330 | * **default**: `64k` 331 | * **context**: `srt, server` 332 | 333 | Sets the size of the buffer used for reading data from the proxied server. 334 | 335 | #### srt_proxy_stream_id 336 | * **syntax**: `srt_proxy_stream_id expr;` 337 | * **default**: `` 338 | * **context**: `srt, server` 339 | 340 | Sets the SRT stream id, see the libsrt documentation of the `SRTO_STREAMID` option for more details. 341 | 342 | The parameter value can contain variables. 343 | 344 | #### srt_proxy_passphrase 345 | * **syntax**: `srt_proxy_passphrase expr;` 346 | * **default**: `` 347 | * **context**: `srt, server` 348 | 349 | Sets a passphrase for encryption, see the libsrt documentation of the `SRTO_PASSPHRASE` option for more details. 350 | 351 | The parameter value can contain variables. 352 | 353 | ## Embedded Variables 354 | 355 | ### Core 356 | 357 | * `binary_remote_addr` - client address in a binary form, the length of the value is always 4 bytes for IPv4 addresses or 16 bytes for IPv6 addresses 358 | * `bytes_received` - number of bytes received from the client 359 | * `bytes_sent` - number of bytes sent to the client 360 | * `connection` - connection serial number 361 | * `hostname` - host name 362 | * `msec` - current time, in seconds with milliseconds resolution 363 | * `nginx_version` - nginx version 364 | * `peer_version` - libsrt version of the remote peer, see the libsrt documentation of the `SRTO_PEERVERSION` option for more details. 365 | * `pid` - PID of the worker process 366 | * `protocol` - protocol used to communicate with the client, always evaluates to `SRT` 367 | * `remote_addr` - client address 368 | * `remote_port` - client port 369 | * `server_addr` - the address of the server which accepted the connection 370 | * `server_port` - the port of the server which accepted the connection 371 | * `session_time` - session duration, in seconds with a milliseconds resolution 372 | * `status` - session status 373 | * `stream_id` - SRT stream id, see the libsrt documentation of the `SRTO_STREAMID` option for more details. 374 | * `time_iso8601` - local time, in ISO 8601 standard format 375 | * `time_local` - local time, in the Common Log Format 376 | 377 | ### Upstream 378 | 379 | * `upstream_addr` - the IP address and port of the upstream server 380 | * `upstream_bytes_received` - number of bytes received from the upstream server 381 | * `upstream_bytes_sent` - number of bytes sent to the upstream server 382 | * `upstream_connect_time` - time to connect to the upstream server, in seconds with millisecond resolution 383 | * `upstream_first_byte_time` - time to receive the first byte of data, in seconds with millisecond resolution 384 | * `upstream_session_time` - session duration, in seconds with millisecond resolution 385 | 386 | ## Copyright & License 387 | 388 | All code in this project is released under the [AGPLv3 license](http://www.gnu.org/licenses/agpl-3.0.html) unless a different license for a particular library is specified in the applicable library path. 389 | 390 | Copyright © Kaltura Inc. All rights reserved. 391 | -------------------------------------------------------------------------------- /ci_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -o nounset # Treat unset variables as an error 3 | 4 | NGINX_VERSION=1.23.2 5 | NGINX_URI="http://nginx.org/download/nginx-$NGINX_VERSION.tar.gz" 6 | 7 | 8 | if [ ! -x "`which wget 2>/dev/null`" ];then 9 | echo "Need to install wget." 10 | exit 2 11 | fi 12 | mkdir -p /tmp/builddir/nginx-$NGINX_VERSION 13 | cp -r . /tmp/builddir/nginx-$NGINX_VERSION/nginx-srt-module 14 | cd /tmp/builddir 15 | wget $NGINX_URI -O kaltura-nginx-$NGINX_VERSION.tar.gz 16 | tar zxf kaltura-nginx-$NGINX_VERSION.tar.gz 17 | mv nginx-$NGINX_VERSION nginx 18 | cd nginx 19 | 20 | 21 | ./configure \ 22 | --prefix=/etc/nginx \ 23 | --sbin-path=/sbin/nginx \ 24 | --conf-path=/etc/nginx/nginx.conf \ 25 | --error-log-path=/var/log/log/nginx/error.log \ 26 | --http-log-path=/var/log/log/nginx/access.log \ 27 | --pid-path=/var/log/run/nginx.pid \ 28 | --lock-path=/var/log/run/nginx.lock \ 29 | --http-client-body-temp-path=/var/log/cache/nginx/client_temp \ 30 | --http-proxy-temp-path=/var/log/cache/nginx/proxy_temp \ 31 | --http-fastcgi-temp-path=/var/log/cache/nginx/fastcgi_temp \ 32 | --http-uwsgi-temp-path=/var/log/cache/nginx/uwsgi_temp \ 33 | --http-scgi-temp-path=/var/log/cache/nginx/scgi_temp \ 34 | --with-http_ssl_module \ 35 | --with-http_realip_module \ 36 | --with-http_addition_module \ 37 | --with-http_sub_module \ 38 | --with-http_dav_module \ 39 | --with-http_flv_module \ 40 | --with-http_mp4_module \ 41 | --with-http_gunzip_module \ 42 | --with-http_gzip_static_module \ 43 | --with-http_random_index_module \ 44 | --with-http_secure_link_module \ 45 | --with-http_stub_status_module \ 46 | --with-http_auth_request_module \ 47 | --with-mail \ 48 | --with-mail_ssl_module \ 49 | --with-file-aio \ 50 | --with-ipv6 \ 51 | --with-debug \ 52 | --with-threads \ 53 | --with-stream \ 54 | --with-cc-opt="-O3" \ 55 | $* 56 | make -j $(nproc) 57 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_srt_module 2 | ngx_module_libs= 3 | 4 | 5 | # libsrt 6 | # 7 | ngx_feature="libsrt" 8 | ngx_feature_name="NGX_HAVE_LIBSRT" 9 | ngx_feature_run=no 10 | ngx_feature_incs="#include " 11 | ngx_feature_path= 12 | ngx_feature_libs="-lsrt" 13 | ngx_feature_test="srt_bind_acquire(0, 0);" 14 | . auto/feature 15 | 16 | if [ $ngx_found = no ]; then 17 | echo "error: libsrt not found, version v1.4.2 or newer is required." 1>&2 18 | exit 1 19 | fi 20 | 21 | ngx_module_libs="$ngx_module_libs $ngx_feature_libs" 22 | 23 | 24 | # openssl evp 25 | # 26 | if [ $OPENSSL = NONE ]; then 27 | LIB_CRYPTO=${LIB_CRYPTO:--lcrypto} 28 | LIB_PTHREAD=${LIB_PTHREAD:--lpthread} 29 | 30 | ngx_feature="OpenSSL EVP library" 31 | ngx_feature_name="NGX_HAVE_OPENSSL_EVP" 32 | ngx_feature_run=no 33 | ngx_feature_incs="#include " 34 | ngx_feature_path= 35 | ngx_feature_libs="$LIB_CRYPTO $NGX_LIBDL $LIB_PTHREAD" 36 | ngx_feature_test="EVP_CIPHER_CTX_new();" 37 | . auto/feature 38 | 39 | if [ $ngx_found = yes ]; then 40 | ngx_module_libs="$ngx_module_libs $ngx_feature_libs" 41 | fi 42 | else 43 | ngx_found=yes 44 | cat << END >> $NGX_AUTO_CONFIG_H 45 | 46 | #ifndef NGX_HAVE_OPENSSL_EVP 47 | #define NGX_HAVE_OPENSSL_EVP 1 48 | #endif 49 | 50 | END 51 | fi 52 | 53 | 54 | # sources 55 | # 56 | SRT_CORE_DEPS="$ngx_addon_dir/src/ngx_srt.h \ 57 | $ngx_addon_dir/src/ngx_srt_connection.h \ 58 | $ngx_addon_dir/src/ngx_srt_script.h \ 59 | $ngx_addon_dir/src/ngx_srt_stream.h \ 60 | $ngx_addon_dir/src/ngx_srt_upstream.h \ 61 | $ngx_addon_dir/src/ngx_srt_variables.h \ 62 | " 63 | 64 | SRT_CORE_SRCS="$ngx_addon_dir/src/ngx_srt.c \ 65 | $ngx_addon_dir/src/ngx_srt_connection.c \ 66 | $ngx_addon_dir/src/ngx_srt_core_module.c \ 67 | $ngx_addon_dir/src/ngx_srt_log_module.c \ 68 | $ngx_addon_dir/src/ngx_srt_map_module.c \ 69 | $ngx_addon_dir/src/ngx_srt_set_misc_module.c \ 70 | $ngx_addon_dir/src/ngx_srt_proxy_module.c \ 71 | $ngx_addon_dir/src/ngx_srt_script.c \ 72 | $ngx_addon_dir/src/ngx_srt_stream.c \ 73 | $ngx_addon_dir/src/ngx_srt_upstream.c \ 74 | $ngx_addon_dir/src/ngx_srt_variables.c \ 75 | $ngx_addon_dir/src/ngx_srt_write_filter_module.c \ 76 | " 77 | 78 | SRT_STREAM_SRCS=" \ 79 | $ngx_addon_dir/src/ngx_stream_srt_proxy_module.c \ 80 | " 81 | 82 | SRT_CORE_MODS="ngx_srt_module \ 83 | ngx_srt_core_module \ 84 | ngx_srt_log_module \ 85 | ngx_srt_map_module \ 86 | ngx_srt_set_misc_module \ 87 | ngx_srt_proxy_module \ 88 | ngx_srt_upstream_module \ 89 | ngx_srt_write_filter_module \ 90 | " 91 | 92 | SRT_STREAM_MODULES=" \ 93 | ngx_stream_srt_proxy_module \ 94 | " 95 | 96 | if [ -n "$ngx_module_link" ]; then 97 | ngx_module_type=CORE 98 | ngx_module_name="$SRT_CORE_MODS" 99 | ngx_module_deps="$SRT_CORE_DEPS" 100 | ngx_module_srcs="$SRT_CORE_SRCS" 101 | 102 | . auto/module 103 | 104 | if [ $STREAM != NO ]; then 105 | ngx_module_type=STREAM 106 | ngx_module_name=$SRT_STREAM_MODULES 107 | ngx_module_srcs=$SRT_STREAM_SRCS 108 | 109 | . auto/module 110 | fi 111 | 112 | else 113 | CORE_MODULES="$CORE_MODULES $SRT_CORE_MODS" 114 | 115 | NGX_ADDON_DEPS="$NGX_ADDON_DEPS $SRT_CORE_DEPS" 116 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $SRT_CORE_SRCS" 117 | CORE_LIBS="$CORE_LIBS $ngx_module_libs" 118 | 119 | if [ $STREAM != NO ]; then 120 | STREAM_MODULES="$STREAM_MODULES $SRT_STREAM_MODULES" 121 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $SRT_STREAM_SRCS" 122 | fi 123 | fi 124 | -------------------------------------------------------------------------------- /src/ngx_srt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "ngx_srt.h" 5 | 6 | 7 | #define NGX_SRT_SESSION 0x53545253 /* "SRTS" */ 8 | 9 | #define NGX_INVALID_SOCKET ((ngx_socket_t) -1) 10 | 11 | 12 | static char *ngx_srt_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 13 | static ngx_int_t ngx_srt_init_phases(ngx_conf_t *cf, 14 | ngx_srt_core_main_conf_t *cmcf); 15 | 16 | static ngx_int_t ngx_srt_create_listening(ngx_conf_t *cf); 17 | static char *ngx_srt_init_listening(ngx_cycle_t *cycle, void *conf); 18 | 19 | static void ngx_srt_conn_handler(ngx_connection_t *c); 20 | static void ngx_srt_log_session(void *data); 21 | 22 | 23 | ngx_uint_t ngx_srt_max_module; 24 | 25 | 26 | ngx_srt_filter_pt ngx_srt_top_filter; 27 | 28 | 29 | static ngx_command_t ngx_srt_commands[] = { 30 | 31 | { ngx_string("srt"), 32 | NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, 33 | ngx_srt_block, 34 | 0, 35 | 0, 36 | NULL }, 37 | 38 | ngx_null_command 39 | }; 40 | 41 | 42 | static ngx_core_module_t ngx_srt_module_ctx = { 43 | ngx_string("srt"), 44 | NULL, 45 | ngx_srt_init_listening, 46 | }; 47 | 48 | 49 | ngx_module_t ngx_srt_module = { 50 | NGX_MODULE_V1, 51 | &ngx_srt_module_ctx, /* module context */ 52 | ngx_srt_commands, /* module directives */ 53 | NGX_CORE_MODULE, /* module type */ 54 | NULL, /* init master */ 55 | NULL, /* init module */ 56 | ngx_srt_init_worker, /* init process */ 57 | NULL, /* init thread */ 58 | NULL, /* exit thread */ 59 | ngx_srt_exit_worker, /* exit process */ 60 | NULL, /* exit master */ 61 | NGX_MODULE_V1_PADDING 62 | }; 63 | 64 | 65 | static char * 66 | ngx_srt_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 67 | { 68 | char *rv; 69 | ngx_uint_t m, mi, s; 70 | ngx_conf_t pcf; 71 | ngx_srt_module_t *module; 72 | ngx_srt_conf_ctx_t *ctx; 73 | ngx_srt_core_srv_conf_t **cscfp; 74 | ngx_srt_core_main_conf_t *cmcf; 75 | 76 | if (*(ngx_srt_conf_ctx_t **) conf) { 77 | return "is duplicate"; 78 | } 79 | 80 | /* the main srt context */ 81 | 82 | ctx = ngx_pcalloc(cf->pool, sizeof(ngx_srt_conf_ctx_t)); 83 | if (ctx == NULL) { 84 | return NGX_CONF_ERROR; 85 | } 86 | 87 | *(ngx_srt_conf_ctx_t **) conf = ctx; 88 | 89 | /* count the number of the srt modules and set up their indices */ 90 | 91 | ngx_srt_max_module = ngx_count_modules(cf->cycle, NGX_SRT_MODULE); 92 | 93 | 94 | /* the srt main_conf context, it's the same in the all srt contexts */ 95 | 96 | ctx->main_conf = ngx_pcalloc(cf->pool, 97 | sizeof(void *) * ngx_srt_max_module); 98 | if (ctx->main_conf == NULL) { 99 | return NGX_CONF_ERROR; 100 | } 101 | 102 | 103 | /* 104 | * the srt null srv_conf context, it is used to merge 105 | * the server{}s' srv_conf's 106 | */ 107 | 108 | ctx->srv_conf = ngx_pcalloc(cf->pool, 109 | sizeof(void *) * ngx_srt_max_module); 110 | if (ctx->srv_conf == NULL) { 111 | return NGX_CONF_ERROR; 112 | } 113 | 114 | 115 | /* 116 | * create the main_conf's and the null srv_conf's of the all srt modules 117 | */ 118 | 119 | for (m = 0; cf->cycle->modules[m]; m++) { 120 | if (cf->cycle->modules[m]->type != NGX_SRT_MODULE) { 121 | continue; 122 | } 123 | 124 | module = cf->cycle->modules[m]->ctx; 125 | mi = cf->cycle->modules[m]->ctx_index; 126 | 127 | if (module->create_main_conf) { 128 | ctx->main_conf[mi] = module->create_main_conf(cf); 129 | if (ctx->main_conf[mi] == NULL) { 130 | return NGX_CONF_ERROR; 131 | } 132 | } 133 | 134 | if (module->create_srv_conf) { 135 | ctx->srv_conf[mi] = module->create_srv_conf(cf); 136 | if (ctx->srv_conf[mi] == NULL) { 137 | return NGX_CONF_ERROR; 138 | } 139 | } 140 | } 141 | 142 | 143 | pcf = *cf; 144 | cf->ctx = ctx; 145 | 146 | for (m = 0; cf->cycle->modules[m]; m++) { 147 | if (cf->cycle->modules[m]->type != NGX_SRT_MODULE) { 148 | continue; 149 | } 150 | 151 | module = cf->cycle->modules[m]->ctx; 152 | 153 | if (module->preconfiguration) { 154 | if (module->preconfiguration(cf) != NGX_OK) { 155 | return NGX_CONF_ERROR; 156 | } 157 | } 158 | } 159 | 160 | 161 | /* parse inside the srt{} block */ 162 | 163 | cf->module_type = NGX_SRT_MODULE; 164 | cf->cmd_type = NGX_SRT_MAIN_CONF; 165 | rv = ngx_conf_parse(cf, NULL); 166 | 167 | if (rv != NGX_CONF_OK) { 168 | *cf = pcf; 169 | return rv; 170 | } 171 | 172 | 173 | /* init srt{} main_conf's, merge the server{}s' srv_conf's */ 174 | 175 | cmcf = ctx->main_conf[ngx_srt_core_module.ctx_index]; 176 | cscfp = cmcf->servers.elts; 177 | 178 | for (m = 0; cf->cycle->modules[m]; m++) { 179 | if (cf->cycle->modules[m]->type != NGX_SRT_MODULE) { 180 | continue; 181 | } 182 | 183 | module = cf->cycle->modules[m]->ctx; 184 | mi = cf->cycle->modules[m]->ctx_index; 185 | 186 | /* init srt{} main_conf's */ 187 | 188 | cf->ctx = ctx; 189 | 190 | if (module->init_main_conf) { 191 | rv = module->init_main_conf(cf, ctx->main_conf[mi]); 192 | if (rv != NGX_CONF_OK) { 193 | *cf = pcf; 194 | return rv; 195 | } 196 | } 197 | 198 | for (s = 0; s < cmcf->servers.nelts; s++) { 199 | 200 | /* merge the server{}s' srv_conf's */ 201 | 202 | cf->ctx = cscfp[s]->ctx; 203 | 204 | if (module->merge_srv_conf) { 205 | rv = module->merge_srv_conf(cf, 206 | ctx->srv_conf[mi], 207 | cscfp[s]->ctx->srv_conf[mi]); 208 | if (rv != NGX_CONF_OK) { 209 | *cf = pcf; 210 | return rv; 211 | } 212 | } 213 | } 214 | } 215 | 216 | if (ngx_srt_init_phases(cf, cmcf) != NGX_OK) { 217 | return NGX_CONF_ERROR; 218 | } 219 | 220 | for (m = 0; cf->cycle->modules[m]; m++) { 221 | if (cf->cycle->modules[m]->type != NGX_SRT_MODULE) { 222 | continue; 223 | } 224 | 225 | module = cf->cycle->modules[m]->ctx; 226 | 227 | if (module->postconfiguration) { 228 | if (module->postconfiguration(cf) != NGX_OK) { 229 | return NGX_CONF_ERROR; 230 | } 231 | } 232 | } 233 | 234 | if (ngx_srt_variables_init_vars(cf) != NGX_OK) { 235 | return NGX_CONF_ERROR; 236 | } 237 | 238 | if (ngx_srt_create_listening(cf) != NGX_OK) { 239 | return NGX_CONF_ERROR; 240 | } 241 | 242 | *cf = pcf; 243 | 244 | return NGX_CONF_OK; 245 | } 246 | 247 | 248 | static ngx_int_t 249 | ngx_srt_init_phases(ngx_conf_t *cf, ngx_srt_core_main_conf_t *cmcf) 250 | { 251 | if (ngx_array_init(&cmcf->phases[NGX_SRT_PRE_LOG_PHASE].handlers, 252 | cf->pool, 1, sizeof(ngx_srt_handler_pt)) 253 | != NGX_OK) 254 | { 255 | return NGX_ERROR; 256 | } 257 | 258 | if (ngx_array_init(&cmcf->phases[NGX_SRT_LOG_PHASE].handlers, 259 | cf->pool, 1, sizeof(ngx_srt_handler_pt)) 260 | != NGX_OK) 261 | { 262 | return NGX_ERROR; 263 | } 264 | 265 | return NGX_OK; 266 | } 267 | 268 | 269 | static ngx_int_t 270 | ngx_srt_create_listening(ngx_conf_t *cf) 271 | { 272 | ngx_uint_t s, n; 273 | ngx_cycle_t *cycle; 274 | ngx_listening_t *ls; 275 | ngx_srt_listen_t *opt, *opts; 276 | ngx_srt_core_srv_conf_t **cscfp, *cscf; 277 | ngx_srt_core_main_conf_t *cmcf; 278 | 279 | cmcf = ngx_srt_conf_get_module_main_conf(cf, ngx_srt_core_module); 280 | 281 | cscfp = cmcf->servers.elts; 282 | for (s = 0; s < cmcf->servers.nelts; s++) { 283 | 284 | cscf = ngx_srt_conf_get_module_srv_conf(cscfp[s], 285 | ngx_srt_core_module); 286 | 287 | opts = cscf->listen->elts; 288 | for (n = 0; n < cscf->listen->nelts; n++) { 289 | 290 | opt = &opts[n]; 291 | 292 | /* trick ngx_create_listening to add the socket to cmcf */ 293 | 294 | cycle = cf->cycle; 295 | cf->cycle = (void *)((u_char *) &cmcf->listening - 296 | offsetof(ngx_cycle_t, listening)); 297 | 298 | ls = ngx_create_listening(cf, opt->sockaddr, opt->socklen); 299 | 300 | cf->cycle = cycle; 301 | 302 | if (ls == NULL) { 303 | return NGX_ERROR; 304 | } 305 | 306 | /* 307 | * set by ngx_memzero(): 308 | * 309 | * ls->log.handler = NULL; 310 | */ 311 | 312 | ls->addr_ntop = 1; 313 | ls->handler = ngx_srt_conn_handler; 314 | ls->pool_size = 256; 315 | ls->type = opt->type; 316 | 317 | ls->logp = cscf->error_log; 318 | ls->log.data = &ls->addr_text; 319 | 320 | ls->backlog = opt->backlog; 321 | 322 | ls->wildcard = opt->wildcard; 323 | 324 | #if (NGX_HAVE_INET6) 325 | ls->ipv6only = opt->ipv6only; 326 | #endif 327 | 328 | #if (NGX_HAVE_REUSEPORT) 329 | ls->reuseport = opt->reuseport; 330 | #endif 331 | 332 | ls->servers = opt->ctx; 333 | } 334 | } 335 | 336 | return NGX_OK; 337 | } 338 | 339 | 340 | static char * 341 | ngx_srt_init_listening(ngx_cycle_t *cycle, void *conf) 342 | { 343 | ngx_int_t rc; 344 | ngx_uint_t i, n; 345 | ngx_uint_t orig_flags; 346 | ngx_cycle_t *old_cycle; 347 | ngx_cycle_t dummy_cycle; 348 | ngx_array_t listening; 349 | ngx_array_t old_listening; 350 | ngx_listening_t *ls, *nls; 351 | ngx_srt_core_main_conf_t *old_cmcf; 352 | ngx_srt_core_main_conf_t *cmcf; 353 | 354 | if (ngx_process == NGX_PROCESS_SIGNALLER) { 355 | return NGX_CONF_OK; 356 | } 357 | 358 | old_cycle = cycle->old_cycle; 359 | 360 | if (old_cycle->conf_ctx != NULL) { 361 | old_cmcf = ngx_srt_cycle_get_module_main_conf(old_cycle, 362 | ngx_srt_core_module); 363 | old_listening = old_cmcf->listening; 364 | 365 | } else { 366 | old_listening.nelts = 0; 367 | old_listening.elts = NULL; 368 | } 369 | 370 | cmcf = ngx_srt_cycle_get_module_main_conf(cycle, ngx_srt_core_module); 371 | if (cmcf != NULL) { 372 | listening = cmcf->listening; 373 | 374 | } else { 375 | listening.nelts = 0; 376 | listening.elts = NULL; 377 | } 378 | 379 | /* Note: code copied from ngx_init_cycle */ 380 | 381 | /* handle the listening sockets */ 382 | 383 | if (old_listening.nelts) { 384 | ls = old_listening.elts; 385 | for (i = 0; i < old_listening.nelts; i++) { 386 | ls[i].remain = 0; 387 | } 388 | 389 | nls = listening.elts; 390 | for (n = 0; n < listening.nelts; n++) { 391 | 392 | for (i = 0; i < old_listening.nelts; i++) { 393 | if (ls[i].ignore) { 394 | continue; 395 | } 396 | 397 | if (ls[i].remain) { 398 | continue; 399 | } 400 | 401 | if (ls[i].type != nls[n].type) { 402 | continue; 403 | } 404 | 405 | if (ngx_cmp_sockaddr(nls[n].sockaddr, nls[n].socklen, 406 | ls[i].sockaddr, ls[i].socklen, 1) 407 | == NGX_OK) 408 | { 409 | nls[n].fd = ls[i].fd; 410 | nls[n].previous = &ls[i]; 411 | ls[i].remain = 1; 412 | 413 | if (ls[i].backlog != nls[n].backlog) { 414 | nls[n].listen = 1; 415 | } 416 | 417 | #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) 418 | 419 | /* 420 | * FreeBSD, except the most recent versions, 421 | * could not remove accept filter 422 | */ 423 | nls[n].deferred_accept = ls[i].deferred_accept; 424 | 425 | if (ls[i].accept_filter && nls[n].accept_filter) { 426 | if (ngx_strcmp(ls[i].accept_filter, 427 | nls[n].accept_filter) 428 | != 0) 429 | { 430 | nls[n].delete_deferred = 1; 431 | nls[n].add_deferred = 1; 432 | } 433 | 434 | } else if (ls[i].accept_filter) { 435 | nls[n].delete_deferred = 1; 436 | 437 | } else if (nls[n].accept_filter) { 438 | nls[n].add_deferred = 1; 439 | } 440 | #endif 441 | 442 | #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) 443 | 444 | if (ls[i].deferred_accept && !nls[n].deferred_accept) { 445 | nls[n].delete_deferred = 1; 446 | 447 | } else if (ls[i].deferred_accept != nls[n].deferred_accept) 448 | { 449 | nls[n].add_deferred = 1; 450 | } 451 | #endif 452 | 453 | #if (NGX_HAVE_REUSEPORT) 454 | if (nls[n].reuseport && !ls[i].reuseport) { 455 | nls[n].add_reuseport = 1; 456 | } 457 | #endif 458 | 459 | break; 460 | } 461 | } 462 | 463 | if (nls[n].fd == NGX_INVALID_SOCKET) { 464 | nls[n].open = 1; 465 | #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) 466 | if (nls[n].accept_filter) { 467 | nls[n].add_deferred = 1; 468 | } 469 | #endif 470 | #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) 471 | if (nls[n].deferred_accept) { 472 | nls[n].add_deferred = 1; 473 | } 474 | #endif 475 | } 476 | } 477 | 478 | } else { 479 | ls = listening.elts; 480 | for (i = 0; i < listening.nelts; i++) { 481 | ls[i].open = 1; 482 | #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) 483 | if (ls[i].accept_filter) { 484 | ls[i].add_deferred = 1; 485 | } 486 | #endif 487 | #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) 488 | if (ls[i].deferred_accept) { 489 | ls[i].add_deferred = 1; 490 | } 491 | #endif 492 | } 493 | } 494 | 495 | /* trick ngx_open_listening_sockets to process cmcf sockets */ 496 | 497 | dummy_cycle.log = cycle->log; 498 | dummy_cycle.listening = listening; 499 | 500 | /* enable iocp flag to avoid setting the socket as nonblocking */ 501 | 502 | orig_flags = ngx_event_flags; 503 | ngx_event_flags |= NGX_USE_IOCP_EVENT; 504 | 505 | rc = ngx_open_listening_sockets(&dummy_cycle); 506 | 507 | ngx_event_flags = orig_flags; 508 | 509 | if (rc != NGX_OK) { 510 | return NGX_CONF_ERROR; 511 | } 512 | 513 | if (!ngx_test_config) { 514 | ngx_configure_listening_sockets(&dummy_cycle); 515 | } 516 | 517 | /* close the unnecessary listening sockets */ 518 | 519 | ls = old_listening.elts; 520 | for (i = 0; i < old_listening.nelts; i++) { 521 | 522 | if (ls[i].remain || ls[i].fd == NGX_INVALID_SOCKET) { 523 | continue; 524 | } 525 | 526 | if (ngx_close_socket(ls[i].fd) == -1) { 527 | ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, 528 | ngx_close_socket_n " listening socket on %V failed", 529 | &ls[i].addr_text); 530 | } 531 | } 532 | 533 | return NGX_CONF_OK; 534 | } 535 | 536 | 537 | ngx_srt_session_t * 538 | ngx_srt_init_session(ngx_srt_conn_t *sc) 539 | { 540 | ngx_connection_t *c; 541 | ngx_srt_session_t *s; 542 | ngx_srt_conf_ctx_t *ctx; 543 | ngx_srt_core_main_conf_t *cmcf; 544 | 545 | c = sc->connection; 546 | 547 | s = ngx_pcalloc(c->pool, sizeof(ngx_srt_session_t)); 548 | if (s == NULL) { 549 | ngx_log_error(NGX_LOG_NOTICE, c->log, 0, 550 | "ngx_srt_init_session: alloc session failed"); 551 | return NULL; 552 | } 553 | 554 | s->sc = sc; 555 | 556 | s->signature = NGX_SRT_SESSION; 557 | 558 | ctx = c->listening->servers; 559 | 560 | s->main_conf = ctx->main_conf; 561 | s->srv_conf = ctx->srv_conf; 562 | 563 | s->connection = c; 564 | 565 | s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_srt_max_module); 566 | if (s->ctx == NULL) { 567 | ngx_log_error(NGX_LOG_NOTICE, c->log, 0, 568 | "ngx_srt_init_session: alloc ctx failed"); 569 | return NULL; 570 | } 571 | 572 | cmcf = ngx_srt_get_module_main_conf(s, ngx_srt_core_module); 573 | 574 | s->variables = ngx_pcalloc(c->pool, cmcf->variables.nelts 575 | * sizeof(ngx_srt_variable_value_t)); 576 | if (s->variables == NULL) { 577 | ngx_log_error(NGX_LOG_NOTICE, c->log, 0, 578 | "ngx_srt_init_session: alloc variables failed"); 579 | return NULL; 580 | } 581 | 582 | sc->session = s; 583 | 584 | sc->log_session = ngx_srt_log_session; 585 | 586 | return s; 587 | } 588 | 589 | 590 | static void 591 | ngx_srt_log_session(void *data) 592 | { 593 | ngx_uint_t i, n; 594 | ngx_srt_conn_t *sc; 595 | ngx_srt_session_t *s; 596 | ngx_srt_handler_pt *log_handler; 597 | ngx_srt_core_main_conf_t *cmcf; 598 | 599 | sc = data; 600 | s = sc->session; 601 | 602 | cmcf = ngx_srt_get_module_main_conf(s, ngx_srt_core_module); 603 | 604 | /* pre log */ 605 | 606 | log_handler = cmcf->phases[NGX_SRT_PRE_LOG_PHASE].handlers.elts; 607 | n = cmcf->phases[NGX_SRT_PRE_LOG_PHASE].handlers.nelts; 608 | 609 | for (i = 0; i < n; i++) { 610 | log_handler[i](s); 611 | } 612 | 613 | /* log */ 614 | 615 | log_handler = cmcf->phases[NGX_SRT_LOG_PHASE].handlers.elts; 616 | n = cmcf->phases[NGX_SRT_LOG_PHASE].handlers.nelts; 617 | 618 | for (i = 0; i < n; i++) { 619 | log_handler[i](s); 620 | } 621 | } 622 | 623 | 624 | static void 625 | ngx_srt_conn_handler(ngx_connection_t *c) 626 | { 627 | ngx_srt_conn_t *sc; 628 | ngx_srt_session_t *s; 629 | ngx_srt_core_srv_conf_t *cscf; 630 | 631 | sc = c->data; 632 | 633 | s = ngx_srt_init_session(sc); 634 | if (s == NULL) { 635 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 636 | return; 637 | } 638 | 639 | cscf = ngx_srt_get_module_srv_conf(s, ngx_srt_core_module); 640 | 641 | cscf->handler(s); 642 | } 643 | 644 | 645 | /* Context: SRT thread */ 646 | ngx_int_t 647 | ngx_srt_start_listening(ngx_cycle_t *cycle) 648 | { 649 | ngx_uint_t i; 650 | ngx_listening_t *ls; 651 | ngx_srt_conf_ctx_t *ctx; 652 | ngx_srt_core_srv_conf_t *cscf; 653 | ngx_srt_core_main_conf_t *cmcf; 654 | 655 | cmcf = ngx_srt_cycle_get_module_main_conf(cycle, ngx_srt_core_module); 656 | if (cmcf == NULL) { 657 | return NGX_OK; 658 | } 659 | 660 | ls = cmcf->listening.elts; 661 | for (i = 0; i < cmcf->listening.nelts; i++) { 662 | ctx = ls[i].servers; 663 | 664 | cscf = ngx_srt_get_module_srv_conf(ctx, ngx_srt_core_module); 665 | 666 | if (ngx_srt_listen(cycle, &ls[i], cscf->error_log, cscf->in_buf_size, 667 | &cscf->srt_opts) != NGX_OK) 668 | { 669 | return NGX_ERROR; 670 | } 671 | } 672 | 673 | return NGX_OK; 674 | } 675 | -------------------------------------------------------------------------------- /src/ngx_srt.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_SRT_H_INCLUDED_ 2 | #define _NGX_SRT_H_INCLUDED_ 3 | 4 | 5 | #include 6 | #include 7 | 8 | 9 | typedef struct ngx_srt_session_s ngx_srt_session_t; 10 | typedef struct ngx_srt_conn_s ngx_srt_conn_t; 11 | 12 | 13 | #include "ngx_srt_variables.h" 14 | #include "ngx_srt_script.h" 15 | #include "ngx_srt_stream.h" 16 | #include "ngx_srt_upstream.h" 17 | #include "ngx_srt_connection.h" 18 | 19 | 20 | #define NGX_SRT_OK 200 21 | #define NGX_SRT_INTERNAL_SERVER_ERROR 500 22 | #define NGX_SRT_BAD_GATEWAY 502 23 | 24 | 25 | #define NGX_LOG_DEBUG_SRT NGX_LOG_DEBUG_STREAM 26 | 27 | 28 | typedef struct { 29 | void **main_conf; 30 | void **srv_conf; 31 | } ngx_srt_conf_ctx_t; 32 | 33 | 34 | typedef struct { 35 | struct sockaddr *sockaddr; 36 | socklen_t socklen; 37 | ngx_str_t addr_text; 38 | 39 | /* server ctx */ 40 | ngx_srt_conf_ctx_t *ctx; 41 | 42 | unsigned bind:1; 43 | unsigned wildcard:1; 44 | #if (NGX_HAVE_INET6) 45 | unsigned ipv6only:1; 46 | #endif 47 | unsigned reuseport:1; 48 | int backlog; 49 | int type; 50 | } ngx_srt_listen_t; 51 | 52 | 53 | typedef enum { 54 | NGX_SRT_PRE_LOG_PHASE = 0, 55 | NGX_SRT_LOG_PHASE 56 | } ngx_srt_phases; 57 | 58 | 59 | typedef ngx_int_t (*ngx_srt_handler_pt)(ngx_srt_session_t *s); 60 | typedef void (*ngx_srt_content_handler_pt)(ngx_srt_session_t *s); 61 | 62 | 63 | typedef struct { 64 | ngx_array_t handlers; 65 | } ngx_srt_phase_t; 66 | 67 | 68 | typedef struct { 69 | ngx_array_t servers; /* ngx_srt_core_srv_conf_t */ 70 | ngx_array_t listening; /* ngx_srt_listen_t */ 71 | 72 | ngx_hash_t variables_hash; 73 | 74 | ngx_array_t variables; /* ngx_srt_variable_t */ 75 | ngx_array_t prefix_variables; /* ngx_srt_variable_t */ 76 | ngx_uint_t ncaptures; 77 | 78 | ngx_uint_t variables_hash_max_size; 79 | ngx_uint_t variables_hash_bucket_size; 80 | 81 | ngx_hash_keys_arrays_t *variables_keys; 82 | 83 | ngx_srt_phase_t phases[NGX_SRT_LOG_PHASE + 1]; 84 | } ngx_srt_core_main_conf_t; 85 | 86 | 87 | typedef struct { 88 | ngx_srt_content_handler_pt handler; 89 | 90 | ngx_srt_conf_ctx_t *ctx; 91 | 92 | ngx_array_t *listen; /* ngx_srt_listen_t */ 93 | 94 | u_char *file_name; 95 | ngx_uint_t line; 96 | 97 | ngx_log_t *error_log; 98 | 99 | size_t in_buf_size; 100 | 101 | ngx_srt_conn_options_t srt_opts; 102 | ngx_srt_complex_value_t *passphrase; 103 | } ngx_srt_core_srv_conf_t; 104 | 105 | 106 | struct ngx_srt_session_s { 107 | uint32_t signature; /* "SRT " */ 108 | 109 | /* Note: connection is copied from ngx_srt_conn_t for script/variables */ 110 | ngx_connection_t *connection; 111 | 112 | void **ctx; 113 | void **main_conf; 114 | void **srv_conf; 115 | 116 | ngx_array_t *upstream_states; 117 | /* of ngx_srt_upstream_state_t */ 118 | ngx_srt_variable_value_t *variables; 119 | 120 | #if (NGX_PCRE) 121 | ngx_uint_t ncaptures; 122 | int *captures; 123 | u_char *captures_data; 124 | #endif 125 | 126 | ngx_srt_conn_t *sc; 127 | }; 128 | 129 | 130 | typedef struct { 131 | ngx_int_t (*preconfiguration)(ngx_conf_t *cf); 132 | ngx_int_t (*postconfiguration)(ngx_conf_t *cf); 133 | 134 | void *(*create_main_conf)(ngx_conf_t *cf); 135 | char *(*init_main_conf)(ngx_conf_t *cf, void *conf); 136 | 137 | void *(*create_srv_conf)(ngx_conf_t *cf); 138 | char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, 139 | void *conf); 140 | } ngx_srt_module_t; 141 | 142 | 143 | #define NGX_SRT_MODULE 0x20545253 /* "SRT " */ 144 | 145 | #define NGX_SRT_MAIN_CONF 0x02000000 146 | #define NGX_SRT_SRV_CONF 0x04000000 147 | #define NGX_SRT_UPS_CONF 0x08000000 148 | 149 | 150 | #define NGX_SRT_MAIN_CONF_OFFSET offsetof(ngx_srt_conf_ctx_t, main_conf) 151 | #define NGX_SRT_SRV_CONF_OFFSET offsetof(ngx_srt_conf_ctx_t, srv_conf) 152 | 153 | 154 | #define ngx_srt_get_module_ctx(s, module) (s)->ctx[module.ctx_index] 155 | #define ngx_srt_set_ctx(s, c, module) s->ctx[module.ctx_index] = c; 156 | #define ngx_srt_delete_ctx(s, module) s->ctx[module.ctx_index] = NULL; 157 | 158 | 159 | #define ngx_srt_get_module_main_conf(s, module) \ 160 | (s)->main_conf[module.ctx_index] 161 | #define ngx_srt_get_module_srv_conf(s, module) \ 162 | (s)->srv_conf[module.ctx_index] 163 | 164 | #define ngx_srt_conf_get_module_main_conf(cf, module) \ 165 | ((ngx_srt_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index] 166 | #define ngx_srt_conf_get_module_srv_conf(cf, module) \ 167 | ((ngx_srt_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index] 168 | 169 | #define ngx_srt_cycle_get_module_main_conf(cycle, module) \ 170 | (cycle->conf_ctx[ngx_srt_module.index] ? \ 171 | ((ngx_srt_conf_ctx_t *) cycle->conf_ctx[ngx_srt_module.index]) \ 172 | ->main_conf[module.ctx_index]: \ 173 | NULL) 174 | 175 | 176 | #define NGX_SRT_WRITE_BUFFERED 0x10 177 | 178 | 179 | extern ngx_module_t ngx_srt_module; 180 | extern ngx_uint_t ngx_srt_max_module; 181 | extern ngx_module_t ngx_srt_core_module; 182 | 183 | 184 | ngx_int_t ngx_srt_start_listening(ngx_cycle_t *cycle); 185 | 186 | ngx_srt_session_t *ngx_srt_init_session(ngx_srt_conn_t *sc); 187 | 188 | 189 | typedef ngx_int_t (*ngx_srt_filter_pt)(ngx_srt_conn_t *sc, 190 | ngx_chain_t *chain, ngx_uint_t from_srt); 191 | 192 | 193 | extern ngx_srt_filter_pt ngx_srt_top_filter; 194 | 195 | 196 | #endif /* _NGX_SRT_H_INCLUDED_ */ 197 | -------------------------------------------------------------------------------- /src/ngx_srt_connection.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_SRT_CONNECTION_H_INCLUDED_ 2 | #define _NGX_SRT_CONNECTION_H_INCLUDED_ 3 | 4 | 5 | #include 6 | #include 7 | 8 | 9 | #define NGX_SRT_POST_READ 0x1 10 | #define NGX_SRT_POST_WRITE 0x2 11 | #define NGX_SRT_POST_CONNECT 0x4 12 | #define NGX_SRT_POST_CLOSE 0x8 13 | 14 | 15 | typedef struct { 16 | ngx_uint_t fc_pkts; 17 | size_t mss; 18 | 19 | size_t recv_buf; 20 | size_t recv_udp_buf; 21 | ngx_msec_t recv_latency; 22 | 23 | size_t send_buf; 24 | size_t send_udp_buf; 25 | ngx_msec_t send_latency; 26 | } ngx_srt_conn_options_t; 27 | 28 | 29 | typedef struct { 30 | ngx_buf_t buf; /* last - shared */ 31 | 32 | ngx_chain_t *free; /* shared */ 33 | ngx_chain_t *out; /* shared */ 34 | ngx_chain_t *busy; /* ngx */ 35 | 36 | ngx_atomic_t lock; 37 | } ngx_srt_conn_in_t; 38 | 39 | 40 | typedef struct { 41 | ngx_chain_t *free; /* shared */ 42 | ngx_chain_t *out; /* shared */ 43 | ngx_chain_t *busy; /* srt */ 44 | 45 | ngx_atomic_t out_lock; 46 | ngx_atomic_t free_lock; 47 | 48 | size_t added; /* ngx */ 49 | size_t acked; /* ngx */ 50 | } ngx_srt_conn_out_t; 51 | 52 | 53 | typedef struct { 54 | ngx_chain_t *from_srt; /* ngx */ 55 | ngx_chain_t *from_ngx; /* ngx */ 56 | } ngx_srt_write_filter_ctx_t; 57 | 58 | 59 | struct ngx_srt_conn_s { 60 | ngx_rbtree_node_t node; /* srt */ 61 | 62 | ngx_pool_t *srt_pool; /* srt */ 63 | ngx_connection_t *connection; /* sent, fd - srt 64 | pool, log, read, write - ngx */ 65 | 66 | ngx_srt_stream_t *stream; /* ngx */ 67 | void *session; /* ngx */ 68 | void *upstream; /* ngx */ 69 | 70 | ngx_str_t stream_id; 71 | ngx_str_t passphrase; 72 | uint32_t peer_version; 73 | uint32_t payload_size; 74 | 75 | ngx_log_handler_pt log_handler; 76 | ngx_pool_cleanup_pt log_session; 77 | 78 | ngx_uint_t status; 79 | off_t received; 80 | time_t start_sec; 81 | ngx_msec_t start_msec; 82 | 83 | ngx_srt_conn_t *ngx_next; /* shared */ 84 | uint32_t ngx_post_flags; /* shared */ 85 | 86 | ngx_srt_conn_t *srt_next; /* shared */ 87 | uint32_t srt_post_flags; /* shared */ 88 | 89 | ngx_srt_conn_in_t srt_in; 90 | ngx_srt_conn_out_t srt_out; 91 | 92 | ngx_srt_write_filter_ctx_t writer_ctx; /* ngx */ 93 | 94 | unsigned connected:1; 95 | }; 96 | 97 | 98 | ngx_int_t ngx_srt_init_worker(ngx_cycle_t *cycle); 99 | 100 | void ngx_srt_exit_worker(ngx_cycle_t *cycle); 101 | 102 | 103 | void ngx_srt_merge_options(ngx_srt_conn_options_t *conf, 104 | ngx_srt_conn_options_t *prev); 105 | 106 | ngx_int_t ngx_srt_listen(ngx_cycle_t *cycle, ngx_listening_t *ls, 107 | ngx_log_t *error_log, size_t in_buf_size, ngx_srt_conn_options_t *opts); 108 | 109 | ngx_srt_conn_t *ngx_srt_conn_create_connect(ngx_log_t *log, ngx_url_t *url, 110 | size_t in_buf_size, ngx_str_t *stream_id, ngx_str_t *passphrase); 111 | 112 | void ngx_srt_conn_finalize(ngx_srt_conn_t *sc, ngx_uint_t rc); 113 | 114 | 115 | ngx_int_t ngx_srt_conn_in_insert_head(ngx_srt_conn_t *sc, 116 | u_char *start, u_char *end); 117 | 118 | ngx_chain_t *ngx_srt_conn_in_get_chain(ngx_srt_conn_t *sc); 119 | 120 | void ngx_srt_conn_in_update_chains(ngx_srt_conn_t *sc, ngx_chain_t *out); 121 | 122 | #endif /* _NGX_SRT_CONNECTION_H_INCLUDED_ */ 123 | -------------------------------------------------------------------------------- /src/ngx_srt_core_module.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Roman Arutyunyan 4 | * Copyright (C) Nginx, Inc. 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | #include "ngx_srt.h" 11 | 12 | 13 | static ngx_int_t ngx_srt_core_preconfiguration(ngx_conf_t *cf); 14 | static void *ngx_srt_core_create_main_conf(ngx_conf_t *cf); 15 | static char *ngx_srt_core_init_main_conf(ngx_conf_t *cf, void *conf); 16 | static void *ngx_srt_core_create_srv_conf(ngx_conf_t *cf); 17 | static char *ngx_srt_core_merge_srv_conf(ngx_conf_t *cf, void *parent, 18 | void *child); 19 | static char *ngx_srt_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, 20 | void *conf); 21 | static char *ngx_srt_core_server(ngx_conf_t *cf, ngx_command_t *cmd, 22 | void *conf); 23 | static char *ngx_srt_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, 24 | void *conf); 25 | 26 | 27 | static ngx_command_t ngx_srt_core_commands[] = { 28 | 29 | { ngx_string("variables_hash_max_size"), 30 | NGX_SRT_MAIN_CONF|NGX_CONF_TAKE1, 31 | ngx_conf_set_num_slot, 32 | NGX_SRT_MAIN_CONF_OFFSET, 33 | offsetof(ngx_srt_core_main_conf_t, variables_hash_max_size), 34 | NULL }, 35 | 36 | { ngx_string("variables_hash_bucket_size"), 37 | NGX_SRT_MAIN_CONF|NGX_CONF_TAKE1, 38 | ngx_conf_set_num_slot, 39 | NGX_SRT_MAIN_CONF_OFFSET, 40 | offsetof(ngx_srt_core_main_conf_t, variables_hash_bucket_size), 41 | NULL }, 42 | 43 | { ngx_string("server"), 44 | NGX_SRT_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, 45 | ngx_srt_core_server, 46 | 0, 47 | 0, 48 | NULL }, 49 | 50 | { ngx_string("listen"), 51 | NGX_SRT_SRV_CONF|NGX_CONF_1MORE, 52 | ngx_srt_core_listen, 53 | NGX_SRT_SRV_CONF_OFFSET, 54 | 0, 55 | NULL }, 56 | 57 | { ngx_string("error_log"), 58 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_1MORE, 59 | ngx_srt_core_error_log, 60 | NGX_SRT_SRV_CONF_OFFSET, 61 | 0, 62 | NULL }, 63 | 64 | { ngx_string("in_buf_size"), 65 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_TAKE1, 66 | ngx_conf_set_size_slot, 67 | NGX_SRT_SRV_CONF_OFFSET, 68 | offsetof(ngx_srt_core_srv_conf_t, in_buf_size), 69 | NULL }, 70 | 71 | 72 | { ngx_string("fc_pkts"), 73 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_TAKE1, 74 | ngx_conf_set_num_slot, 75 | NGX_SRT_SRV_CONF_OFFSET, 76 | offsetof(ngx_srt_core_srv_conf_t, srt_opts.fc_pkts), 77 | NULL }, 78 | 79 | { ngx_string("mss"), 80 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_TAKE1, 81 | ngx_conf_set_size_slot, 82 | NGX_SRT_SRV_CONF_OFFSET, 83 | offsetof(ngx_srt_core_srv_conf_t, srt_opts.mss), 84 | NULL }, 85 | 86 | 87 | { ngx_string("recv_buf"), 88 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_TAKE1, 89 | ngx_conf_set_size_slot, 90 | NGX_SRT_SRV_CONF_OFFSET, 91 | offsetof(ngx_srt_core_srv_conf_t, srt_opts.recv_buf), 92 | NULL }, 93 | 94 | { ngx_string("recv_udp_buf"), 95 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_TAKE1, 96 | ngx_conf_set_size_slot, 97 | NGX_SRT_SRV_CONF_OFFSET, 98 | offsetof(ngx_srt_core_srv_conf_t, srt_opts.recv_udp_buf), 99 | NULL }, 100 | 101 | { ngx_string("recv_latency"), 102 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_TAKE1, 103 | ngx_conf_set_msec_slot, 104 | NGX_SRT_SRV_CONF_OFFSET, 105 | offsetof(ngx_srt_core_srv_conf_t, srt_opts.recv_latency), 106 | NULL }, 107 | 108 | 109 | { ngx_string("send_buf"), 110 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_TAKE1, 111 | ngx_conf_set_size_slot, 112 | NGX_SRT_SRV_CONF_OFFSET, 113 | offsetof(ngx_srt_core_srv_conf_t, srt_opts.send_buf), 114 | NULL }, 115 | 116 | { ngx_string("send_udp_buf"), 117 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_TAKE1, 118 | ngx_conf_set_size_slot, 119 | NGX_SRT_SRV_CONF_OFFSET, 120 | offsetof(ngx_srt_core_srv_conf_t, srt_opts.send_udp_buf), 121 | NULL }, 122 | 123 | { ngx_string("send_latency"), 124 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_TAKE1, 125 | ngx_conf_set_msec_slot, 126 | NGX_SRT_SRV_CONF_OFFSET, 127 | offsetof(ngx_srt_core_srv_conf_t, srt_opts.send_latency), 128 | NULL }, 129 | 130 | { ngx_string("passphrase"), 131 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_TAKE1, 132 | ngx_srt_set_complex_value_slot, 133 | NGX_SRT_SRV_CONF_OFFSET, 134 | offsetof(ngx_srt_core_srv_conf_t, passphrase), 135 | NULL }, 136 | 137 | ngx_null_command 138 | }; 139 | 140 | 141 | static ngx_srt_module_t ngx_srt_core_module_ctx = { 142 | ngx_srt_core_preconfiguration, /* preconfiguration */ 143 | NULL, /* postconfiguration */ 144 | 145 | ngx_srt_core_create_main_conf, /* create main configuration */ 146 | ngx_srt_core_init_main_conf, /* init main configuration */ 147 | 148 | ngx_srt_core_create_srv_conf, /* create server configuration */ 149 | ngx_srt_core_merge_srv_conf /* merge server configuration */ 150 | }; 151 | 152 | 153 | ngx_module_t ngx_srt_core_module = { 154 | NGX_MODULE_V1, 155 | &ngx_srt_core_module_ctx, /* module context */ 156 | ngx_srt_core_commands, /* module directives */ 157 | NGX_SRT_MODULE, /* module type */ 158 | NULL, /* init master */ 159 | NULL, /* init module */ 160 | NULL, /* init process */ 161 | NULL, /* init thread */ 162 | NULL, /* exit thread */ 163 | NULL, /* exit process */ 164 | NULL, /* exit master */ 165 | NGX_MODULE_V1_PADDING 166 | }; 167 | 168 | 169 | static ngx_int_t 170 | ngx_srt_core_preconfiguration(ngx_conf_t *cf) 171 | { 172 | return ngx_srt_variables_add_core_vars(cf); 173 | } 174 | 175 | 176 | static void * 177 | ngx_srt_core_create_main_conf(ngx_conf_t *cf) 178 | { 179 | ngx_srt_core_main_conf_t *cmcf; 180 | 181 | cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_srt_core_main_conf_t)); 182 | if (cmcf == NULL) { 183 | return NULL; 184 | } 185 | 186 | if (ngx_array_init(&cmcf->servers, cf->pool, 4, 187 | sizeof(ngx_srt_core_srv_conf_t *)) 188 | != NGX_OK) 189 | { 190 | return NULL; 191 | } 192 | 193 | if (ngx_array_init(&cmcf->listening, cf->pool, 4, sizeof(ngx_listening_t)) 194 | != NGX_OK) 195 | { 196 | return NULL; 197 | } 198 | 199 | cmcf->variables_hash_max_size = NGX_CONF_UNSET_UINT; 200 | cmcf->variables_hash_bucket_size = NGX_CONF_UNSET_UINT; 201 | 202 | return cmcf; 203 | } 204 | 205 | 206 | static char * 207 | ngx_srt_core_init_main_conf(ngx_conf_t *cf, void *conf) 208 | { 209 | ngx_srt_core_main_conf_t *cmcf = conf; 210 | 211 | ngx_conf_init_uint_value(cmcf->variables_hash_max_size, 1024); 212 | ngx_conf_init_uint_value(cmcf->variables_hash_bucket_size, 64); 213 | 214 | cmcf->variables_hash_bucket_size = 215 | ngx_align(cmcf->variables_hash_bucket_size, ngx_cacheline_size); 216 | 217 | if (cmcf->ncaptures) { 218 | cmcf->ncaptures = (cmcf->ncaptures + 1) * 3; 219 | } 220 | 221 | return NGX_CONF_OK; 222 | } 223 | 224 | 225 | static void * 226 | ngx_srt_core_create_srv_conf(ngx_conf_t *cf) 227 | { 228 | ngx_srt_core_srv_conf_t *cscf; 229 | 230 | cscf = ngx_pcalloc(cf->pool, sizeof(ngx_srt_core_srv_conf_t)); 231 | if (cscf == NULL) { 232 | return NULL; 233 | } 234 | 235 | /* 236 | * set by ngx_pcalloc(): 237 | * 238 | * cscf->handler = NULL; 239 | * cscf->error_log = NULL; 240 | * cscf->listen = NULL; 241 | */ 242 | 243 | cscf->file_name = cf->conf_file->file.name.data; 244 | cscf->line = cf->conf_file->line; 245 | cscf->in_buf_size = NGX_CONF_UNSET_SIZE; 246 | 247 | ngx_memset(&cscf->srt_opts, 0xff, sizeof(cscf->srt_opts)); 248 | 249 | return cscf; 250 | } 251 | 252 | 253 | static char * 254 | ngx_srt_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) 255 | { 256 | ngx_srt_core_srv_conf_t *prev = parent; 257 | ngx_srt_core_srv_conf_t *conf = child; 258 | 259 | if (conf->handler == NULL) { 260 | ngx_log_error(NGX_LOG_EMERG, cf->log, 0, 261 | "no handler for server in %s:%ui", 262 | conf->file_name, conf->line); 263 | return NGX_CONF_ERROR; 264 | } 265 | 266 | if (conf->error_log == NULL) { 267 | if (prev->error_log) { 268 | conf->error_log = prev->error_log; 269 | 270 | } else { 271 | conf->error_log = &cf->cycle->new_log; 272 | } 273 | } 274 | 275 | ngx_conf_merge_size_value(conf->in_buf_size, prev->in_buf_size, 64 * 1024); 276 | 277 | ngx_srt_merge_options(&conf->srt_opts, &prev->srt_opts); 278 | 279 | if (conf->passphrase == NULL) { 280 | conf->passphrase = prev->passphrase; 281 | } 282 | 283 | return NGX_CONF_OK; 284 | } 285 | 286 | 287 | static char * 288 | ngx_srt_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 289 | { 290 | ngx_srt_core_srv_conf_t *cscf = conf; 291 | 292 | return ngx_log_set_log(cf, &cscf->error_log); 293 | } 294 | 295 | 296 | static char * 297 | ngx_srt_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 298 | { 299 | char *rv; 300 | void *mconf; 301 | ngx_uint_t m; 302 | ngx_conf_t pcf; 303 | ngx_srt_module_t *module; 304 | ngx_srt_conf_ctx_t *ctx, *srt_ctx; 305 | ngx_srt_core_srv_conf_t *cscf, **cscfp; 306 | ngx_srt_core_main_conf_t *cmcf; 307 | 308 | ctx = ngx_pcalloc(cf->pool, sizeof(ngx_srt_conf_ctx_t)); 309 | if (ctx == NULL) { 310 | return NGX_CONF_ERROR; 311 | } 312 | 313 | srt_ctx = cf->ctx; 314 | ctx->main_conf = srt_ctx->main_conf; 315 | 316 | /* the server{}'s srv_conf */ 317 | 318 | ctx->srv_conf = ngx_pcalloc(cf->pool, 319 | sizeof(void *) * ngx_srt_max_module); 320 | if (ctx->srv_conf == NULL) { 321 | return NGX_CONF_ERROR; 322 | } 323 | 324 | for (m = 0; cf->cycle->modules[m]; m++) { 325 | if (cf->cycle->modules[m]->type != NGX_SRT_MODULE) { 326 | continue; 327 | } 328 | 329 | module = cf->cycle->modules[m]->ctx; 330 | 331 | if (module->create_srv_conf) { 332 | mconf = module->create_srv_conf(cf); 333 | if (mconf == NULL) { 334 | return NGX_CONF_ERROR; 335 | } 336 | 337 | ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf; 338 | } 339 | } 340 | 341 | /* the server configuration context */ 342 | 343 | cscf = ctx->srv_conf[ngx_srt_core_module.ctx_index]; 344 | cscf->ctx = ctx; 345 | 346 | cmcf = ctx->main_conf[ngx_srt_core_module.ctx_index]; 347 | 348 | cscfp = ngx_array_push(&cmcf->servers); 349 | if (cscfp == NULL) { 350 | return NGX_CONF_ERROR; 351 | } 352 | 353 | *cscfp = cscf; 354 | 355 | 356 | /* parse inside server{} */ 357 | 358 | pcf = *cf; 359 | cf->ctx = ctx; 360 | cf->cmd_type = NGX_SRT_SRV_CONF; 361 | 362 | rv = ngx_conf_parse(cf, NULL); 363 | 364 | *cf = pcf; 365 | 366 | if (rv == NGX_CONF_OK && !cscf->listen) { 367 | ngx_log_error(NGX_LOG_EMERG, cf->log, 0, 368 | "no \"listen\" is defined for server in %s:%ui", 369 | cscf->file_name, cscf->line); 370 | return NGX_CONF_ERROR; 371 | } 372 | 373 | return rv; 374 | } 375 | 376 | 377 | static char * 378 | ngx_srt_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 379 | { 380 | ngx_srt_core_srv_conf_t *cscf = conf; 381 | 382 | ngx_str_t *value; 383 | ngx_url_t u; 384 | ngx_uint_t i, n; 385 | ngx_srt_listen_t *ls, *als; 386 | 387 | value = cf->args->elts; 388 | 389 | ngx_memzero(&u, sizeof(ngx_url_t)); 390 | 391 | u.url = value[1]; 392 | u.listen = 1; 393 | 394 | if (ngx_parse_url(cf->pool, &u) != NGX_OK) { 395 | if (u.err) { 396 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 397 | "%s in \"%V\" of the \"listen\" directive", 398 | u.err, &u.url); 399 | } 400 | 401 | return NGX_CONF_ERROR; 402 | } 403 | 404 | if (cscf->listen == NULL) { 405 | cscf->listen = ngx_array_create(cf->pool, 4, 406 | sizeof(ngx_srt_listen_t)); 407 | if (cscf->listen == NULL) { 408 | return NGX_CONF_ERROR; 409 | } 410 | } 411 | 412 | ls = ngx_array_push_n(cscf->listen, u.naddrs); 413 | if (ls == NULL) { 414 | return NGX_CONF_ERROR; 415 | } 416 | 417 | ngx_memzero(ls, sizeof(ngx_srt_listen_t)); 418 | 419 | ls->backlog = NGX_LISTEN_BACKLOG; 420 | ls->type = SOCK_DGRAM; 421 | ls->ctx = cf->ctx; 422 | 423 | #if (NGX_HAVE_INET6) 424 | ls->ipv6only = 1; 425 | #endif 426 | 427 | for (i = 2; i < cf->args->nelts; i++) { 428 | 429 | if (ngx_strcmp(value[i].data, "bind") == 0) { 430 | ls->bind = 1; 431 | continue; 432 | } 433 | 434 | if (ngx_strncmp(value[i].data, "backlog=", 8) == 0) { 435 | ls->backlog = ngx_atoi(value[i].data + 8, value[i].len - 8); 436 | ls->bind = 1; 437 | 438 | if (ls->backlog == NGX_ERROR || ls->backlog == 0) { 439 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 440 | "invalid backlog \"%V\"", &value[i]); 441 | return NGX_CONF_ERROR; 442 | } 443 | 444 | continue; 445 | } 446 | 447 | if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) { 448 | #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) 449 | if (ngx_strcmp(&value[i].data[10], "n") == 0) { 450 | ls->ipv6only = 1; 451 | 452 | } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) { 453 | ls->ipv6only = 0; 454 | 455 | } else { 456 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 457 | "invalid ipv6only flags \"%s\"", 458 | &value[i].data[9]); 459 | return NGX_CONF_ERROR; 460 | } 461 | 462 | ls->bind = 1; 463 | continue; 464 | #else 465 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 466 | "bind ipv6only is not supported " 467 | "on this platform"); 468 | return NGX_CONF_ERROR; 469 | #endif 470 | } 471 | 472 | if (ngx_strcmp(value[i].data, "reuseport") == 0) { 473 | #if (NGX_HAVE_REUSEPORT) 474 | ls->reuseport = 1; 475 | ls->bind = 1; 476 | #else 477 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 478 | "reuseport is not supported " 479 | "on this platform, ignored"); 480 | #endif 481 | continue; 482 | } 483 | 484 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 485 | "invalid parameter \"%V\"", &value[i]); 486 | return NGX_CONF_ERROR; 487 | } 488 | 489 | als = cscf->listen->elts; 490 | 491 | for (n = 0; n < u.naddrs; n++) { 492 | ls[n] = ls[0]; 493 | 494 | ls[n].sockaddr = u.addrs[n].sockaddr; 495 | ls[n].socklen = u.addrs[n].socklen; 496 | ls[n].addr_text = u.addrs[n].name; 497 | 498 | /* Note: forcing wildcard to off, if it is on, nginx enables 499 | IP_PKTINFO on the socket, which breaks libsrt */ 500 | ls[n].wildcard = 0; 501 | 502 | for (i = 0; i < cscf->listen->nelts - u.naddrs + n; i++) { 503 | if (ls[n].type != als[i].type) { 504 | continue; 505 | } 506 | 507 | if (ngx_cmp_sockaddr(als[i].sockaddr, als[i].socklen, 508 | ls[n].sockaddr, ls[n].socklen, 1) 509 | != NGX_OK) 510 | { 511 | continue; 512 | } 513 | 514 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 515 | "duplicate \"%V\" address and port pair", 516 | &ls[n].addr_text); 517 | return NGX_CONF_ERROR; 518 | } 519 | } 520 | 521 | return NGX_CONF_OK; 522 | } 523 | -------------------------------------------------------------------------------- /src/ngx_srt_map_module.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Igor Sysoev 4 | * Copyright (C) Nginx, Inc. 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | #include "ngx_srt.h" 11 | 12 | 13 | typedef struct { 14 | ngx_uint_t hash_max_size; 15 | ngx_uint_t hash_bucket_size; 16 | } ngx_srt_map_conf_t; 17 | 18 | 19 | typedef struct { 20 | ngx_hash_keys_arrays_t keys; 21 | 22 | ngx_array_t *values_hash; 23 | #if (NGX_PCRE) 24 | ngx_array_t regexes; 25 | #endif 26 | 27 | ngx_srt_variable_value_t *default_value; 28 | ngx_conf_t *cf; 29 | unsigned hostnames:1; 30 | unsigned no_cacheable:1; 31 | } ngx_srt_map_conf_ctx_t; 32 | 33 | 34 | typedef struct { 35 | ngx_srt_map_t map; 36 | ngx_srt_complex_value_t value; 37 | ngx_srt_variable_value_t *default_value; 38 | ngx_uint_t hostnames; /* unsigned hostnames:1 */ 39 | } ngx_srt_map_ctx_t; 40 | 41 | 42 | static int ngx_libc_cdecl ngx_srt_map_cmp_dns_wildcards(const void *one, 43 | const void *two); 44 | static void *ngx_srt_map_create_conf(ngx_conf_t *cf); 45 | static char *ngx_srt_map_block(ngx_conf_t *cf, ngx_command_t *cmd, 46 | void *conf); 47 | static char *ngx_srt_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); 48 | 49 | 50 | static ngx_command_t ngx_srt_map_commands[] = { 51 | 52 | { ngx_string("map"), 53 | NGX_SRT_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2, 54 | ngx_srt_map_block, 55 | NGX_SRT_MAIN_CONF_OFFSET, 56 | 0, 57 | NULL }, 58 | 59 | { ngx_string("map_hash_max_size"), 60 | NGX_SRT_MAIN_CONF|NGX_CONF_TAKE1, 61 | ngx_conf_set_num_slot, 62 | NGX_SRT_MAIN_CONF_OFFSET, 63 | offsetof(ngx_srt_map_conf_t, hash_max_size), 64 | NULL }, 65 | 66 | { ngx_string("map_hash_bucket_size"), 67 | NGX_SRT_MAIN_CONF|NGX_CONF_TAKE1, 68 | ngx_conf_set_num_slot, 69 | NGX_SRT_MAIN_CONF_OFFSET, 70 | offsetof(ngx_srt_map_conf_t, hash_bucket_size), 71 | NULL }, 72 | 73 | ngx_null_command 74 | }; 75 | 76 | 77 | static ngx_srt_module_t ngx_srt_map_module_ctx = { 78 | NULL, /* preconfiguration */ 79 | NULL, /* postconfiguration */ 80 | 81 | ngx_srt_map_create_conf, /* create main configuration */ 82 | NULL, /* init main configuration */ 83 | 84 | NULL, /* create server configuration */ 85 | NULL /* merge server configuration */ 86 | }; 87 | 88 | 89 | ngx_module_t ngx_srt_map_module = { 90 | NGX_MODULE_V1, 91 | &ngx_srt_map_module_ctx, /* module context */ 92 | ngx_srt_map_commands, /* module directives */ 93 | NGX_SRT_MODULE, /* module type */ 94 | NULL, /* init master */ 95 | NULL, /* init module */ 96 | NULL, /* init process */ 97 | NULL, /* init thread */ 98 | NULL, /* exit thread */ 99 | NULL, /* exit process */ 100 | NULL, /* exit master */ 101 | NGX_MODULE_V1_PADDING 102 | }; 103 | 104 | 105 | static ngx_int_t 106 | ngx_srt_map_variable(ngx_srt_session_t *s, ngx_srt_variable_value_t *v, 107 | uintptr_t data) 108 | { 109 | ngx_srt_map_ctx_t *map = (ngx_srt_map_ctx_t *) data; 110 | 111 | ngx_str_t val, str; 112 | ngx_srt_complex_value_t *cv; 113 | ngx_srt_variable_value_t *value; 114 | 115 | ngx_log_debug0(NGX_LOG_DEBUG_SRT, s->connection->log, 0, 116 | "srt map started"); 117 | 118 | if (ngx_srt_complex_value(s, &map->value, &val) != NGX_OK) { 119 | return NGX_ERROR; 120 | } 121 | 122 | if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') { 123 | val.len--; 124 | } 125 | 126 | value = ngx_srt_map_find(s, &map->map, &val); 127 | 128 | if (value == NULL) { 129 | value = map->default_value; 130 | } 131 | 132 | if (!value->valid) { 133 | cv = (ngx_srt_complex_value_t *) value->data; 134 | 135 | if (ngx_srt_complex_value(s, cv, &str) != NGX_OK) { 136 | return NGX_ERROR; 137 | } 138 | 139 | v->valid = 1; 140 | v->no_cacheable = 0; 141 | v->not_found = 0; 142 | v->len = str.len; 143 | v->data = str.data; 144 | 145 | } else { 146 | *v = *value; 147 | } 148 | 149 | ngx_log_debug2(NGX_LOG_DEBUG_SRT, s->connection->log, 0, 150 | "srt map: \"%V\" \"%v\"", &val, v); 151 | 152 | return NGX_OK; 153 | } 154 | 155 | 156 | static void * 157 | ngx_srt_map_create_conf(ngx_conf_t *cf) 158 | { 159 | ngx_srt_map_conf_t *mcf; 160 | 161 | mcf = ngx_palloc(cf->pool, sizeof(ngx_srt_map_conf_t)); 162 | if (mcf == NULL) { 163 | return NULL; 164 | } 165 | 166 | mcf->hash_max_size = NGX_CONF_UNSET_UINT; 167 | mcf->hash_bucket_size = NGX_CONF_UNSET_UINT; 168 | 169 | return mcf; 170 | } 171 | 172 | 173 | static char * 174 | ngx_srt_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 175 | { 176 | ngx_srt_map_conf_t *mcf = conf; 177 | 178 | char *rv; 179 | ngx_str_t *value, name; 180 | ngx_conf_t save; 181 | ngx_pool_t *pool; 182 | ngx_hash_init_t hash; 183 | ngx_srt_map_ctx_t *map; 184 | ngx_srt_variable_t *var; 185 | ngx_srt_map_conf_ctx_t ctx; 186 | ngx_srt_compile_complex_value_t ccv; 187 | 188 | if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) { 189 | mcf->hash_max_size = 2048; 190 | } 191 | 192 | if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) { 193 | mcf->hash_bucket_size = ngx_cacheline_size; 194 | 195 | } else { 196 | mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size, 197 | ngx_cacheline_size); 198 | } 199 | 200 | map = ngx_pcalloc(cf->pool, sizeof(ngx_srt_map_ctx_t)); 201 | if (map == NULL) { 202 | return NGX_CONF_ERROR; 203 | } 204 | 205 | value = cf->args->elts; 206 | 207 | ngx_memzero(&ccv, sizeof(ngx_srt_compile_complex_value_t)); 208 | 209 | ccv.cf = cf; 210 | ccv.value = &value[1]; 211 | ccv.complex_value = &map->value; 212 | 213 | if (ngx_srt_compile_complex_value(&ccv) != NGX_OK) { 214 | return NGX_CONF_ERROR; 215 | } 216 | 217 | name = value[2]; 218 | 219 | if (name.data[0] != '$') { 220 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 221 | "invalid variable name \"%V\"", &name); 222 | return NGX_CONF_ERROR; 223 | } 224 | 225 | name.len--; 226 | name.data++; 227 | 228 | var = ngx_srt_add_variable(cf, &name, NGX_SRT_VAR_CHANGEABLE); 229 | if (var == NULL) { 230 | return NGX_CONF_ERROR; 231 | } 232 | 233 | var->get_handler = ngx_srt_map_variable; 234 | var->data = (uintptr_t) map; 235 | 236 | pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log); 237 | if (pool == NULL) { 238 | return NGX_CONF_ERROR; 239 | } 240 | 241 | ctx.keys.pool = cf->pool; 242 | ctx.keys.temp_pool = pool; 243 | 244 | if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) { 245 | ngx_destroy_pool(pool); 246 | return NGX_CONF_ERROR; 247 | } 248 | 249 | ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize); 250 | if (ctx.values_hash == NULL) { 251 | ngx_destroy_pool(pool); 252 | return NGX_CONF_ERROR; 253 | } 254 | 255 | #if (NGX_PCRE) 256 | if (ngx_array_init(&ctx.regexes, cf->pool, 2, 257 | sizeof(ngx_srt_map_regex_t)) 258 | != NGX_OK) 259 | { 260 | ngx_destroy_pool(pool); 261 | return NGX_CONF_ERROR; 262 | } 263 | #endif 264 | 265 | ctx.default_value = NULL; 266 | ctx.cf = &save; 267 | ctx.hostnames = 0; 268 | ctx.no_cacheable = 0; 269 | 270 | save = *cf; 271 | cf->pool = pool; 272 | cf->ctx = &ctx; 273 | cf->handler = ngx_srt_map; 274 | cf->handler_conf = conf; 275 | 276 | rv = ngx_conf_parse(cf, NULL); 277 | 278 | *cf = save; 279 | 280 | if (rv != NGX_CONF_OK) { 281 | ngx_destroy_pool(pool); 282 | return rv; 283 | } 284 | 285 | if (ctx.no_cacheable) { 286 | var->flags |= NGX_SRT_VAR_NOCACHEABLE; 287 | } 288 | 289 | map->default_value = ctx.default_value ? ctx.default_value: 290 | &ngx_srt_variable_null_value; 291 | 292 | map->hostnames = ctx.hostnames; 293 | 294 | hash.key = ngx_hash_key_lc; 295 | hash.max_size = mcf->hash_max_size; 296 | hash.bucket_size = mcf->hash_bucket_size; 297 | hash.name = "map_hash"; 298 | hash.pool = cf->pool; 299 | 300 | if (ctx.keys.keys.nelts) { 301 | hash.hash = &map->map.hash.hash; 302 | hash.temp_pool = NULL; 303 | 304 | if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts) 305 | != NGX_OK) 306 | { 307 | ngx_destroy_pool(pool); 308 | return NGX_CONF_ERROR; 309 | } 310 | } 311 | 312 | if (ctx.keys.dns_wc_head.nelts) { 313 | 314 | ngx_qsort(ctx.keys.dns_wc_head.elts, 315 | (size_t) ctx.keys.dns_wc_head.nelts, 316 | sizeof(ngx_hash_key_t), ngx_srt_map_cmp_dns_wildcards); 317 | 318 | hash.hash = NULL; 319 | hash.temp_pool = pool; 320 | 321 | if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts, 322 | ctx.keys.dns_wc_head.nelts) 323 | != NGX_OK) 324 | { 325 | ngx_destroy_pool(pool); 326 | return NGX_CONF_ERROR; 327 | } 328 | 329 | map->map.hash.wc_head = (ngx_hash_wildcard_t *) hash.hash; 330 | } 331 | 332 | if (ctx.keys.dns_wc_tail.nelts) { 333 | 334 | ngx_qsort(ctx.keys.dns_wc_tail.elts, 335 | (size_t) ctx.keys.dns_wc_tail.nelts, 336 | sizeof(ngx_hash_key_t), ngx_srt_map_cmp_dns_wildcards); 337 | 338 | hash.hash = NULL; 339 | hash.temp_pool = pool; 340 | 341 | if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts, 342 | ctx.keys.dns_wc_tail.nelts) 343 | != NGX_OK) 344 | { 345 | ngx_destroy_pool(pool); 346 | return NGX_CONF_ERROR; 347 | } 348 | 349 | map->map.hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash; 350 | } 351 | 352 | #if (NGX_PCRE) 353 | 354 | if (ctx.regexes.nelts) { 355 | map->map.regex = ctx.regexes.elts; 356 | map->map.nregex = ctx.regexes.nelts; 357 | } 358 | 359 | #endif 360 | 361 | ngx_destroy_pool(pool); 362 | 363 | return rv; 364 | } 365 | 366 | 367 | static int ngx_libc_cdecl 368 | ngx_srt_map_cmp_dns_wildcards(const void *one, const void *two) 369 | { 370 | ngx_hash_key_t *first, *second; 371 | 372 | first = (ngx_hash_key_t *) one; 373 | second = (ngx_hash_key_t *) two; 374 | 375 | return ngx_dns_strcmp(first->key.data, second->key.data); 376 | } 377 | 378 | 379 | static char * 380 | ngx_srt_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) 381 | { 382 | u_char *data; 383 | size_t len; 384 | ngx_int_t rv; 385 | ngx_str_t *value, v; 386 | ngx_uint_t i, key; 387 | ngx_srt_map_conf_ctx_t *ctx; 388 | ngx_srt_complex_value_t cv, *cvp; 389 | ngx_srt_variable_value_t *var, **vp; 390 | ngx_srt_compile_complex_value_t ccv; 391 | 392 | ctx = cf->ctx; 393 | 394 | value = cf->args->elts; 395 | 396 | if (cf->args->nelts == 1 397 | && ngx_strcmp(value[0].data, "hostnames") == 0) 398 | { 399 | ctx->hostnames = 1; 400 | return NGX_CONF_OK; 401 | } 402 | 403 | if (cf->args->nelts == 1 404 | && ngx_strcmp(value[0].data, "volatile") == 0) 405 | { 406 | ctx->no_cacheable = 1; 407 | return NGX_CONF_OK; 408 | } 409 | 410 | if (cf->args->nelts != 2) { 411 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 412 | "invalid number of the map parameters"); 413 | return NGX_CONF_ERROR; 414 | } 415 | 416 | if (ngx_strcmp(value[0].data, "include") == 0) { 417 | return ngx_conf_include(cf, dummy, conf); 418 | } 419 | 420 | key = 0; 421 | 422 | for (i = 0; i < value[1].len; i++) { 423 | key = ngx_hash(key, value[1].data[i]); 424 | } 425 | 426 | key %= ctx->keys.hsize; 427 | 428 | vp = ctx->values_hash[key].elts; 429 | 430 | if (vp) { 431 | for (i = 0; i < ctx->values_hash[key].nelts; i++) { 432 | 433 | if (vp[i]->valid) { 434 | data = vp[i]->data; 435 | len = vp[i]->len; 436 | 437 | } else { 438 | cvp = (ngx_srt_complex_value_t *) vp[i]->data; 439 | data = cvp->value.data; 440 | len = cvp->value.len; 441 | } 442 | 443 | if (value[1].len != len) { 444 | continue; 445 | } 446 | 447 | if (ngx_strncmp(value[1].data, data, len) == 0) { 448 | var = vp[i]; 449 | goto found; 450 | } 451 | } 452 | 453 | } else { 454 | if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4, 455 | sizeof(ngx_srt_variable_value_t *)) 456 | != NGX_OK) 457 | { 458 | return NGX_CONF_ERROR; 459 | } 460 | } 461 | 462 | var = ngx_palloc(ctx->keys.pool, sizeof(ngx_srt_variable_value_t)); 463 | if (var == NULL) { 464 | return NGX_CONF_ERROR; 465 | } 466 | 467 | v.len = value[1].len; 468 | v.data = ngx_pstrdup(ctx->keys.pool, &value[1]); 469 | if (v.data == NULL) { 470 | return NGX_CONF_ERROR; 471 | } 472 | 473 | ngx_memzero(&ccv, sizeof(ngx_srt_compile_complex_value_t)); 474 | 475 | ccv.cf = ctx->cf; 476 | ccv.value = &v; 477 | ccv.complex_value = &cv; 478 | 479 | if (ngx_srt_compile_complex_value(&ccv) != NGX_OK) { 480 | return NGX_CONF_ERROR; 481 | } 482 | 483 | if (cv.lengths != NULL) { 484 | cvp = ngx_palloc(ctx->keys.pool, sizeof(ngx_srt_complex_value_t)); 485 | if (cvp == NULL) { 486 | return NGX_CONF_ERROR; 487 | } 488 | 489 | *cvp = cv; 490 | 491 | var->len = 0; 492 | var->data = (u_char *) cvp; 493 | var->valid = 0; 494 | 495 | } else { 496 | var->len = v.len; 497 | var->data = v.data; 498 | var->valid = 1; 499 | } 500 | 501 | var->no_cacheable = 0; 502 | var->not_found = 0; 503 | 504 | vp = ngx_array_push(&ctx->values_hash[key]); 505 | if (vp == NULL) { 506 | return NGX_CONF_ERROR; 507 | } 508 | 509 | *vp = var; 510 | 511 | found: 512 | 513 | if (ngx_strcmp(value[0].data, "default") == 0) { 514 | 515 | if (ctx->default_value) { 516 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 517 | "duplicate default map parameter"); 518 | return NGX_CONF_ERROR; 519 | } 520 | 521 | ctx->default_value = var; 522 | 523 | return NGX_CONF_OK; 524 | } 525 | 526 | #if (NGX_PCRE) 527 | 528 | if (value[0].len && value[0].data[0] == '~') { 529 | ngx_regex_compile_t rc; 530 | ngx_srt_map_regex_t *regex; 531 | u_char errstr[NGX_MAX_CONF_ERRSTR]; 532 | 533 | regex = ngx_array_push(&ctx->regexes); 534 | if (regex == NULL) { 535 | return NGX_CONF_ERROR; 536 | } 537 | 538 | value[0].len--; 539 | value[0].data++; 540 | 541 | ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); 542 | 543 | if (value[0].data[0] == '*') { 544 | value[0].len--; 545 | value[0].data++; 546 | rc.options = NGX_REGEX_CASELESS; 547 | } 548 | 549 | rc.pattern = value[0]; 550 | rc.err.len = NGX_MAX_CONF_ERRSTR; 551 | rc.err.data = errstr; 552 | 553 | regex->regex = ngx_srt_regex_compile(ctx->cf, &rc); 554 | if (regex->regex == NULL) { 555 | return NGX_CONF_ERROR; 556 | } 557 | 558 | regex->value = var; 559 | 560 | return NGX_CONF_OK; 561 | } 562 | 563 | #endif 564 | 565 | if (value[0].len && value[0].data[0] == '\\') { 566 | value[0].len--; 567 | value[0].data++; 568 | } 569 | 570 | rv = ngx_hash_add_key(&ctx->keys, &value[0], var, 571 | (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0); 572 | 573 | if (rv == NGX_OK) { 574 | return NGX_CONF_OK; 575 | } 576 | 577 | if (rv == NGX_DECLINED) { 578 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 579 | "invalid hostname or wildcard \"%V\"", &value[0]); 580 | } 581 | 582 | if (rv == NGX_BUSY) { 583 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 584 | "conflicting parameter \"%V\"", &value[0]); 585 | } 586 | 587 | return NGX_CONF_ERROR; 588 | } 589 | -------------------------------------------------------------------------------- /src/ngx_srt_proxy_module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "ngx_srt.h" 5 | 6 | 7 | typedef struct { 8 | ngx_url_t *url; 9 | ngx_msec_t connect_timeout; 10 | ngx_msec_t timeout; 11 | size_t buffer_size; 12 | ngx_flag_t proxy_protocol; 13 | ngx_flag_t tcp_nodelay; 14 | ngx_srt_complex_value_t *header; 15 | } ngx_srt_proxy_srv_conf_t; 16 | 17 | 18 | typedef struct { 19 | ngx_srt_upstream_state_t *state; 20 | ngx_msec_t start_time; 21 | unsigned proxy_protocol:1; 22 | } ngx_srt_proxy_upstream_t; 23 | 24 | 25 | static void ngx_srt_proxy_connect(ngx_srt_session_t *s); 26 | static void ngx_srt_proxy_init_upstream(ngx_srt_session_t *s); 27 | static void ngx_srt_proxy_ngx_handler(ngx_event_t *ev); 28 | static void ngx_srt_proxy_srt_handler(ngx_event_t *ev); 29 | static void ngx_srt_proxy_connect_handler(ngx_event_t *ev); 30 | static ngx_int_t ngx_srt_proxy_test_connect(ngx_connection_t *c); 31 | static ngx_int_t ngx_srt_proxy_test_finalize(ngx_srt_conn_t *sc, 32 | ngx_uint_t from_upstream); 33 | static void ngx_srt_proxy_next_upstream(ngx_srt_session_t *s); 34 | static u_char *ngx_srt_proxy_log_error(ngx_log_t *log, u_char *buf, 35 | size_t len); 36 | 37 | static void *ngx_srt_proxy_create_srv_conf(ngx_conf_t *cf); 38 | static char *ngx_srt_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, 39 | void *child); 40 | static ngx_int_t ngx_srt_proxy_init(ngx_conf_t *cf); 41 | static char *ngx_srt_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, 42 | void *conf); 43 | 44 | 45 | static ngx_command_t ngx_srt_proxy_commands[] = { 46 | 47 | { ngx_string("proxy_pass"), 48 | NGX_SRT_SRV_CONF|NGX_CONF_TAKE1, 49 | ngx_srt_proxy_pass, 50 | NGX_SRT_SRV_CONF_OFFSET, 51 | 0, 52 | NULL }, 53 | 54 | { ngx_string("proxy_connect_timeout"), 55 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_TAKE1, 56 | ngx_conf_set_msec_slot, 57 | NGX_SRT_SRV_CONF_OFFSET, 58 | offsetof(ngx_srt_proxy_srv_conf_t, connect_timeout), 59 | NULL }, 60 | 61 | { ngx_string("proxy_timeout"), 62 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_TAKE1, 63 | ngx_conf_set_msec_slot, 64 | NGX_SRT_SRV_CONF_OFFSET, 65 | offsetof(ngx_srt_proxy_srv_conf_t, timeout), 66 | NULL }, 67 | 68 | { ngx_string("proxy_buffer_size"), 69 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_TAKE1, 70 | ngx_conf_set_size_slot, 71 | NGX_SRT_SRV_CONF_OFFSET, 72 | offsetof(ngx_srt_proxy_srv_conf_t, buffer_size), 73 | NULL }, 74 | 75 | { ngx_string("proxy_protocol"), 76 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_FLAG, 77 | ngx_conf_set_flag_slot, 78 | NGX_SRT_SRV_CONF_OFFSET, 79 | offsetof(ngx_srt_proxy_srv_conf_t, proxy_protocol), 80 | NULL }, 81 | 82 | { ngx_string("proxy_tcp_nodelay"), 83 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_FLAG, 84 | ngx_conf_set_flag_slot, 85 | NGX_SRT_SRV_CONF_OFFSET, 86 | offsetof(ngx_srt_proxy_srv_conf_t, tcp_nodelay), 87 | NULL }, 88 | 89 | { ngx_string("proxy_header"), 90 | NGX_SRT_MAIN_CONF|NGX_SRT_SRV_CONF|NGX_CONF_FLAG, 91 | ngx_srt_set_complex_value_slot, 92 | NGX_SRT_SRV_CONF_OFFSET, 93 | offsetof(ngx_srt_proxy_srv_conf_t, header), 94 | NULL }, 95 | 96 | ngx_null_command 97 | }; 98 | 99 | 100 | static ngx_srt_module_t ngx_srt_proxy_module_ctx = { 101 | NULL, /* preconfiguration */ 102 | ngx_srt_proxy_init, /* postconfiguration */ 103 | 104 | NULL, /* create main configuration */ 105 | NULL, /* init main configuration */ 106 | 107 | ngx_srt_proxy_create_srv_conf, /* create server configuration */ 108 | ngx_srt_proxy_merge_srv_conf /* merge server configuration */ 109 | }; 110 | 111 | 112 | ngx_module_t ngx_srt_proxy_module = { 113 | NGX_MODULE_V1, 114 | &ngx_srt_proxy_module_ctx, /* module context */ 115 | ngx_srt_proxy_commands, /* module directives */ 116 | NGX_SRT_MODULE, /* module type */ 117 | NULL, /* init master */ 118 | NULL, /* init module */ 119 | NULL, /* init process */ 120 | NULL, /* init thread */ 121 | NULL, /* exit thread */ 122 | NULL, /* exit process */ 123 | NULL, /* exit master */ 124 | NGX_MODULE_V1_PADDING 125 | }; 126 | 127 | 128 | static void 129 | ngx_srt_proxy_handler(ngx_srt_session_t *s) 130 | { 131 | ngx_srt_conn_t *sc; 132 | ngx_srt_stream_t *st; 133 | ngx_connection_t *c; 134 | ngx_srt_proxy_upstream_t *u; 135 | 136 | sc = s->sc; 137 | c = sc->connection; 138 | 139 | ngx_log_debug0(NGX_LOG_DEBUG_SRT, c->log, 0, 140 | "ngx_srt_proxy_handler: called"); 141 | 142 | st = ngx_pcalloc(c->pool, sizeof(ngx_srt_stream_t)); 143 | if (st == NULL) { 144 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 145 | return; 146 | } 147 | 148 | sc->stream = st; 149 | 150 | u = ngx_pcalloc(c->pool, sizeof(ngx_srt_proxy_upstream_t)); 151 | if (u == NULL) { 152 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 153 | return; 154 | } 155 | 156 | sc->upstream = u; 157 | 158 | s->upstream_states = ngx_array_create(c->pool, 1, 159 | sizeof(ngx_srt_upstream_state_t)); 160 | if (s->upstream_states == NULL) { 161 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 162 | return; 163 | } 164 | 165 | sc->log_handler = ngx_srt_proxy_log_error; 166 | 167 | c->write->handler = ngx_srt_proxy_srt_handler; 168 | c->read->handler = ngx_srt_proxy_srt_handler; 169 | 170 | ngx_srt_proxy_connect(s); 171 | } 172 | 173 | 174 | static void 175 | ngx_srt_proxy_connect(ngx_srt_session_t *s) 176 | { 177 | ngx_int_t rc; 178 | ngx_srt_conn_t *sc; 179 | ngx_connection_t *c, *pc; 180 | ngx_srt_stream_t *st; 181 | ngx_peer_connection_t *peer; 182 | ngx_srt_proxy_upstream_t *u; 183 | ngx_srt_proxy_srv_conf_t *pscf; 184 | 185 | sc = s->sc; 186 | c = sc->connection; 187 | 188 | c->log->action = "connecting to upstream"; 189 | 190 | pscf = ngx_srt_get_module_srv_conf(s, ngx_srt_proxy_module); 191 | 192 | st = sc->stream; 193 | st->connected = 0; 194 | 195 | u = sc->upstream; 196 | 197 | u->proxy_protocol = pscf->proxy_protocol; 198 | 199 | if (u->state) { 200 | u->state->response_time = ngx_current_msec - u->start_time; 201 | } 202 | 203 | u->state = ngx_array_push(s->upstream_states); 204 | if (u->state == NULL) { 205 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 206 | return; 207 | } 208 | 209 | ngx_memzero(u->state, sizeof(ngx_srt_upstream_state_t)); 210 | 211 | u->start_time = ngx_current_msec; 212 | 213 | u->state->connect_time = (ngx_msec_t) -1; 214 | u->state->first_byte_time = (ngx_msec_t) -1; 215 | u->state->response_time = (ngx_msec_t) -1; 216 | 217 | peer = ngx_pcalloc(c->pool, sizeof(*peer)); 218 | if (peer == NULL) { 219 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 220 | return; 221 | } 222 | 223 | peer->sockaddr = &pscf->url->sockaddr.sockaddr; 224 | peer->socklen = pscf->url->socklen; 225 | 226 | peer->name = &pscf->url->host; 227 | peer->get = ngx_event_get_peer; 228 | peer->log = c->log; 229 | peer->log_error = NGX_ERROR_ERR; 230 | 231 | peer->type = SOCK_STREAM; 232 | 233 | rc = ngx_event_connect_peer(peer); 234 | 235 | ngx_log_debug1(NGX_LOG_DEBUG_SRT, c->log, 0, 236 | "ngx_srt_proxy_connect: proxy connect: %i", rc); 237 | 238 | if (rc == NGX_ERROR) { 239 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 240 | return; 241 | } 242 | 243 | u->state->peer = peer->name; 244 | 245 | if (rc == NGX_BUSY) { 246 | ngx_log_error(NGX_LOG_ERR, c->log, 0, 247 | "ngx_srt_proxy_connect: no live upstreams"); 248 | ngx_srt_conn_finalize(sc, NGX_SRT_BAD_GATEWAY); 249 | return; 250 | } 251 | 252 | if (rc == NGX_DECLINED) { 253 | ngx_srt_proxy_next_upstream(s); 254 | return; 255 | } 256 | 257 | /* rc == NGX_OK || rc == NGX_AGAIN || rc == NGX_DONE */ 258 | pc = peer->connection; 259 | st->connection = pc; 260 | st->close_conn = 1; 261 | 262 | pc->data = sc; 263 | pc->log = c->log; 264 | pc->pool = c->pool; 265 | pc->read->log = c->log; 266 | pc->write->log = c->log; 267 | pc->addr_text = pscf->url->url; 268 | 269 | if (rc != NGX_AGAIN) { 270 | ngx_srt_proxy_init_upstream(s); 271 | return; 272 | } 273 | 274 | pc->read->handler = ngx_srt_proxy_connect_handler; 275 | pc->write->handler = ngx_srt_proxy_connect_handler; 276 | 277 | ngx_add_timer(pc->write, pscf->connect_timeout); 278 | } 279 | 280 | 281 | static void 282 | ngx_srt_proxy_init_upstream(ngx_srt_session_t *s) 283 | { 284 | u_char *p, *last; 285 | ngx_str_t header; 286 | ngx_srt_conn_t *sc; 287 | ngx_srt_stream_t *st; 288 | ngx_connection_t *c, *pc; 289 | ngx_log_handler_pt handler; 290 | ngx_srt_proxy_upstream_t *u; 291 | ngx_srt_proxy_srv_conf_t *pscf; 292 | 293 | sc = s->sc; 294 | st = sc->stream; 295 | pc = st->connection; 296 | 297 | pscf = ngx_srt_get_module_srv_conf(s, ngx_srt_proxy_module); 298 | 299 | if (pc->type == SOCK_STREAM 300 | && pscf->tcp_nodelay 301 | && ngx_tcp_nodelay(pc) != NGX_OK) 302 | { 303 | ngx_srt_proxy_next_upstream(s); 304 | return; 305 | } 306 | 307 | c = sc->connection; 308 | 309 | if (c->log->log_level >= NGX_LOG_INFO) { 310 | ngx_str_t str; 311 | u_char addr[NGX_SOCKADDR_STRLEN]; 312 | 313 | str.len = NGX_SOCKADDR_STRLEN; 314 | str.data = addr; 315 | 316 | if (ngx_connection_local_sockaddr(pc, &str, 1) == NGX_OK) { 317 | handler = c->log->handler; 318 | c->log->handler = NULL; 319 | 320 | ngx_log_error(NGX_LOG_INFO, c->log, 0, 321 | "ngx_srt_proxy_init_upstream: proxy %V connected to %V", 322 | &str, &pc->addr_text); 323 | 324 | c->log->handler = handler; 325 | } 326 | } 327 | 328 | u = sc->upstream; 329 | 330 | u->state->connect_time = ngx_current_msec - u->start_time; 331 | 332 | if (st->buf.start == NULL) { 333 | p = ngx_pnalloc(c->pool, pscf->buffer_size); 334 | if (p == NULL) { 335 | ngx_srt_conn_finalize(s->sc, NGX_SRT_INTERNAL_SERVER_ERROR); 336 | return; 337 | } 338 | 339 | st->buf.start = p; 340 | st->buf.end = p + pscf->buffer_size; 341 | st->buf.pos = p; 342 | st->buf.last = p; 343 | } 344 | 345 | 346 | if (pscf->header) { 347 | if (ngx_srt_complex_value(s, pscf->header, &header) != NGX_OK) { 348 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 349 | return; 350 | } 351 | 352 | if (header.len) { 353 | if (ngx_srt_conn_in_insert_head(sc, header.data, 354 | header.data + header.len) != NGX_OK) 355 | { 356 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 357 | return; 358 | } 359 | } 360 | } 361 | 362 | if (u->proxy_protocol) { 363 | ngx_log_debug0(NGX_LOG_DEBUG_SRT, c->log, 0, 364 | "ngx_srt_proxy_init_upstream: add PROXY protocol header"); 365 | 366 | p = ngx_pnalloc(c->pool, NGX_PROXY_PROTOCOL_MAX_HEADER); 367 | if (p == NULL) { 368 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 369 | return; 370 | } 371 | 372 | last = ngx_proxy_protocol_write(c, p, 373 | p + NGX_PROXY_PROTOCOL_MAX_HEADER); 374 | if (last == NULL) { 375 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 376 | return; 377 | } 378 | 379 | if (ngx_srt_conn_in_insert_head(sc, p, last) != NGX_OK) { 380 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 381 | return; 382 | } 383 | 384 | u->proxy_protocol = 0; 385 | } 386 | 387 | st->connected = 1; 388 | 389 | pc->read->handler = ngx_srt_proxy_ngx_handler; 390 | pc->write->handler = ngx_srt_proxy_ngx_handler; 391 | 392 | if (pc->read->ready) { 393 | ngx_post_event(pc->read, &ngx_posted_events); 394 | } 395 | 396 | ngx_srt_proxy_ngx_handler(pc->write); 397 | } 398 | 399 | 400 | static void 401 | ngx_srt_proxy_ngx_handler(ngx_event_t *ev) 402 | { 403 | ngx_uint_t from_srt; 404 | ngx_srt_conn_t *sc; 405 | ngx_srt_stream_t *st; 406 | ngx_connection_t *c, *pc; 407 | ngx_srt_session_t *s; 408 | ngx_srt_proxy_upstream_t *u; 409 | ngx_srt_proxy_srv_conf_t *pscf; 410 | 411 | pc = ev->data; 412 | sc = pc->data; 413 | s = sc->session; 414 | 415 | if (pc->close) { 416 | ngx_log_error(NGX_LOG_INFO, pc->log, 0, 417 | "ngx_srt_proxy_ngx_handler: shutdown timeout"); 418 | ngx_srt_conn_finalize(sc, NGX_SRT_OK); 419 | return; 420 | } 421 | 422 | c = sc->connection; 423 | 424 | if (ev->timedout) { 425 | ev->timedout = 0; 426 | 427 | ngx_connection_error(c, NGX_ETIMEDOUT, 428 | "ngx_srt_proxy_ngx_handler: connection timed out"); 429 | 430 | ngx_srt_conn_finalize(sc, NGX_SRT_OK); 431 | 432 | return; 433 | } 434 | 435 | from_srt = ev->write; 436 | 437 | if (from_srt) { 438 | ngx_srt_proxy_process_srt_to_ngx(sc); 439 | 440 | if (ngx_srt_proxy_test_finalize(sc, 0) == NGX_OK) { 441 | return; 442 | } 443 | 444 | if (ngx_handle_write_event(ev, 0) != NGX_OK) { 445 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 446 | return; 447 | } 448 | 449 | } else { 450 | ngx_srt_proxy_process_ngx_to_srt(sc); 451 | 452 | st = sc->stream; 453 | 454 | if (st->received) { 455 | u = sc->upstream; 456 | if (u->state->first_byte_time == (ngx_msec_t) -1) { 457 | u->state->first_byte_time = ngx_current_msec 458 | - u->start_time; 459 | } 460 | } 461 | 462 | if (ngx_srt_proxy_test_finalize(sc, 1) == NGX_OK) { 463 | return; 464 | } 465 | 466 | if (ngx_handle_read_event(ev, 0) != NGX_OK) { 467 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 468 | return; 469 | } 470 | } 471 | 472 | pscf = ngx_srt_get_module_srv_conf(s, ngx_srt_proxy_module); 473 | 474 | ngx_add_timer(c->write, pscf->timeout); 475 | } 476 | 477 | 478 | static void 479 | ngx_srt_proxy_srt_handler(ngx_event_t *ev) 480 | { 481 | ngx_srt_conn_t *sc; 482 | ngx_connection_t *c, *pc; 483 | ngx_srt_stream_t *st; 484 | ngx_srt_session_t *s; 485 | ngx_srt_proxy_srv_conf_t *pscf; 486 | 487 | c = ev->data; 488 | sc = c->data; 489 | 490 | st = sc->stream; 491 | if (!st->connected) { 492 | return; 493 | } 494 | 495 | pc = st->connection; 496 | 497 | if (ev->write) { 498 | ngx_srt_proxy_process_ngx_to_srt(sc); 499 | 500 | if (ngx_handle_read_event(pc->read, 0) != NGX_OK) { 501 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 502 | return; 503 | } 504 | 505 | } else { 506 | ngx_srt_proxy_process_srt_to_ngx(sc); 507 | 508 | if (ngx_handle_write_event(pc->write, 0) != NGX_OK) { 509 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 510 | return; 511 | } 512 | } 513 | 514 | s = sc->session; 515 | 516 | pscf = ngx_srt_get_module_srv_conf(s, ngx_srt_proxy_module); 517 | 518 | ngx_add_timer(c->write, pscf->timeout); 519 | } 520 | 521 | 522 | static void 523 | ngx_srt_proxy_connect_handler(ngx_event_t *ev) 524 | { 525 | ngx_srt_conn_t *sc; 526 | ngx_connection_t *pc; 527 | ngx_srt_session_t *s; 528 | 529 | pc = ev->data; 530 | sc = pc->data; 531 | s = sc->session; 532 | 533 | if (ev->timedout) { 534 | ngx_log_error(NGX_LOG_ERR, pc->log, NGX_ETIMEDOUT, 535 | "ngx_srt_proxy_connect_handler: upstream timed out"); 536 | goto failed; 537 | } 538 | 539 | ngx_del_timer(pc->write); 540 | 541 | ngx_log_debug0(NGX_LOG_DEBUG_SRT, pc->log, 0, 542 | "ngx_srt_proxy_connect_handler: called"); 543 | 544 | 545 | if (ngx_srt_proxy_test_connect(pc) != NGX_OK) { 546 | goto failed; 547 | } 548 | 549 | ngx_srt_proxy_init_upstream(s); 550 | 551 | return; 552 | 553 | failed: 554 | 555 | ngx_srt_proxy_next_upstream(s); 556 | } 557 | 558 | 559 | static ngx_int_t 560 | ngx_srt_proxy_test_connect(ngx_connection_t *c) 561 | { 562 | int err; 563 | socklen_t len; 564 | 565 | #if (NGX_HAVE_KQUEUE) 566 | 567 | if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { 568 | err = c->write->kq_errno ? c->write->kq_errno : c->read->kq_errno; 569 | 570 | if (err) { 571 | (void) ngx_connection_error(c, err, 572 | "kevent() reported that connect() failed"); 573 | return NGX_ERROR; 574 | } 575 | 576 | } else 577 | #endif 578 | { 579 | err = 0; 580 | len = sizeof(int); 581 | 582 | /* 583 | * BSDs and Linux return 0 and set a pending error in err 584 | * Solaris returns -1 and sets errno 585 | */ 586 | 587 | if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) 588 | == -1) 589 | { 590 | err = ngx_socket_errno; 591 | } 592 | 593 | if (err) { 594 | (void) ngx_connection_error(c, err, "connect() failed"); 595 | return NGX_ERROR; 596 | } 597 | } 598 | 599 | return NGX_OK; 600 | } 601 | 602 | 603 | static ngx_int_t 604 | ngx_srt_proxy_test_finalize(ngx_srt_conn_t *sc, ngx_uint_t from_upstream) 605 | { 606 | ngx_srt_stream_t *st; 607 | ngx_connection_t *c, *pc; 608 | ngx_log_handler_pt handler; 609 | 610 | c = sc->connection; 611 | st = sc->stream; 612 | pc = st->connected ? st->connection : NULL; 613 | 614 | c->log->action = "proxying connection"; 615 | 616 | if (pc == NULL 617 | || (!c->read->eof && !pc->read->eof) 618 | || (!c->read->eof && c->buffered) 619 | || (!pc->read->eof && pc->buffered)) 620 | { 621 | return NGX_DECLINED; 622 | } 623 | 624 | handler = c->log->handler; 625 | c->log->handler = NULL; 626 | 627 | ngx_log_error(NGX_LOG_INFO, c->log, 0, 628 | "ngx_srt_proxy_test_finalize: %s disconnected" 629 | ", bytes from/to client:%O/%O" 630 | ", bytes from/to upstream:%O/%O", 631 | from_upstream ? "upstream" : "client", 632 | sc->received, c->sent, st->received, pc ? pc->sent : 0); 633 | 634 | c->log->handler = handler; 635 | 636 | ngx_srt_conn_finalize(sc, NGX_SRT_OK); 637 | 638 | return NGX_OK; 639 | } 640 | 641 | 642 | static void 643 | ngx_srt_proxy_next_upstream(ngx_srt_session_t *s) 644 | { 645 | /* supporting only one upstream */ 646 | ngx_srt_conn_finalize(s->sc, NGX_SRT_BAD_GATEWAY); 647 | } 648 | 649 | 650 | static ngx_int_t 651 | ngx_srt_proxy_log_handler(ngx_srt_session_t *s) 652 | { 653 | ngx_srt_conn_t *sc; 654 | ngx_srt_stream_t *st; 655 | ngx_connection_t *pc; 656 | ngx_srt_proxy_upstream_t *u; 657 | 658 | sc = s->sc; 659 | 660 | u = sc->upstream; 661 | 662 | if (u->state) { 663 | if (u->state->response_time == (ngx_msec_t) -1) { 664 | u->state->response_time = ngx_current_msec - u->start_time; 665 | } 666 | 667 | st = sc->stream; 668 | pc = st->connection; 669 | 670 | if (pc) { 671 | u->state->bytes_received = st->received; 672 | u->state->bytes_sent = pc->sent; 673 | } 674 | } 675 | 676 | return NGX_OK; 677 | } 678 | 679 | 680 | static u_char * 681 | ngx_srt_proxy_log_error(ngx_log_t *log, u_char *buf, size_t len) 682 | { 683 | u_char *p; 684 | ngx_srt_conn_t *sc; 685 | ngx_srt_stream_t *st; 686 | ngx_connection_t *pc; 687 | ngx_srt_proxy_upstream_t *u; 688 | 689 | sc = log->data; 690 | 691 | st = sc->stream; 692 | 693 | p = buf; 694 | 695 | u = sc->upstream; 696 | 697 | if (u->state && u->state->peer) { 698 | p = ngx_snprintf(p, len, ", upstream: \"%V\"", u->state->peer); 699 | len -= p - buf; 700 | } 701 | 702 | pc = st->connection; 703 | 704 | p = ngx_snprintf(p, len, 705 | ", bytes from/to client:%O/%O" 706 | ", bytes from/to upstream:%O/%O", 707 | sc->received, sc->connection->sent, 708 | st->received, pc ? pc->sent : 0); 709 | 710 | return p; 711 | } 712 | 713 | 714 | static void * 715 | ngx_srt_proxy_create_srv_conf(ngx_conf_t *cf) 716 | { 717 | ngx_srt_proxy_srv_conf_t *conf; 718 | 719 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_srt_proxy_srv_conf_t)); 720 | if (conf == NULL) { 721 | return NULL; 722 | } 723 | 724 | conf->connect_timeout = NGX_CONF_UNSET_MSEC; 725 | conf->timeout = NGX_CONF_UNSET_MSEC; 726 | conf->buffer_size = NGX_CONF_UNSET_SIZE; 727 | conf->proxy_protocol = NGX_CONF_UNSET; 728 | conf->tcp_nodelay = NGX_CONF_UNSET; 729 | 730 | return conf; 731 | } 732 | 733 | 734 | static char * 735 | ngx_srt_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) 736 | { 737 | ngx_srt_proxy_srv_conf_t *prev = parent; 738 | ngx_srt_proxy_srv_conf_t *conf = child; 739 | 740 | ngx_conf_merge_msec_value(conf->connect_timeout, 741 | prev->connect_timeout, 60000); 742 | 743 | ngx_conf_merge_msec_value(conf->timeout, 744 | prev->timeout, 10 * 60000); 745 | 746 | ngx_conf_merge_size_value(conf->buffer_size, 747 | prev->buffer_size, 64 * 1024); 748 | 749 | ngx_conf_merge_value(conf->proxy_protocol, prev->proxy_protocol, 0); 750 | 751 | ngx_conf_merge_value(conf->tcp_nodelay, prev->tcp_nodelay, 1); 752 | 753 | if (conf->header == NULL) { 754 | conf->header = prev->header; 755 | } 756 | 757 | return NGX_CONF_OK; 758 | } 759 | 760 | 761 | static char * 762 | ngx_srt_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 763 | { 764 | size_t add; 765 | ngx_url_t *u; 766 | ngx_str_t *value; 767 | ngx_srt_core_srv_conf_t *cscf; 768 | ngx_srt_proxy_srv_conf_t *pscf = conf; 769 | 770 | cscf = ngx_srt_conf_get_module_srv_conf(cf, ngx_srt_core_module); 771 | 772 | if (cscf->handler) { 773 | return "is duplicate"; 774 | } 775 | 776 | cscf->handler = ngx_srt_proxy_handler; 777 | 778 | value = cf->args->elts; 779 | 780 | u = ngx_pcalloc(cf->pool, sizeof(ngx_url_t)); 781 | if (u == NULL) { 782 | return NULL; 783 | } 784 | 785 | add = 0; 786 | if (ngx_strncasecmp(value[1].data, (u_char *) "tcp://", 6) == 0) { 787 | add = 6; 788 | } 789 | 790 | u->url.len = value[1].len - add; 791 | u->url.data = value[1].data + add; 792 | u->default_port = 80; 793 | u->uri_part = 1; 794 | 795 | if (ngx_parse_url(cf->pool, u) != NGX_OK) { 796 | if (u->err) { 797 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 798 | "%s in url \"%V\"", u->err, &u->url); 799 | } 800 | return NULL; 801 | } 802 | 803 | pscf->url = u; 804 | 805 | return NGX_CONF_OK; 806 | } 807 | 808 | 809 | static ngx_int_t 810 | ngx_srt_proxy_init(ngx_conf_t *cf) 811 | { 812 | ngx_srt_handler_pt *h; 813 | ngx_srt_core_main_conf_t *cmcf; 814 | 815 | cmcf = ngx_srt_conf_get_module_main_conf(cf, ngx_srt_core_module); 816 | 817 | h = ngx_array_push(&cmcf->phases[NGX_SRT_PRE_LOG_PHASE].handlers); 818 | if (h == NULL) { 819 | return NGX_ERROR; 820 | } 821 | 822 | *h = ngx_srt_proxy_log_handler; 823 | 824 | return NGX_OK; 825 | } 826 | -------------------------------------------------------------------------------- /src/ngx_srt_script.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Igor Sysoev 4 | * Copyright (C) Nginx, Inc. 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | #include "ngx_srt.h" 11 | 12 | 13 | static ngx_int_t ngx_srt_script_init_arrays( 14 | ngx_srt_script_compile_t *sc); 15 | static ngx_int_t ngx_srt_script_done(ngx_srt_script_compile_t *sc); 16 | static ngx_int_t ngx_srt_script_add_copy_code( 17 | ngx_srt_script_compile_t *sc, ngx_str_t *value, ngx_uint_t last); 18 | static ngx_int_t ngx_srt_script_add_var_code( 19 | ngx_srt_script_compile_t *sc, ngx_str_t *name); 20 | #if (NGX_PCRE) 21 | static ngx_int_t ngx_srt_script_add_capture_code( 22 | ngx_srt_script_compile_t *sc, ngx_uint_t n); 23 | #endif 24 | static ngx_int_t ngx_srt_script_add_full_name_code( 25 | ngx_srt_script_compile_t *sc); 26 | static size_t ngx_srt_script_full_name_len_code( 27 | ngx_srt_script_engine_t *e); 28 | static void ngx_srt_script_full_name_code(ngx_srt_script_engine_t *e); 29 | 30 | 31 | #define ngx_srt_script_exit (u_char *) &ngx_srt_script_exit_code 32 | 33 | static uintptr_t ngx_srt_script_exit_code = (uintptr_t) NULL; 34 | 35 | 36 | void 37 | ngx_srt_script_flush_complex_value(ngx_srt_session_t *s, 38 | ngx_srt_complex_value_t *val) 39 | { 40 | ngx_uint_t *index; 41 | 42 | index = val->flushes; 43 | 44 | if (index) { 45 | while (*index != (ngx_uint_t) -1) { 46 | 47 | if (s->variables[*index].no_cacheable) { 48 | s->variables[*index].valid = 0; 49 | s->variables[*index].not_found = 0; 50 | } 51 | 52 | index++; 53 | } 54 | } 55 | } 56 | 57 | 58 | ngx_int_t 59 | ngx_srt_complex_value(ngx_srt_session_t *s, 60 | ngx_srt_complex_value_t *val, ngx_str_t *value) 61 | { 62 | size_t len; 63 | ngx_srt_script_code_pt code; 64 | ngx_srt_script_engine_t e; 65 | ngx_srt_script_len_code_pt lcode; 66 | 67 | if (val->lengths == NULL) { 68 | *value = val->value; 69 | return NGX_OK; 70 | } 71 | 72 | ngx_srt_script_flush_complex_value(s, val); 73 | 74 | ngx_memzero(&e, sizeof(ngx_srt_script_engine_t)); 75 | 76 | e.ip = val->lengths; 77 | e.session = s; 78 | e.flushed = 1; 79 | 80 | len = 0; 81 | 82 | while (*(uintptr_t *) e.ip) { 83 | lcode = *(ngx_srt_script_len_code_pt *) e.ip; 84 | len += lcode(&e); 85 | } 86 | 87 | value->len = len; 88 | value->data = ngx_pnalloc(s->connection->pool, len); 89 | if (value->data == NULL) { 90 | return NGX_ERROR; 91 | } 92 | 93 | e.ip = val->values; 94 | e.pos = value->data; 95 | e.buf = *value; 96 | 97 | while (*(uintptr_t *) e.ip) { 98 | code = *(ngx_srt_script_code_pt *) e.ip; 99 | code((ngx_srt_script_engine_t *) &e); 100 | } 101 | 102 | *value = e.buf; 103 | 104 | return NGX_OK; 105 | } 106 | 107 | 108 | size_t 109 | ngx_srt_complex_value_size(ngx_srt_session_t *s, 110 | ngx_srt_complex_value_t *val, size_t default_value) 111 | { 112 | size_t size; 113 | ngx_str_t value; 114 | 115 | if (val == NULL) { 116 | return default_value; 117 | } 118 | 119 | if (val->lengths == NULL) { 120 | return val->u.size; 121 | } 122 | 123 | if (ngx_srt_complex_value(s, val, &value) != NGX_OK) { 124 | return default_value; 125 | } 126 | 127 | size = ngx_parse_size(&value); 128 | 129 | if (size == (size_t) NGX_ERROR) { 130 | ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, 131 | "invalid size \"%V\"", &value); 132 | return default_value; 133 | } 134 | 135 | return size; 136 | } 137 | 138 | 139 | ngx_int_t 140 | ngx_srt_compile_complex_value(ngx_srt_compile_complex_value_t *ccv) 141 | { 142 | ngx_str_t *v; 143 | ngx_uint_t i, n, nv, nc; 144 | ngx_array_t flushes, lengths, values, *pf, *pl, *pv; 145 | ngx_srt_script_compile_t sc; 146 | 147 | v = ccv->value; 148 | 149 | nv = 0; 150 | nc = 0; 151 | 152 | for (i = 0; i < v->len; i++) { 153 | if (v->data[i] == '$') { 154 | if (v->data[i + 1] >= '1' && v->data[i + 1] <= '9') { 155 | nc++; 156 | 157 | } else { 158 | nv++; 159 | } 160 | } 161 | } 162 | 163 | if ((v->len == 0 || v->data[0] != '$') 164 | && (ccv->conf_prefix || ccv->root_prefix)) 165 | { 166 | if (ngx_conf_full_name(ccv->cf->cycle, v, ccv->conf_prefix) != NGX_OK) { 167 | return NGX_ERROR; 168 | } 169 | 170 | ccv->conf_prefix = 0; 171 | ccv->root_prefix = 0; 172 | } 173 | 174 | ccv->complex_value->value = *v; 175 | ccv->complex_value->flushes = NULL; 176 | ccv->complex_value->lengths = NULL; 177 | ccv->complex_value->values = NULL; 178 | 179 | if (nv == 0 && nc == 0) { 180 | return NGX_OK; 181 | } 182 | 183 | n = nv + 1; 184 | 185 | if (ngx_array_init(&flushes, ccv->cf->pool, n, sizeof(ngx_uint_t)) 186 | != NGX_OK) 187 | { 188 | return NGX_ERROR; 189 | } 190 | 191 | n = nv * (2 * sizeof(ngx_srt_script_copy_code_t) 192 | + sizeof(ngx_srt_script_var_code_t)) 193 | + sizeof(uintptr_t); 194 | 195 | if (ngx_array_init(&lengths, ccv->cf->pool, n, 1) != NGX_OK) { 196 | return NGX_ERROR; 197 | } 198 | 199 | n = (nv * (2 * sizeof(ngx_srt_script_copy_code_t) 200 | + sizeof(ngx_srt_script_var_code_t)) 201 | + sizeof(uintptr_t) 202 | + v->len 203 | + sizeof(uintptr_t) - 1) 204 | & ~(sizeof(uintptr_t) - 1); 205 | 206 | if (ngx_array_init(&values, ccv->cf->pool, n, 1) != NGX_OK) { 207 | return NGX_ERROR; 208 | } 209 | 210 | pf = &flushes; 211 | pl = &lengths; 212 | pv = &values; 213 | 214 | ngx_memzero(&sc, sizeof(ngx_srt_script_compile_t)); 215 | 216 | sc.cf = ccv->cf; 217 | sc.source = v; 218 | sc.flushes = &pf; 219 | sc.lengths = &pl; 220 | sc.values = &pv; 221 | sc.complete_lengths = 1; 222 | sc.complete_values = 1; 223 | sc.zero = ccv->zero; 224 | sc.conf_prefix = ccv->conf_prefix; 225 | sc.root_prefix = ccv->root_prefix; 226 | 227 | if (ngx_srt_script_compile(&sc) != NGX_OK) { 228 | return NGX_ERROR; 229 | } 230 | 231 | if (flushes.nelts) { 232 | ccv->complex_value->flushes = flushes.elts; 233 | ccv->complex_value->flushes[flushes.nelts] = (ngx_uint_t) -1; 234 | } 235 | 236 | ccv->complex_value->lengths = lengths.elts; 237 | ccv->complex_value->values = values.elts; 238 | 239 | return NGX_OK; 240 | } 241 | 242 | 243 | char * 244 | ngx_srt_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd, 245 | void *conf) 246 | { 247 | char *p = conf; 248 | 249 | ngx_str_t *value; 250 | ngx_srt_complex_value_t **cv; 251 | ngx_srt_compile_complex_value_t ccv; 252 | 253 | cv = (ngx_srt_complex_value_t **) (p + cmd->offset); 254 | 255 | if (*cv != NULL) { 256 | return "is duplicate"; 257 | } 258 | 259 | *cv = ngx_palloc(cf->pool, sizeof(ngx_srt_complex_value_t)); 260 | if (*cv == NULL) { 261 | return NGX_CONF_ERROR; 262 | } 263 | 264 | value = cf->args->elts; 265 | 266 | ngx_memzero(&ccv, sizeof(ngx_srt_compile_complex_value_t)); 267 | 268 | ccv.cf = cf; 269 | ccv.value = &value[1]; 270 | ccv.complex_value = *cv; 271 | 272 | if (ngx_srt_compile_complex_value(&ccv) != NGX_OK) { 273 | return NGX_CONF_ERROR; 274 | } 275 | 276 | return NGX_CONF_OK; 277 | } 278 | 279 | 280 | char * 281 | ngx_srt_set_complex_value_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, 282 | void *conf) 283 | { 284 | char *p = conf; 285 | 286 | char *rv; 287 | ngx_srt_complex_value_t *cv; 288 | 289 | rv = ngx_srt_set_complex_value_slot(cf, cmd, conf); 290 | 291 | if (rv != NGX_CONF_OK) { 292 | return rv; 293 | } 294 | 295 | cv = *(ngx_srt_complex_value_t **) (p + cmd->offset); 296 | 297 | if (cv->lengths) { 298 | return NGX_CONF_OK; 299 | } 300 | 301 | cv->u.size = ngx_parse_size(&cv->value); 302 | if (cv->u.size == (size_t) NGX_ERROR) { 303 | return "invalid value"; 304 | } 305 | 306 | return NGX_CONF_OK; 307 | } 308 | 309 | 310 | ngx_uint_t 311 | ngx_srt_script_variables_count(ngx_str_t *value) 312 | { 313 | ngx_uint_t i, n; 314 | 315 | for (n = 0, i = 0; i < value->len; i++) { 316 | if (value->data[i] == '$') { 317 | n++; 318 | } 319 | } 320 | 321 | return n; 322 | } 323 | 324 | 325 | ngx_int_t 326 | ngx_srt_script_compile(ngx_srt_script_compile_t *sc) 327 | { 328 | u_char ch; 329 | ngx_str_t name; 330 | ngx_uint_t i, bracket; 331 | 332 | if (ngx_srt_script_init_arrays(sc) != NGX_OK) { 333 | return NGX_ERROR; 334 | } 335 | 336 | for (i = 0; i < sc->source->len; /* void */ ) { 337 | 338 | name.len = 0; 339 | 340 | if (sc->source->data[i] == '$') { 341 | 342 | if (++i == sc->source->len) { 343 | goto invalid_variable; 344 | } 345 | 346 | if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') { 347 | #if (NGX_PCRE) 348 | ngx_uint_t n; 349 | 350 | n = sc->source->data[i] - '0'; 351 | 352 | if (ngx_srt_script_add_capture_code(sc, n) != NGX_OK) { 353 | return NGX_ERROR; 354 | } 355 | 356 | i++; 357 | 358 | continue; 359 | #else 360 | ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, 361 | "using variable \"$%c\" requires " 362 | "PCRE library", sc->source->data[i]); 363 | return NGX_ERROR; 364 | #endif 365 | } 366 | 367 | if (sc->source->data[i] == '{') { 368 | bracket = 1; 369 | 370 | if (++i == sc->source->len) { 371 | goto invalid_variable; 372 | } 373 | 374 | name.data = &sc->source->data[i]; 375 | 376 | } else { 377 | bracket = 0; 378 | name.data = &sc->source->data[i]; 379 | } 380 | 381 | for ( /* void */ ; i < sc->source->len; i++, name.len++) { 382 | ch = sc->source->data[i]; 383 | 384 | if (ch == '}' && bracket) { 385 | i++; 386 | bracket = 0; 387 | break; 388 | } 389 | 390 | if ((ch >= 'A' && ch <= 'Z') 391 | || (ch >= 'a' && ch <= 'z') 392 | || (ch >= '0' && ch <= '9') 393 | || ch == '_') 394 | { 395 | continue; 396 | } 397 | 398 | break; 399 | } 400 | 401 | if (bracket) { 402 | ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, 403 | "the closing bracket in \"%V\" " 404 | "variable is missing", &name); 405 | return NGX_ERROR; 406 | } 407 | 408 | if (name.len == 0) { 409 | goto invalid_variable; 410 | } 411 | 412 | sc->variables++; 413 | 414 | if (ngx_srt_script_add_var_code(sc, &name) != NGX_OK) { 415 | return NGX_ERROR; 416 | } 417 | 418 | continue; 419 | } 420 | 421 | name.data = &sc->source->data[i]; 422 | 423 | while (i < sc->source->len) { 424 | 425 | if (sc->source->data[i] == '$') { 426 | break; 427 | } 428 | 429 | i++; 430 | name.len++; 431 | } 432 | 433 | sc->size += name.len; 434 | 435 | if (ngx_srt_script_add_copy_code(sc, &name, (i == sc->source->len)) 436 | != NGX_OK) 437 | { 438 | return NGX_ERROR; 439 | } 440 | } 441 | 442 | return ngx_srt_script_done(sc); 443 | 444 | invalid_variable: 445 | 446 | ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, "invalid variable name"); 447 | 448 | return NGX_ERROR; 449 | } 450 | 451 | 452 | u_char * 453 | ngx_srt_script_run(ngx_srt_session_t *s, ngx_str_t *value, 454 | void *code_lengths, size_t len, void *code_values) 455 | { 456 | ngx_uint_t i; 457 | ngx_srt_script_code_pt code; 458 | ngx_srt_script_engine_t e; 459 | ngx_srt_core_main_conf_t *cmcf; 460 | ngx_srt_script_len_code_pt lcode; 461 | 462 | cmcf = ngx_srt_get_module_main_conf(s, ngx_srt_core_module); 463 | 464 | for (i = 0; i < cmcf->variables.nelts; i++) { 465 | if (s->variables[i].no_cacheable) { 466 | s->variables[i].valid = 0; 467 | s->variables[i].not_found = 0; 468 | } 469 | } 470 | 471 | ngx_memzero(&e, sizeof(ngx_srt_script_engine_t)); 472 | 473 | e.ip = code_lengths; 474 | e.session = s; 475 | e.flushed = 1; 476 | 477 | while (*(uintptr_t *) e.ip) { 478 | lcode = *(ngx_srt_script_len_code_pt *) e.ip; 479 | len += lcode(&e); 480 | } 481 | 482 | 483 | value->len = len; 484 | value->data = ngx_pnalloc(s->connection->pool, len); 485 | if (value->data == NULL) { 486 | return NULL; 487 | } 488 | 489 | e.ip = code_values; 490 | e.pos = value->data; 491 | 492 | while (*(uintptr_t *) e.ip) { 493 | code = *(ngx_srt_script_code_pt *) e.ip; 494 | code((ngx_srt_script_engine_t *) &e); 495 | } 496 | 497 | return e.pos; 498 | } 499 | 500 | 501 | void 502 | ngx_srt_script_flush_no_cacheable_variables(ngx_srt_session_t *s, 503 | ngx_array_t *indices) 504 | { 505 | ngx_uint_t n, *index; 506 | 507 | if (indices) { 508 | index = indices->elts; 509 | for (n = 0; n < indices->nelts; n++) { 510 | if (s->variables[index[n]].no_cacheable) { 511 | s->variables[index[n]].valid = 0; 512 | s->variables[index[n]].not_found = 0; 513 | } 514 | } 515 | } 516 | } 517 | 518 | 519 | static ngx_int_t 520 | ngx_srt_script_init_arrays(ngx_srt_script_compile_t *sc) 521 | { 522 | ngx_uint_t n; 523 | 524 | if (sc->flushes && *sc->flushes == NULL) { 525 | n = sc->variables ? sc->variables : 1; 526 | *sc->flushes = ngx_array_create(sc->cf->pool, n, sizeof(ngx_uint_t)); 527 | if (*sc->flushes == NULL) { 528 | return NGX_ERROR; 529 | } 530 | } 531 | 532 | if (*sc->lengths == NULL) { 533 | n = sc->variables * (2 * sizeof(ngx_srt_script_copy_code_t) 534 | + sizeof(ngx_srt_script_var_code_t)) 535 | + sizeof(uintptr_t); 536 | 537 | *sc->lengths = ngx_array_create(sc->cf->pool, n, 1); 538 | if (*sc->lengths == NULL) { 539 | return NGX_ERROR; 540 | } 541 | } 542 | 543 | if (*sc->values == NULL) { 544 | n = (sc->variables * (2 * sizeof(ngx_srt_script_copy_code_t) 545 | + sizeof(ngx_srt_script_var_code_t)) 546 | + sizeof(uintptr_t) 547 | + sc->source->len 548 | + sizeof(uintptr_t) - 1) 549 | & ~(sizeof(uintptr_t) - 1); 550 | 551 | *sc->values = ngx_array_create(sc->cf->pool, n, 1); 552 | if (*sc->values == NULL) { 553 | return NGX_ERROR; 554 | } 555 | } 556 | 557 | sc->variables = 0; 558 | 559 | return NGX_OK; 560 | } 561 | 562 | 563 | static ngx_int_t 564 | ngx_srt_script_done(ngx_srt_script_compile_t *sc) 565 | { 566 | ngx_str_t zero; 567 | uintptr_t *code; 568 | 569 | if (sc->zero) { 570 | 571 | zero.len = 1; 572 | zero.data = (u_char *) "\0"; 573 | 574 | if (ngx_srt_script_add_copy_code(sc, &zero, 0) != NGX_OK) { 575 | return NGX_ERROR; 576 | } 577 | } 578 | 579 | if (sc->conf_prefix || sc->root_prefix) { 580 | if (ngx_srt_script_add_full_name_code(sc) != NGX_OK) { 581 | return NGX_ERROR; 582 | } 583 | } 584 | 585 | if (sc->complete_lengths) { 586 | code = ngx_srt_script_add_code(*sc->lengths, sizeof(uintptr_t), 587 | NULL); 588 | if (code == NULL) { 589 | return NGX_ERROR; 590 | } 591 | 592 | *code = (uintptr_t) NULL; 593 | } 594 | 595 | if (sc->complete_values) { 596 | code = ngx_srt_script_add_code(*sc->values, sizeof(uintptr_t), 597 | &sc->main); 598 | if (code == NULL) { 599 | return NGX_ERROR; 600 | } 601 | 602 | *code = (uintptr_t) NULL; 603 | } 604 | 605 | return NGX_OK; 606 | } 607 | 608 | 609 | void * 610 | ngx_srt_script_add_code(ngx_array_t *codes, size_t size, void *code) 611 | { 612 | u_char *elts, **p; 613 | void *new; 614 | 615 | elts = codes->elts; 616 | 617 | new = ngx_array_push_n(codes, size); 618 | if (new == NULL) { 619 | return NULL; 620 | } 621 | 622 | if (code) { 623 | if (elts != codes->elts) { 624 | p = code; 625 | *p += (u_char *) codes->elts - elts; 626 | } 627 | } 628 | 629 | return new; 630 | } 631 | 632 | 633 | static ngx_int_t 634 | ngx_srt_script_add_copy_code(ngx_srt_script_compile_t *sc, 635 | ngx_str_t *value, ngx_uint_t last) 636 | { 637 | u_char *p; 638 | size_t size, len, zero; 639 | ngx_srt_script_copy_code_t *code; 640 | 641 | zero = (sc->zero && last); 642 | len = value->len + zero; 643 | 644 | code = ngx_srt_script_add_code(*sc->lengths, 645 | sizeof(ngx_srt_script_copy_code_t), 646 | NULL); 647 | if (code == NULL) { 648 | return NGX_ERROR; 649 | } 650 | 651 | code->code = (ngx_srt_script_code_pt) (void *) 652 | ngx_srt_script_copy_len_code; 653 | code->len = len; 654 | 655 | size = (sizeof(ngx_srt_script_copy_code_t) + len + sizeof(uintptr_t) - 1) 656 | & ~(sizeof(uintptr_t) - 1); 657 | 658 | code = ngx_srt_script_add_code(*sc->values, size, &sc->main); 659 | if (code == NULL) { 660 | return NGX_ERROR; 661 | } 662 | 663 | code->code = ngx_srt_script_copy_code; 664 | code->len = len; 665 | 666 | p = ngx_cpymem((u_char *) code + sizeof(ngx_srt_script_copy_code_t), 667 | value->data, value->len); 668 | 669 | if (zero) { 670 | *p = '\0'; 671 | sc->zero = 0; 672 | } 673 | 674 | return NGX_OK; 675 | } 676 | 677 | 678 | size_t 679 | ngx_srt_script_copy_len_code(ngx_srt_script_engine_t *e) 680 | { 681 | ngx_srt_script_copy_code_t *code; 682 | 683 | code = (ngx_srt_script_copy_code_t *) e->ip; 684 | 685 | e->ip += sizeof(ngx_srt_script_copy_code_t); 686 | 687 | return code->len; 688 | } 689 | 690 | 691 | void 692 | ngx_srt_script_copy_code(ngx_srt_script_engine_t *e) 693 | { 694 | u_char *p; 695 | ngx_srt_script_copy_code_t *code; 696 | 697 | code = (ngx_srt_script_copy_code_t *) e->ip; 698 | 699 | p = e->pos; 700 | 701 | if (!e->skip) { 702 | e->pos = ngx_copy(p, e->ip + sizeof(ngx_srt_script_copy_code_t), 703 | code->len); 704 | } 705 | 706 | e->ip += sizeof(ngx_srt_script_copy_code_t) 707 | + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1)); 708 | 709 | ngx_log_debug2(NGX_LOG_DEBUG_SRT, e->session->connection->log, 0, 710 | "srt script copy: \"%*s\"", e->pos - p, p); 711 | } 712 | 713 | 714 | static ngx_int_t 715 | ngx_srt_script_add_var_code(ngx_srt_script_compile_t *sc, ngx_str_t *name) 716 | { 717 | ngx_int_t index, *p; 718 | ngx_srt_script_var_code_t *code; 719 | 720 | index = ngx_srt_get_variable_index(sc->cf, name); 721 | 722 | if (index == NGX_ERROR) { 723 | return NGX_ERROR; 724 | } 725 | 726 | if (sc->flushes) { 727 | p = ngx_array_push(*sc->flushes); 728 | if (p == NULL) { 729 | return NGX_ERROR; 730 | } 731 | 732 | *p = index; 733 | } 734 | 735 | code = ngx_srt_script_add_code(*sc->lengths, 736 | sizeof(ngx_srt_script_var_code_t), 737 | NULL); 738 | if (code == NULL) { 739 | return NGX_ERROR; 740 | } 741 | 742 | code->code = (ngx_srt_script_code_pt) (void *) 743 | ngx_srt_script_copy_var_len_code; 744 | code->index = (uintptr_t) index; 745 | 746 | code = ngx_srt_script_add_code(*sc->values, 747 | sizeof(ngx_srt_script_var_code_t), 748 | &sc->main); 749 | if (code == NULL) { 750 | return NGX_ERROR; 751 | } 752 | 753 | code->code = ngx_srt_script_copy_var_code; 754 | code->index = (uintptr_t) index; 755 | 756 | return NGX_OK; 757 | } 758 | 759 | 760 | size_t 761 | ngx_srt_script_copy_var_len_code(ngx_srt_script_engine_t *e) 762 | { 763 | ngx_srt_variable_value_t *value; 764 | ngx_srt_script_var_code_t *code; 765 | 766 | code = (ngx_srt_script_var_code_t *) e->ip; 767 | 768 | e->ip += sizeof(ngx_srt_script_var_code_t); 769 | 770 | if (e->flushed) { 771 | value = ngx_srt_get_indexed_variable(e->session, code->index); 772 | 773 | } else { 774 | value = ngx_srt_get_flushed_variable(e->session, code->index); 775 | } 776 | 777 | if (value && !value->not_found) { 778 | return value->len; 779 | } 780 | 781 | return 0; 782 | } 783 | 784 | 785 | void 786 | ngx_srt_script_copy_var_code(ngx_srt_script_engine_t *e) 787 | { 788 | u_char *p; 789 | ngx_srt_variable_value_t *value; 790 | ngx_srt_script_var_code_t *code; 791 | 792 | code = (ngx_srt_script_var_code_t *) e->ip; 793 | 794 | e->ip += sizeof(ngx_srt_script_var_code_t); 795 | 796 | if (!e->skip) { 797 | 798 | if (e->flushed) { 799 | value = ngx_srt_get_indexed_variable(e->session, code->index); 800 | 801 | } else { 802 | value = ngx_srt_get_flushed_variable(e->session, code->index); 803 | } 804 | 805 | if (value && !value->not_found) { 806 | p = e->pos; 807 | e->pos = ngx_copy(p, value->data, value->len); 808 | 809 | ngx_log_debug2(NGX_LOG_DEBUG_SRT, 810 | e->session->connection->log, 0, 811 | "srt script var: \"%*s\"", e->pos - p, p); 812 | } 813 | } 814 | } 815 | 816 | 817 | #if (NGX_PCRE) 818 | 819 | static ngx_int_t 820 | ngx_srt_script_add_capture_code(ngx_srt_script_compile_t *sc, 821 | ngx_uint_t n) 822 | { 823 | ngx_srt_script_copy_capture_code_t *code; 824 | 825 | code = ngx_srt_script_add_code(*sc->lengths, 826 | sizeof(ngx_srt_script_copy_capture_code_t), 827 | NULL); 828 | if (code == NULL) { 829 | return NGX_ERROR; 830 | } 831 | 832 | code->code = (ngx_srt_script_code_pt) (void *) 833 | ngx_srt_script_copy_capture_len_code; 834 | code->n = 2 * n; 835 | 836 | 837 | code = ngx_srt_script_add_code(*sc->values, 838 | sizeof(ngx_srt_script_copy_capture_code_t), 839 | &sc->main); 840 | if (code == NULL) { 841 | return NGX_ERROR; 842 | } 843 | 844 | code->code = ngx_srt_script_copy_capture_code; 845 | code->n = 2 * n; 846 | 847 | if (sc->ncaptures < n) { 848 | sc->ncaptures = n; 849 | } 850 | 851 | return NGX_OK; 852 | } 853 | 854 | 855 | size_t 856 | ngx_srt_script_copy_capture_len_code(ngx_srt_script_engine_t *e) 857 | { 858 | int *cap; 859 | ngx_uint_t n; 860 | ngx_srt_session_t *s; 861 | ngx_srt_script_copy_capture_code_t *code; 862 | 863 | s = e->session; 864 | 865 | code = (ngx_srt_script_copy_capture_code_t *) e->ip; 866 | 867 | e->ip += sizeof(ngx_srt_script_copy_capture_code_t); 868 | 869 | n = code->n; 870 | 871 | if (n < s->ncaptures) { 872 | cap = s->captures; 873 | return cap[n + 1] - cap[n]; 874 | } 875 | 876 | return 0; 877 | } 878 | 879 | 880 | void 881 | ngx_srt_script_copy_capture_code(ngx_srt_script_engine_t *e) 882 | { 883 | int *cap; 884 | u_char *p, *pos; 885 | ngx_uint_t n; 886 | ngx_srt_session_t *s; 887 | ngx_srt_script_copy_capture_code_t *code; 888 | 889 | s = e->session; 890 | 891 | code = (ngx_srt_script_copy_capture_code_t *) e->ip; 892 | 893 | e->ip += sizeof(ngx_srt_script_copy_capture_code_t); 894 | 895 | n = code->n; 896 | 897 | pos = e->pos; 898 | 899 | if (n < s->ncaptures) { 900 | cap = s->captures; 901 | p = s->captures_data; 902 | e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]); 903 | } 904 | 905 | ngx_log_debug2(NGX_LOG_DEBUG_SRT, e->session->connection->log, 0, 906 | "srt script capture: \"%*s\"", e->pos - pos, pos); 907 | } 908 | 909 | #endif 910 | 911 | 912 | static ngx_int_t 913 | ngx_srt_script_add_full_name_code(ngx_srt_script_compile_t *sc) 914 | { 915 | ngx_srt_script_full_name_code_t *code; 916 | 917 | code = ngx_srt_script_add_code(*sc->lengths, 918 | sizeof(ngx_srt_script_full_name_code_t), 919 | NULL); 920 | if (code == NULL) { 921 | return NGX_ERROR; 922 | } 923 | 924 | code->code = (ngx_srt_script_code_pt) (void *) 925 | ngx_srt_script_full_name_len_code; 926 | code->conf_prefix = sc->conf_prefix; 927 | 928 | code = ngx_srt_script_add_code(*sc->values, 929 | sizeof(ngx_srt_script_full_name_code_t), &sc->main); 930 | if (code == NULL) { 931 | return NGX_ERROR; 932 | } 933 | 934 | code->code = ngx_srt_script_full_name_code; 935 | code->conf_prefix = sc->conf_prefix; 936 | 937 | return NGX_OK; 938 | } 939 | 940 | 941 | static size_t 942 | ngx_srt_script_full_name_len_code(ngx_srt_script_engine_t *e) 943 | { 944 | ngx_srt_script_full_name_code_t *code; 945 | 946 | code = (ngx_srt_script_full_name_code_t *) e->ip; 947 | 948 | e->ip += sizeof(ngx_srt_script_full_name_code_t); 949 | 950 | return code->conf_prefix ? ngx_cycle->conf_prefix.len: 951 | ngx_cycle->prefix.len; 952 | } 953 | 954 | 955 | static void 956 | ngx_srt_script_full_name_code(ngx_srt_script_engine_t *e) 957 | { 958 | ngx_srt_script_full_name_code_t *code; 959 | 960 | ngx_str_t value, *prefix; 961 | 962 | code = (ngx_srt_script_full_name_code_t *) e->ip; 963 | 964 | value.data = e->buf.data; 965 | value.len = e->pos - e->buf.data; 966 | 967 | prefix = code->conf_prefix ? (ngx_str_t *) &ngx_cycle->conf_prefix: 968 | (ngx_str_t *) &ngx_cycle->prefix; 969 | 970 | if (ngx_get_full_name(e->session->connection->pool, prefix, &value) 971 | != NGX_OK) 972 | { 973 | e->ip = ngx_srt_script_exit; 974 | return; 975 | } 976 | 977 | e->buf = value; 978 | 979 | ngx_log_debug1(NGX_LOG_DEBUG_SRT, e->session->connection->log, 0, 980 | "srt script fullname: \"%V\"", &value); 981 | 982 | e->ip += sizeof(ngx_srt_script_full_name_code_t); 983 | } 984 | -------------------------------------------------------------------------------- /src/ngx_srt_script.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Igor Sysoev 4 | * Copyright (C) Nginx, Inc. 5 | */ 6 | 7 | 8 | #ifndef _NGX_SRT_SCRIPT_H_INCLUDED_ 9 | #define _NGX_SRT_SCRIPT_H_INCLUDED_ 10 | 11 | 12 | #include 13 | #include 14 | #include "ngx_srt.h" 15 | 16 | 17 | typedef struct { 18 | u_char *ip; 19 | u_char *pos; 20 | ngx_srt_variable_value_t *sp; 21 | 22 | ngx_str_t buf; 23 | ngx_str_t line; 24 | 25 | unsigned flushed:1; 26 | unsigned skip:1; 27 | 28 | ngx_srt_session_t *session; 29 | } ngx_srt_script_engine_t; 30 | 31 | 32 | typedef struct { 33 | ngx_conf_t *cf; 34 | ngx_str_t *source; 35 | 36 | ngx_array_t **flushes; 37 | ngx_array_t **lengths; 38 | ngx_array_t **values; 39 | 40 | ngx_uint_t variables; 41 | ngx_uint_t ncaptures; 42 | ngx_uint_t size; 43 | 44 | void *main; 45 | 46 | unsigned complete_lengths:1; 47 | unsigned complete_values:1; 48 | unsigned zero:1; 49 | unsigned conf_prefix:1; 50 | unsigned root_prefix:1; 51 | } ngx_srt_script_compile_t; 52 | 53 | 54 | typedef struct { 55 | ngx_str_t value; 56 | ngx_uint_t *flushes; 57 | void *lengths; 58 | void *values; 59 | 60 | union { 61 | size_t size; 62 | } u; 63 | } ngx_srt_complex_value_t; 64 | 65 | 66 | typedef struct { 67 | ngx_conf_t *cf; 68 | ngx_str_t *value; 69 | ngx_srt_complex_value_t *complex_value; 70 | 71 | unsigned zero:1; 72 | unsigned conf_prefix:1; 73 | unsigned root_prefix:1; 74 | } ngx_srt_compile_complex_value_t; 75 | 76 | 77 | typedef void (*ngx_srt_script_code_pt) (ngx_srt_script_engine_t *e); 78 | typedef size_t (*ngx_srt_script_len_code_pt) (ngx_srt_script_engine_t *e); 79 | 80 | 81 | typedef struct { 82 | ngx_srt_script_code_pt code; 83 | uintptr_t len; 84 | } ngx_srt_script_copy_code_t; 85 | 86 | 87 | typedef struct { 88 | ngx_srt_script_code_pt code; 89 | uintptr_t index; 90 | } ngx_srt_script_var_code_t; 91 | 92 | 93 | typedef struct { 94 | ngx_srt_script_code_pt code; 95 | uintptr_t n; 96 | } ngx_srt_script_copy_capture_code_t; 97 | 98 | 99 | typedef struct { 100 | ngx_srt_script_code_pt code; 101 | uintptr_t conf_prefix; 102 | } ngx_srt_script_full_name_code_t; 103 | 104 | 105 | void ngx_srt_script_flush_complex_value(ngx_srt_session_t *s, 106 | ngx_srt_complex_value_t *val); 107 | ngx_int_t ngx_srt_complex_value(ngx_srt_session_t *s, 108 | ngx_srt_complex_value_t *val, ngx_str_t *value); 109 | size_t ngx_srt_complex_value_size(ngx_srt_session_t *s, 110 | ngx_srt_complex_value_t *val, size_t default_value); 111 | ngx_int_t ngx_srt_compile_complex_value( 112 | ngx_srt_compile_complex_value_t *ccv); 113 | char *ngx_srt_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd, 114 | void *conf); 115 | char *ngx_srt_set_complex_value_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, 116 | void *conf); 117 | 118 | 119 | ngx_uint_t ngx_srt_script_variables_count(ngx_str_t *value); 120 | ngx_int_t ngx_srt_script_compile(ngx_srt_script_compile_t *sc); 121 | u_char *ngx_srt_script_run(ngx_srt_session_t *s, ngx_str_t *value, 122 | void *code_lengths, size_t reserved, void *code_values); 123 | void ngx_srt_script_flush_no_cacheable_variables(ngx_srt_session_t *s, 124 | ngx_array_t *indices); 125 | 126 | void *ngx_srt_script_add_code(ngx_array_t *codes, size_t size, void *code); 127 | 128 | size_t ngx_srt_script_copy_len_code(ngx_srt_script_engine_t *e); 129 | void ngx_srt_script_copy_code(ngx_srt_script_engine_t *e); 130 | size_t ngx_srt_script_copy_var_len_code(ngx_srt_script_engine_t *e); 131 | void ngx_srt_script_copy_var_code(ngx_srt_script_engine_t *e); 132 | size_t ngx_srt_script_copy_capture_len_code(ngx_srt_script_engine_t *e); 133 | void ngx_srt_script_copy_capture_code(ngx_srt_script_engine_t *e); 134 | 135 | #endif /* _NGX_SRT_SCRIPT_H_INCLUDED_ */ 136 | -------------------------------------------------------------------------------- /src/ngx_srt_set_misc_module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ngx_srt.h" 4 | 5 | #if (NGX_HAVE_OPENSSL_EVP) 6 | #include 7 | 8 | 9 | #define NGX_SRT_SET_MISC_CRYPT_KEY_LEN (256 / 8) 10 | #define NGX_SRT_SET_MISC_CRYPT_IV_LEN EVP_MAX_IV_LENGTH 11 | 12 | 13 | typedef struct { 14 | ngx_srt_complex_value_t key; 15 | ngx_srt_complex_value_t iv; 16 | ngx_srt_complex_value_t value; 17 | } ngx_srt_set_misc_crypt_ctx_t; 18 | 19 | 20 | static char *ngx_srt_set_misc_decrypt(ngx_conf_t *cf, ngx_command_t *cmd, 21 | void *conf); 22 | #endif 23 | 24 | 25 | typedef struct { 26 | ngx_srt_complex_value_t value; 27 | unsigned url_safe:1; 28 | } ngx_srt_set_misc_base64_ctx_t; 29 | 30 | 31 | static char *ngx_srt_set_misc_base64(ngx_conf_t *cf, ngx_command_t *cmd, 32 | void *conf); 33 | 34 | 35 | static ngx_command_t ngx_srt_set_misc_commands[] = { 36 | 37 | { ngx_string("set_decode_base64"), 38 | NGX_SRT_MAIN_CONF|NGX_CONF_TAKE2, 39 | ngx_srt_set_misc_base64, 40 | NGX_SRT_MAIN_CONF_OFFSET, 41 | 0, 42 | (void *) 0 }, 43 | 44 | { ngx_string("set_decode_base64url"), 45 | NGX_SRT_MAIN_CONF|NGX_CONF_TAKE2, 46 | ngx_srt_set_misc_base64, 47 | NGX_SRT_MAIN_CONF_OFFSET, 48 | 0, 49 | (void *) 1 }, 50 | 51 | #if (NGX_HAVE_OPENSSL_EVP) 52 | { ngx_string("set_aes_decrypt"), 53 | NGX_SRT_MAIN_CONF|NGX_CONF_TAKE4, 54 | ngx_srt_set_misc_decrypt, 55 | NGX_SRT_MAIN_CONF_OFFSET, 56 | 0, 57 | NULL }, 58 | #endif 59 | 60 | ngx_null_command 61 | }; 62 | 63 | 64 | static ngx_srt_module_t ngx_srt_set_misc_module_ctx = { 65 | NULL, /* preconfiguration */ 66 | NULL, /* postconfiguration */ 67 | 68 | NULL, /* create main configuration */ 69 | NULL, /* init main configuration */ 70 | 71 | NULL, /* create server configuration */ 72 | NULL /* merge server configuration */ 73 | }; 74 | 75 | 76 | ngx_module_t ngx_srt_set_misc_module = { 77 | NGX_MODULE_V1, 78 | &ngx_srt_set_misc_module_ctx, /* module context */ 79 | ngx_srt_set_misc_commands, /* module directives */ 80 | NGX_SRT_MODULE, /* module type */ 81 | NULL, /* init master */ 82 | NULL, /* init module */ 83 | NULL, /* init process */ 84 | NULL, /* init thread */ 85 | NULL, /* exit thread */ 86 | NULL, /* exit process */ 87 | NULL, /* exit master */ 88 | NGX_MODULE_V1_PADDING 89 | }; 90 | 91 | 92 | static ngx_int_t 93 | ngx_srt_set_misc_base64_decode(ngx_pool_t *pool, ngx_str_t *dst, 94 | ngx_str_t *src, ngx_flag_t url_safe) 95 | { 96 | dst->data = ngx_pnalloc(pool, ngx_base64_decoded_length(src->len)); 97 | if (dst->data == NULL) { 98 | ngx_log_error(NGX_LOG_NOTICE, pool->log, 0, 99 | "ngx_srt_set_misc_base64_decode: alloc failed"); 100 | return NGX_ERROR; 101 | } 102 | 103 | if (url_safe) { 104 | if (ngx_decode_base64url(dst, src) != NGX_OK) { 105 | ngx_log_error(NGX_LOG_ERR, pool->log, 0, 106 | "ngx_srt_set_misc_base64_decode: ngx_decode_base64url failed"); 107 | return NGX_ERROR; 108 | } 109 | 110 | } else { 111 | if (ngx_decode_base64(dst, src) != NGX_OK) { 112 | ngx_log_error(NGX_LOG_ERR, pool->log, 0, 113 | "ngx_srt_set_misc_base64_decode: ngx_decode_base64 failed"); 114 | return NGX_ERROR; 115 | } 116 | } 117 | 118 | return NGX_OK; 119 | } 120 | 121 | 122 | static ngx_int_t 123 | ngx_srt_set_misc_base64_variable(ngx_srt_session_t *s, 124 | ngx_srt_variable_value_t *v, uintptr_t data) 125 | { 126 | ngx_str_t val, decode_str; 127 | ngx_srt_set_misc_base64_ctx_t *base64; 128 | 129 | base64 = (ngx_srt_set_misc_base64_ctx_t *) data; 130 | 131 | ngx_log_debug0(NGX_LOG_DEBUG_SRT, s->connection->log, 0, 132 | "srt base64 started"); 133 | 134 | if (ngx_srt_complex_value(s, &base64->value, &val) != NGX_OK) { 135 | ngx_log_error(NGX_LOG_NOTICE, s->connection->log, 0, 136 | "ngx_srt_set_misc_base64_variable: failed to eval complex value"); 137 | return NGX_ERROR; 138 | } 139 | 140 | if (ngx_srt_set_misc_base64_decode(s->connection->pool, &decode_str, &val, 141 | base64->url_safe) != NGX_OK) 142 | { 143 | return NGX_ERROR; 144 | } 145 | 146 | v->valid = 1; 147 | v->no_cacheable = 0; 148 | v->not_found = 0; 149 | v->len = decode_str.len; 150 | v->data = decode_str.data; 151 | 152 | return NGX_OK; 153 | } 154 | 155 | 156 | static char * 157 | ngx_srt_set_misc_base64(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 158 | { 159 | ngx_str_t *value, name; 160 | ngx_srt_variable_t *var; 161 | ngx_srt_set_misc_base64_ctx_t *base64; 162 | ngx_srt_compile_complex_value_t ccv; 163 | 164 | base64 = ngx_pcalloc(cf->pool, sizeof(ngx_srt_set_misc_base64_ctx_t)); 165 | if (base64 == NULL) { 166 | return NGX_CONF_ERROR; 167 | } 168 | 169 | base64->url_safe = (uintptr_t) cmd->post; 170 | value = cf->args->elts; 171 | 172 | ngx_memzero(&ccv, sizeof(ngx_srt_compile_complex_value_t)); 173 | 174 | ccv.cf = cf; 175 | ccv.value = &value[2]; 176 | ccv.complex_value = &base64->value; 177 | 178 | if (ngx_srt_compile_complex_value(&ccv) != NGX_OK) { 179 | return NGX_CONF_ERROR; 180 | } 181 | 182 | name = value[1]; 183 | 184 | if (name.data[0] != '$') { 185 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 186 | "invalid variable name \"%V\"", &name); 187 | return NGX_CONF_ERROR; 188 | } 189 | 190 | name.len--; 191 | name.data++; 192 | 193 | var = ngx_srt_add_variable(cf, &name, NGX_SRT_VAR_CHANGEABLE); 194 | if (var == NULL) { 195 | return NGX_CONF_ERROR; 196 | } 197 | 198 | var->get_handler = ngx_srt_set_misc_base64_variable; 199 | var->data = (uintptr_t) base64; 200 | 201 | return NGX_CONF_OK; 202 | } 203 | 204 | 205 | #if (NGX_HAVE_OPENSSL_EVP) 206 | static ngx_int_t 207 | ngx_srt_set_misc_decrypt_aes(ngx_pool_t *pool, ngx_str_t *key, ngx_str_t *iv, 208 | ngx_str_t *input, ngx_str_t *dst) 209 | { 210 | int dst_len; 211 | size_t block_size; 212 | EVP_CIPHER_CTX *ctx; 213 | const EVP_CIPHER *cipher; 214 | 215 | cipher = EVP_aes_256_cbc(); 216 | block_size = EVP_CIPHER_block_size(cipher); 217 | 218 | dst->data = ngx_pnalloc(pool, input->len + block_size); 219 | if (dst->data == NULL) { 220 | ngx_log_error(NGX_LOG_NOTICE, pool->log, 0, 221 | "ngx_srt_set_misc_decrypt_aes: alloc failed"); 222 | return NGX_ERROR; 223 | } 224 | 225 | ctx = EVP_CIPHER_CTX_new(); 226 | if (ctx == NULL) { 227 | ngx_log_error(NGX_LOG_ERR, pool->log, 0, 228 | "ngx_srt_set_misc_decrypt_aes: EVP_CIPHER_CTX_new failed"); 229 | return NGX_ERROR; 230 | } 231 | 232 | if (!EVP_DecryptInit_ex(ctx, cipher, NULL, key->data, iv->data)) { 233 | ngx_log_error(NGX_LOG_ERR, pool->log, 0, 234 | "ngx_srt_set_misc_decrypt_aes: EVP_DecryptInit_ex failed"); 235 | goto failed; 236 | } 237 | 238 | if (!EVP_DecryptUpdate(ctx, dst->data, 239 | &dst_len, input->data, input->len)) 240 | { 241 | ngx_log_error(NGX_LOG_ERR, pool->log, 0, 242 | "ngx_srt_set_misc_decrypt_aes: EVP_DecryptUpdate failed"); 243 | goto failed; 244 | } 245 | 246 | dst->len = dst_len; 247 | 248 | if (!EVP_DecryptFinal_ex(ctx, dst->data + dst->len, &dst_len)) { 249 | ngx_log_error(NGX_LOG_ERR, pool->log, 0, 250 | "ngx_srt_set_misc_decrypt_aes: EVP_DecryptFinal_ex failed"); 251 | goto failed; 252 | } 253 | 254 | dst->len += dst_len; 255 | 256 | EVP_CIPHER_CTX_cleanup(ctx); 257 | 258 | return NGX_OK; 259 | 260 | failed: 261 | 262 | EVP_CIPHER_CTX_cleanup(ctx); 263 | 264 | return NGX_ERROR; 265 | } 266 | 267 | 268 | static ngx_int_t 269 | ngx_srt_complex_value_base64(ngx_srt_session_t *s, ngx_srt_complex_value_t *val, 270 | ngx_str_t *dst) 271 | { 272 | ngx_str_t value; 273 | 274 | if (ngx_srt_complex_value(s, val, &value) != NGX_OK) { 275 | ngx_log_error(NGX_LOG_NOTICE, s->connection->log, 0, 276 | "ngx_srt_complex_value_base64: failed to eval complex value"); 277 | return NGX_ERROR; 278 | } 279 | 280 | if (ngx_srt_set_misc_base64_decode(s->connection->pool, dst, &value, 0) 281 | != NGX_OK) 282 | { 283 | ngx_log_error(NGX_LOG_NOTICE, s->connection->log, 0, 284 | "ngx_srt_complex_value_base64: base64_decode failed"); 285 | return NGX_ERROR; 286 | } 287 | 288 | return NGX_OK; 289 | } 290 | 291 | 292 | static ngx_int_t 293 | ngx_srt_set_misc_decrypt_variable(ngx_srt_session_t *s, 294 | ngx_srt_variable_value_t *v, uintptr_t data) 295 | { 296 | ngx_str_t key, iv, val, decrypt_str; 297 | ngx_srt_set_misc_crypt_ctx_t *decrypt; 298 | 299 | decrypt = (ngx_srt_set_misc_crypt_ctx_t *) data; 300 | 301 | ngx_log_debug0(NGX_LOG_DEBUG_SRT, s->connection->log, 0, 302 | "srt decrypt started"); 303 | 304 | if (ngx_srt_complex_value_base64(s, &decrypt->key, &key) != NGX_OK) { 305 | ngx_log_error(NGX_LOG_NOTICE, s->connection->log, 0, 306 | "ngx_srt_set_misc_decrypt_variable: failed to get key"); 307 | return NGX_ERROR; 308 | } 309 | 310 | if (ngx_srt_complex_value_base64(s, &decrypt->iv, &iv) != NGX_OK) { 311 | ngx_log_error(NGX_LOG_NOTICE, s->connection->log, 0, 312 | "ngx_srt_set_misc_decrypt_variable: failed to get iv"); 313 | return NGX_ERROR; 314 | } 315 | 316 | if (key.len != NGX_SRT_SET_MISC_CRYPT_KEY_LEN || 317 | iv.len != NGX_SRT_SET_MISC_CRYPT_IV_LEN) 318 | { 319 | ngx_log_error(NGX_LOG_NOTICE, s->connection->log, 0, 320 | "ngx_srt_set_misc_decrypt_variable: " 321 | "key length or iv length is not correct"); 322 | return NGX_ERROR; 323 | } 324 | 325 | if (ngx_srt_complex_value(s, &decrypt->value, &val) != NGX_OK) { 326 | ngx_log_error(NGX_LOG_NOTICE, s->connection->log, 0, 327 | "ngx_srt_set_misc_decrypt_variable: failed to eval complex value"); 328 | return NGX_ERROR; 329 | } 330 | 331 | if (val.len <= 0) { 332 | v->not_found = 1; 333 | return NGX_OK; 334 | } 335 | 336 | if (ngx_srt_set_misc_decrypt_aes(s->connection->pool, &key, 337 | &iv, &val, &decrypt_str) != NGX_OK) 338 | { 339 | ngx_log_error(NGX_LOG_NOTICE, s->connection->log, 0, 340 | "ngx_srt_set_misc_decrypt_variable: " 341 | "decrypt failed, val=\"%V\"", &val); 342 | return NGX_ERROR; 343 | } 344 | 345 | v->valid = 1; 346 | v->no_cacheable = 0; 347 | v->not_found = 0; 348 | v->len = decrypt_str.len; 349 | v->data = decrypt_str.data; 350 | 351 | return NGX_OK; 352 | } 353 | 354 | 355 | static char * 356 | ngx_srt_set_misc_decrypt(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 357 | { 358 | ngx_str_t *value, name; 359 | ngx_srt_variable_t *var; 360 | ngx_srt_set_misc_crypt_ctx_t *decrypt; 361 | ngx_srt_compile_complex_value_t ccv; 362 | 363 | decrypt = ngx_pcalloc(cf->pool, sizeof(ngx_srt_set_misc_crypt_ctx_t)); 364 | if (decrypt == NULL) { 365 | return NGX_CONF_ERROR; 366 | } 367 | 368 | value = cf->args->elts; 369 | 370 | ngx_memzero(&ccv, sizeof(ngx_srt_compile_complex_value_t)); 371 | 372 | ccv.cf = cf; 373 | ccv.value = &value[2]; 374 | ccv.complex_value = &decrypt->key; 375 | 376 | if (ngx_srt_compile_complex_value(&ccv) != NGX_OK) { 377 | return NGX_CONF_ERROR; 378 | } 379 | 380 | ngx_memzero(&ccv, sizeof(ngx_srt_compile_complex_value_t)); 381 | 382 | ccv.cf = cf; 383 | ccv.value = &value[3]; 384 | ccv.complex_value = &decrypt->iv; 385 | 386 | if (ngx_srt_compile_complex_value(&ccv) != NGX_OK) { 387 | return NGX_CONF_ERROR; 388 | } 389 | 390 | ngx_memzero(&ccv, sizeof(ngx_srt_compile_complex_value_t)); 391 | 392 | ccv.cf = cf; 393 | ccv.value = &value[4]; 394 | ccv.complex_value = &decrypt->value; 395 | 396 | if (ngx_srt_compile_complex_value(&ccv) != NGX_OK) { 397 | return NGX_CONF_ERROR; 398 | } 399 | 400 | name = value[1]; 401 | 402 | if (name.data[0] != '$') { 403 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 404 | "invalid variable name \"%V\"", &name); 405 | return NGX_CONF_ERROR; 406 | } 407 | 408 | name.len--; 409 | name.data++; 410 | 411 | var = ngx_srt_add_variable(cf, &name, NGX_SRT_VAR_CHANGEABLE); 412 | if (var == NULL) { 413 | return NGX_CONF_ERROR; 414 | } 415 | 416 | var->get_handler = ngx_srt_set_misc_decrypt_variable; 417 | var->data = (uintptr_t) decrypt; 418 | 419 | return NGX_CONF_OK; 420 | } 421 | #endif 422 | -------------------------------------------------------------------------------- /src/ngx_srt_stream.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "ngx_srt.h" 5 | 6 | 7 | void 8 | ngx_srt_proxy_process_srt_to_ngx(ngx_srt_conn_t *sc) 9 | { 10 | ngx_int_t rc; 11 | ngx_chain_t *out; 12 | ngx_srt_stream_t *st; 13 | ngx_connection_t *c, *dst; 14 | 15 | st = sc->stream; 16 | if (!st->connected) { 17 | return; 18 | } 19 | 20 | dst = st->connection; 21 | 22 | out = ngx_srt_conn_in_get_chain(sc); 23 | 24 | if (out || sc->srt_in.busy || dst->buffered) { 25 | c = sc->connection; 26 | 27 | c->log->action = "proxying and sending to stream"; 28 | 29 | rc = ngx_srt_top_filter(sc, out, 1); 30 | if (rc == NGX_ERROR) { 31 | ngx_log_error(NGX_LOG_NOTICE, c->log, 0, 32 | "ngx_srt_proxy_process_srt_to_ngx: filter failed %i", rc); 33 | ngx_srt_conn_finalize(sc, NGX_SRT_OK); 34 | return; 35 | } 36 | 37 | ngx_srt_conn_in_update_chains(sc, out); 38 | } 39 | } 40 | 41 | 42 | void 43 | ngx_srt_proxy_process_ngx_to_srt(ngx_srt_conn_t *sc) 44 | { 45 | size_t size; 46 | ssize_t n; 47 | ngx_buf_t *b; 48 | ngx_int_t rc; 49 | ngx_chain_t *cl, **ll, **out, **busy; 50 | ngx_connection_t *c, *pc, *src, *dst; 51 | ngx_srt_stream_t *st; 52 | 53 | st = sc->stream; 54 | 55 | c = sc->connection; 56 | pc = st->connected ? st->connection : NULL; 57 | 58 | src = pc; 59 | dst = c; 60 | b = &st->buf; 61 | out = &st->out; 62 | busy = &st->busy; 63 | 64 | for ( ;; ) { 65 | 66 | if (dst) { 67 | 68 | if (*out || *busy || dst->buffered) { 69 | 70 | c->log->action = "proxying and sending to srt"; 71 | 72 | rc = ngx_srt_top_filter(sc, *out, 0); 73 | 74 | if (rc == NGX_ERROR) { 75 | ngx_log_error(NGX_LOG_NOTICE, c->log, 0, 76 | "ngx_srt_proxy_process_ngx_to_srt: filter failed %i", 77 | rc); 78 | ngx_srt_conn_finalize(sc, NGX_SRT_OK); 79 | return; 80 | } 81 | 82 | ngx_chain_update_chains(c->pool, &st->free, busy, out, 83 | (ngx_buf_tag_t) &ngx_srt_module); 84 | 85 | if (*busy == NULL) { 86 | b->pos = b->start; 87 | b->last = b->start; 88 | } 89 | } 90 | } 91 | 92 | size = b->end - b->last; 93 | 94 | if (size && src->read->ready && !src->read->delayed 95 | && !src->read->error) 96 | { 97 | c->log->action = "proxying and reading from stream"; 98 | 99 | n = src->recv(src, b->last, size); 100 | 101 | if (n == NGX_AGAIN) { 102 | break; 103 | } 104 | 105 | if (n == NGX_ERROR) { 106 | src->read->eof = 1; 107 | n = 0; 108 | } 109 | 110 | if (n >= 0) { 111 | for (ll = out; *ll; ll = &(*ll)->next) { /* void */ } 112 | 113 | cl = ngx_chain_get_free_buf(src->pool, &st->free); 114 | if (cl == NULL) { 115 | ngx_log_error(NGX_LOG_NOTICE, c->log, 0, 116 | "ngx_srt_proxy_process_ngx_to_srt: get buf failed"); 117 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 118 | return; 119 | } 120 | 121 | *ll = cl; 122 | 123 | cl->buf->pos = b->last; 124 | cl->buf->last = b->last + n; 125 | cl->buf->tag = (ngx_buf_tag_t) &ngx_srt_module; 126 | 127 | cl->buf->temporary = (n ? 1 : 0); 128 | cl->buf->last_buf = src->read->eof; 129 | cl->buf->flush = 1; 130 | 131 | st->received += n; 132 | b->last += n; 133 | 134 | continue; 135 | } 136 | } 137 | 138 | break; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/ngx_srt_stream.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_SRT_STREAM_H_INCLUDED_ 2 | #define _NGX_SRT_STREAM_H_INCLUDED_ 3 | 4 | 5 | #include 6 | #include 7 | #include "ngx_srt.h" 8 | 9 | 10 | typedef struct { 11 | ngx_connection_t *connection; 12 | 13 | /* ngx -> srt bufs */ 14 | ngx_buf_t buf; 15 | ngx_chain_t *free; 16 | ngx_chain_t *out; 17 | ngx_chain_t *busy; 18 | 19 | off_t received; 20 | 21 | unsigned connected:1; 22 | unsigned close_conn:1; 23 | } ngx_srt_stream_t; 24 | 25 | 26 | void ngx_srt_proxy_process_srt_to_ngx(ngx_srt_conn_t *sc); 27 | 28 | void ngx_srt_proxy_process_ngx_to_srt(ngx_srt_conn_t *sc); 29 | 30 | #endif /* _NGX_SRT_STREAM_H_INCLUDED_ */ 31 | -------------------------------------------------------------------------------- /src/ngx_srt_upstream.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Igor Sysoev 4 | * Copyright (C) Nginx, Inc. 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | #include "ngx_srt.h" 11 | 12 | 13 | static ngx_int_t ngx_srt_upstream_add_variables(ngx_conf_t *cf); 14 | static ngx_int_t ngx_srt_upstream_addr_variable(ngx_srt_session_t *s, 15 | ngx_srt_variable_value_t *v, uintptr_t data); 16 | static ngx_int_t ngx_srt_upstream_response_time_variable( 17 | ngx_srt_session_t *s, ngx_srt_variable_value_t *v, uintptr_t data); 18 | static ngx_int_t ngx_srt_upstream_bytes_variable(ngx_srt_session_t *s, 19 | ngx_srt_variable_value_t *v, uintptr_t data); 20 | 21 | 22 | static ngx_srt_module_t ngx_srt_upstream_module_ctx = { 23 | ngx_srt_upstream_add_variables, /* preconfiguration */ 24 | NULL, /* postconfiguration */ 25 | 26 | NULL, /* create main configuration */ 27 | NULL, /* init main configuration */ 28 | 29 | NULL, /* create server configuration */ 30 | NULL /* merge server configuration */ 31 | }; 32 | 33 | 34 | ngx_module_t ngx_srt_upstream_module = { 35 | NGX_MODULE_V1, 36 | &ngx_srt_upstream_module_ctx, /* module context */ 37 | NULL, /* module directives */ 38 | NGX_SRT_MODULE, /* module type */ 39 | NULL, /* init master */ 40 | NULL, /* init module */ 41 | NULL, /* init process */ 42 | NULL, /* init thread */ 43 | NULL, /* exit thread */ 44 | NULL, /* exit process */ 45 | NULL, /* exit master */ 46 | NGX_MODULE_V1_PADDING 47 | }; 48 | 49 | 50 | static ngx_srt_variable_t ngx_srt_upstream_vars[] = { 51 | 52 | { ngx_string("upstream_addr"), NULL, 53 | ngx_srt_upstream_addr_variable, 0, 54 | NGX_SRT_VAR_NOCACHEABLE, 0 }, 55 | 56 | { ngx_string("upstream_bytes_sent"), NULL, 57 | ngx_srt_upstream_bytes_variable, 0, 58 | NGX_SRT_VAR_NOCACHEABLE, 0 }, 59 | 60 | { ngx_string("upstream_connect_time"), NULL, 61 | ngx_srt_upstream_response_time_variable, 2, 62 | NGX_SRT_VAR_NOCACHEABLE, 0 }, 63 | 64 | { ngx_string("upstream_first_byte_time"), NULL, 65 | ngx_srt_upstream_response_time_variable, 1, 66 | NGX_SRT_VAR_NOCACHEABLE, 0 }, 67 | 68 | { ngx_string("upstream_session_time"), NULL, 69 | ngx_srt_upstream_response_time_variable, 0, 70 | NGX_SRT_VAR_NOCACHEABLE, 0 }, 71 | 72 | { ngx_string("upstream_bytes_received"), NULL, 73 | ngx_srt_upstream_bytes_variable, 1, 74 | NGX_SRT_VAR_NOCACHEABLE, 0 }, 75 | 76 | ngx_srt_null_variable 77 | }; 78 | 79 | 80 | static ngx_int_t 81 | ngx_srt_upstream_add_variables(ngx_conf_t *cf) 82 | { 83 | ngx_srt_variable_t *var, *v; 84 | 85 | for (v = ngx_srt_upstream_vars; v->name.len; v++) { 86 | var = ngx_srt_add_variable(cf, &v->name, v->flags); 87 | if (var == NULL) { 88 | return NGX_ERROR; 89 | } 90 | 91 | var->get_handler = v->get_handler; 92 | var->data = v->data; 93 | } 94 | 95 | return NGX_OK; 96 | } 97 | 98 | 99 | static ngx_int_t 100 | ngx_srt_upstream_addr_variable(ngx_srt_session_t *s, 101 | ngx_srt_variable_value_t *v, uintptr_t data) 102 | { 103 | u_char *p; 104 | size_t len; 105 | ngx_uint_t i; 106 | ngx_srt_upstream_state_t *state; 107 | 108 | v->valid = 1; 109 | v->no_cacheable = 0; 110 | v->not_found = 0; 111 | 112 | if (s->upstream_states == NULL || s->upstream_states->nelts == 0) { 113 | v->not_found = 1; 114 | return NGX_OK; 115 | } 116 | 117 | len = 0; 118 | state = s->upstream_states->elts; 119 | 120 | for (i = 0; i < s->upstream_states->nelts; i++) { 121 | if (state[i].peer) { 122 | len += state[i].peer->len; 123 | } 124 | 125 | len += 2; 126 | } 127 | 128 | p = ngx_pnalloc(s->connection->pool, len); 129 | if (p == NULL) { 130 | return NGX_ERROR; 131 | } 132 | 133 | v->data = p; 134 | 135 | i = 0; 136 | 137 | for ( ;; ) { 138 | if (state[i].peer) { 139 | p = ngx_cpymem(p, state[i].peer->data, state[i].peer->len); 140 | } 141 | 142 | if (++i == s->upstream_states->nelts) { 143 | break; 144 | } 145 | 146 | *p++ = ','; 147 | *p++ = ' '; 148 | } 149 | 150 | v->len = p - v->data; 151 | 152 | return NGX_OK; 153 | } 154 | 155 | 156 | static ngx_int_t 157 | ngx_srt_upstream_bytes_variable(ngx_srt_session_t *s, 158 | ngx_srt_variable_value_t *v, uintptr_t data) 159 | { 160 | u_char *p; 161 | size_t len; 162 | ngx_uint_t i; 163 | ngx_srt_upstream_state_t *state; 164 | 165 | v->valid = 1; 166 | v->no_cacheable = 0; 167 | v->not_found = 0; 168 | 169 | if (s->upstream_states == NULL || s->upstream_states->nelts == 0) { 170 | v->not_found = 1; 171 | return NGX_OK; 172 | } 173 | 174 | len = s->upstream_states->nelts * (NGX_OFF_T_LEN + 2); 175 | 176 | p = ngx_pnalloc(s->connection->pool, len); 177 | if (p == NULL) { 178 | return NGX_ERROR; 179 | } 180 | 181 | v->data = p; 182 | 183 | i = 0; 184 | state = s->upstream_states->elts; 185 | 186 | for ( ;; ) { 187 | 188 | if (data == 1) { 189 | p = ngx_sprintf(p, "%O", state[i].bytes_received); 190 | 191 | } else { 192 | p = ngx_sprintf(p, "%O", state[i].bytes_sent); 193 | } 194 | 195 | if (++i == s->upstream_states->nelts) { 196 | break; 197 | } 198 | 199 | *p++ = ','; 200 | *p++ = ' '; 201 | } 202 | 203 | v->len = p - v->data; 204 | 205 | return NGX_OK; 206 | } 207 | 208 | 209 | static ngx_int_t 210 | ngx_srt_upstream_response_time_variable(ngx_srt_session_t *s, 211 | ngx_srt_variable_value_t *v, uintptr_t data) 212 | { 213 | u_char *p; 214 | size_t len; 215 | ngx_uint_t i; 216 | ngx_msec_int_t ms; 217 | ngx_srt_upstream_state_t *state; 218 | 219 | v->valid = 1; 220 | v->no_cacheable = 0; 221 | v->not_found = 0; 222 | 223 | if (s->upstream_states == NULL || s->upstream_states->nelts == 0) { 224 | v->not_found = 1; 225 | return NGX_OK; 226 | } 227 | 228 | len = s->upstream_states->nelts * (NGX_TIME_T_LEN + 4 + 2); 229 | 230 | p = ngx_pnalloc(s->connection->pool, len); 231 | if (p == NULL) { 232 | return NGX_ERROR; 233 | } 234 | 235 | v->data = p; 236 | 237 | i = 0; 238 | state = s->upstream_states->elts; 239 | 240 | for ( ;; ) { 241 | 242 | if (data == 1) { 243 | ms = state[i].first_byte_time; 244 | 245 | } else if (data == 2) { 246 | ms = state[i].connect_time; 247 | 248 | } else { 249 | ms = state[i].response_time; 250 | } 251 | 252 | if (ms != -1) { 253 | ms = ngx_max(ms, 0); 254 | p = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000); 255 | 256 | } else { 257 | *p++ = '-'; 258 | } 259 | 260 | if (++i == s->upstream_states->nelts) { 261 | break; 262 | } 263 | 264 | *p++ = ','; 265 | *p++ = ' '; 266 | } 267 | 268 | v->len = p - v->data; 269 | 270 | return NGX_OK; 271 | } 272 | -------------------------------------------------------------------------------- /src/ngx_srt_upstream.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Igor Sysoev 4 | * Copyright (C) Nginx, Inc. 5 | */ 6 | 7 | 8 | #ifndef _NGX_SRT_UPSTREAM_H_INCLUDED_ 9 | #define _NGX_SRT_UPSTREAM_H_INCLUDED_ 10 | 11 | 12 | #include 13 | #include 14 | #include "ngx_srt.h" 15 | 16 | 17 | typedef struct { 18 | ngx_msec_t response_time; 19 | ngx_msec_t connect_time; 20 | ngx_msec_t first_byte_time; 21 | off_t bytes_sent; 22 | off_t bytes_received; 23 | 24 | ngx_str_t *peer; 25 | } ngx_srt_upstream_state_t; 26 | 27 | #endif /* _NGX_SRT_UPSTREAM_H_INCLUDED_ */ 28 | -------------------------------------------------------------------------------- /src/ngx_srt_variables.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Igor Sysoev 4 | * Copyright (C) Nginx, Inc. 5 | */ 6 | 7 | 8 | #ifndef _NGX_SRT_VARIABLES_H_INCLUDED_ 9 | #define _NGX_SRT_VARIABLES_H_INCLUDED_ 10 | 11 | 12 | #include 13 | #include 14 | #include "ngx_srt.h" 15 | 16 | 17 | typedef ngx_variable_value_t ngx_srt_variable_value_t; 18 | 19 | #define ngx_srt_variable(v) { sizeof(v) - 1, 1, 0, 0, 0, (u_char *) v } 20 | 21 | typedef struct ngx_srt_variable_s ngx_srt_variable_t; 22 | 23 | typedef void (*ngx_srt_set_variable_pt) (ngx_srt_session_t *s, 24 | ngx_srt_variable_value_t *v, uintptr_t data); 25 | typedef ngx_int_t (*ngx_srt_get_variable_pt) (ngx_srt_session_t *s, 26 | ngx_srt_variable_value_t *v, uintptr_t data); 27 | 28 | 29 | #define NGX_SRT_VAR_CHANGEABLE 1 30 | #define NGX_SRT_VAR_NOCACHEABLE 2 31 | #define NGX_SRT_VAR_INDEXED 4 32 | #define NGX_SRT_VAR_NOHASH 8 33 | #define NGX_SRT_VAR_WEAK 16 34 | #define NGX_SRT_VAR_PREFIX 32 35 | 36 | 37 | struct ngx_srt_variable_s { 38 | ngx_str_t name; /* must be first to build the hash */ 39 | ngx_srt_set_variable_pt set_handler; 40 | ngx_srt_get_variable_pt get_handler; 41 | uintptr_t data; 42 | ngx_uint_t flags; 43 | ngx_uint_t index; 44 | }; 45 | 46 | #define ngx_srt_null_variable { ngx_null_string, NULL, NULL, 0, 0, 0 } 47 | 48 | 49 | ngx_srt_variable_t *ngx_srt_add_variable(ngx_conf_t *cf, ngx_str_t *name, 50 | ngx_uint_t flags); 51 | ngx_int_t ngx_srt_get_variable_index(ngx_conf_t *cf, ngx_str_t *name); 52 | ngx_srt_variable_value_t *ngx_srt_get_indexed_variable( 53 | ngx_srt_session_t *s, ngx_uint_t index); 54 | ngx_srt_variable_value_t *ngx_srt_get_flushed_variable( 55 | ngx_srt_session_t *s, ngx_uint_t index); 56 | 57 | ngx_srt_variable_value_t *ngx_srt_get_variable(ngx_srt_session_t *s, 58 | ngx_str_t *name, ngx_uint_t key); 59 | 60 | 61 | #if (NGX_PCRE) 62 | 63 | typedef struct { 64 | ngx_uint_t capture; 65 | ngx_int_t index; 66 | } ngx_srt_regex_variable_t; 67 | 68 | 69 | typedef struct { 70 | ngx_regex_t *regex; 71 | ngx_uint_t ncaptures; 72 | ngx_srt_regex_variable_t *variables; 73 | ngx_uint_t nvariables; 74 | ngx_str_t name; 75 | } ngx_srt_regex_t; 76 | 77 | 78 | typedef struct { 79 | ngx_srt_regex_t *regex; 80 | void *value; 81 | } ngx_srt_map_regex_t; 82 | 83 | 84 | ngx_srt_regex_t *ngx_srt_regex_compile(ngx_conf_t *cf, 85 | ngx_regex_compile_t *rc); 86 | ngx_int_t ngx_srt_regex_exec(ngx_srt_session_t *s, ngx_srt_regex_t *re, 87 | ngx_str_t *str); 88 | 89 | #endif 90 | 91 | 92 | typedef struct { 93 | ngx_hash_combined_t hash; 94 | #if (NGX_PCRE) 95 | ngx_srt_map_regex_t *regex; 96 | ngx_uint_t nregex; 97 | #endif 98 | } ngx_srt_map_t; 99 | 100 | 101 | void *ngx_srt_map_find(ngx_srt_session_t *s, ngx_srt_map_t *map, 102 | ngx_str_t *match); 103 | 104 | 105 | ngx_int_t ngx_srt_variables_add_core_vars(ngx_conf_t *cf); 106 | ngx_int_t ngx_srt_variables_init_vars(ngx_conf_t *cf); 107 | 108 | 109 | extern ngx_srt_variable_value_t ngx_srt_variable_null_value; 110 | 111 | #endif /* _NGX_SRT_VARIABLES_H_INCLUDED_ */ 112 | -------------------------------------------------------------------------------- /src/ngx_srt_write_filter_module.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Igor Sysoev 4 | * Copyright (C) Nginx, Inc. 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | #include "ngx_srt.h" 11 | 12 | 13 | static ngx_int_t ngx_srt_write_filter(ngx_srt_conn_t *sc, 14 | ngx_chain_t *in, ngx_uint_t from_srt); 15 | static ngx_int_t ngx_srt_write_filter_init(ngx_conf_t *cf); 16 | 17 | 18 | static ngx_srt_module_t ngx_srt_write_filter_module_ctx = { 19 | NULL, /* preconfiguration */ 20 | ngx_srt_write_filter_init, /* postconfiguration */ 21 | 22 | NULL, /* create main configuration */ 23 | NULL, /* init main configuration */ 24 | 25 | NULL, /* create server configuration */ 26 | NULL /* merge server configuration */ 27 | }; 28 | 29 | 30 | ngx_module_t ngx_srt_write_filter_module = { 31 | NGX_MODULE_V1, 32 | &ngx_srt_write_filter_module_ctx, /* module context */ 33 | NULL, /* module directives */ 34 | NGX_SRT_MODULE, /* module type */ 35 | NULL, /* init master */ 36 | NULL, /* init module */ 37 | NULL, /* init process */ 38 | NULL, /* init thread */ 39 | NULL, /* exit thread */ 40 | NULL, /* exit process */ 41 | NULL, /* exit master */ 42 | NGX_MODULE_V1_PADDING 43 | }; 44 | 45 | 46 | static ngx_int_t 47 | ngx_srt_write_filter(ngx_srt_conn_t *sc, ngx_chain_t *in, 48 | ngx_uint_t from_srt) 49 | { 50 | off_t size; 51 | ngx_uint_t last, flush, sync; 52 | ngx_chain_t *cl, *ln, **ll, **out, *chain; 53 | ngx_connection_t *c; 54 | ngx_srt_write_filter_ctx_t *ctx; 55 | 56 | ctx = &sc->writer_ctx; 57 | 58 | if (from_srt) { 59 | c = sc->stream->connection; 60 | out = &ctx->from_srt; 61 | 62 | } else { 63 | c = sc->connection; 64 | out = &ctx->from_ngx; 65 | } 66 | 67 | if (c->error) { 68 | return NGX_ERROR; 69 | } 70 | 71 | size = 0; 72 | flush = 0; 73 | sync = 0; 74 | last = 0; 75 | ll = out; 76 | 77 | /* find the size, the flush point and the last link of the saved chain */ 78 | 79 | for (cl = *out; cl; cl = cl->next) { 80 | ll = &cl->next; 81 | 82 | ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, 83 | "write old buf t:%d f:%d %p, pos %p, size: %z " 84 | "file: %O, size: %O", 85 | cl->buf->temporary, cl->buf->in_file, 86 | cl->buf->start, cl->buf->pos, 87 | cl->buf->last - cl->buf->pos, 88 | cl->buf->file_pos, 89 | cl->buf->file_last - cl->buf->file_pos); 90 | 91 | if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { 92 | ngx_log_error(NGX_LOG_ALERT, c->log, 0, 93 | "zero size buf in writer " 94 | "t:%d r:%d f:%d %p %p-%p %p %O-%O", 95 | cl->buf->temporary, 96 | cl->buf->recycled, 97 | cl->buf->in_file, 98 | cl->buf->start, 99 | cl->buf->pos, 100 | cl->buf->last, 101 | cl->buf->file, 102 | cl->buf->file_pos, 103 | cl->buf->file_last); 104 | 105 | ngx_debug_point(); 106 | return NGX_ERROR; 107 | } 108 | 109 | if (ngx_buf_size(cl->buf) < 0) { 110 | ngx_log_error(NGX_LOG_ALERT, c->log, 0, 111 | "negative size buf in writer " 112 | "t:%d r:%d f:%d %p %p-%p %p %O-%O", 113 | cl->buf->temporary, 114 | cl->buf->recycled, 115 | cl->buf->in_file, 116 | cl->buf->start, 117 | cl->buf->pos, 118 | cl->buf->last, 119 | cl->buf->file, 120 | cl->buf->file_pos, 121 | cl->buf->file_last); 122 | 123 | ngx_debug_point(); 124 | return NGX_ERROR; 125 | } 126 | 127 | size += ngx_buf_size(cl->buf); 128 | 129 | if (cl->buf->flush || cl->buf->recycled) { 130 | flush = 1; 131 | } 132 | 133 | if (cl->buf->sync) { 134 | sync = 1; 135 | } 136 | 137 | if (cl->buf->last_buf) { 138 | last = 1; 139 | } 140 | } 141 | 142 | /* add the new chain to the existent one */ 143 | 144 | for (ln = in; ln; ln = ln->next) { 145 | cl = ngx_alloc_chain_link(c->pool); 146 | if (cl == NULL) { 147 | return NGX_ERROR; 148 | } 149 | 150 | cl->buf = ln->buf; 151 | *ll = cl; 152 | ll = &cl->next; 153 | 154 | ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, 155 | "write new buf t:%d f:%d %p, pos %p, size: %z " 156 | "file: %O, size: %O", 157 | cl->buf->temporary, cl->buf->in_file, 158 | cl->buf->start, cl->buf->pos, 159 | cl->buf->last - cl->buf->pos, 160 | cl->buf->file_pos, 161 | cl->buf->file_last - cl->buf->file_pos); 162 | 163 | if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { 164 | ngx_log_error(NGX_LOG_ALERT, c->log, 0, 165 | "zero size buf in writer " 166 | "t:%d r:%d f:%d %p %p-%p %p %O-%O", 167 | cl->buf->temporary, 168 | cl->buf->recycled, 169 | cl->buf->in_file, 170 | cl->buf->start, 171 | cl->buf->pos, 172 | cl->buf->last, 173 | cl->buf->file, 174 | cl->buf->file_pos, 175 | cl->buf->file_last); 176 | 177 | ngx_debug_point(); 178 | return NGX_ERROR; 179 | } 180 | 181 | if (ngx_buf_size(cl->buf) < 0) { 182 | ngx_log_error(NGX_LOG_ALERT, c->log, 0, 183 | "negative size buf in writer " 184 | "t:%d r:%d f:%d %p %p-%p %p %O-%O", 185 | cl->buf->temporary, 186 | cl->buf->recycled, 187 | cl->buf->in_file, 188 | cl->buf->start, 189 | cl->buf->pos, 190 | cl->buf->last, 191 | cl->buf->file, 192 | cl->buf->file_pos, 193 | cl->buf->file_last); 194 | 195 | ngx_debug_point(); 196 | return NGX_ERROR; 197 | } 198 | 199 | size += ngx_buf_size(cl->buf); 200 | 201 | if (cl->buf->flush || cl->buf->recycled) { 202 | flush = 1; 203 | } 204 | 205 | if (cl->buf->sync) { 206 | sync = 1; 207 | } 208 | 209 | if (cl->buf->last_buf) { 210 | last = 1; 211 | } 212 | } 213 | 214 | *ll = NULL; 215 | 216 | ngx_log_debug3(NGX_LOG_DEBUG_SRT, c->log, 0, 217 | "srt write filter: l:%ui f:%ui s:%O", last, flush, size); 218 | 219 | if (size == 0 220 | && !(c->buffered & NGX_LOWLEVEL_BUFFERED) 221 | && !(last && c->need_last_buf) 222 | && !(c->type == SOCK_DGRAM && flush)) 223 | { 224 | if (last || flush || sync) { 225 | for (cl = *out; cl; /* void */) { 226 | ln = cl; 227 | cl = cl->next; 228 | ngx_free_chain(c->pool, ln); 229 | } 230 | 231 | *out = NULL; 232 | c->buffered &= ~NGX_SRT_WRITE_BUFFERED; 233 | 234 | return NGX_OK; 235 | } 236 | 237 | ngx_log_error(NGX_LOG_ALERT, c->log, 0, 238 | "the srt output chain is empty"); 239 | 240 | ngx_debug_point(); 241 | 242 | return NGX_ERROR; 243 | } 244 | 245 | chain = c->send_chain(c, *out, 0); 246 | 247 | ngx_log_debug1(NGX_LOG_DEBUG_SRT, c->log, 0, 248 | "srt write filter %p", chain); 249 | 250 | if (chain == NGX_CHAIN_ERROR) { 251 | c->error = 1; 252 | return NGX_ERROR; 253 | } 254 | 255 | for (cl = *out; cl && cl != chain; /* void */) { 256 | ln = cl; 257 | cl = cl->next; 258 | ngx_free_chain(c->pool, ln); 259 | } 260 | 261 | *out = chain; 262 | 263 | if (chain) { 264 | if (c->shared) { 265 | ngx_log_error(NGX_LOG_ALERT, c->log, 0, 266 | "shared connection is busy"); 267 | return NGX_ERROR; 268 | } 269 | 270 | c->buffered |= NGX_SRT_WRITE_BUFFERED; 271 | return NGX_AGAIN; 272 | } 273 | 274 | c->buffered &= ~NGX_SRT_WRITE_BUFFERED; 275 | 276 | if (c->buffered & NGX_LOWLEVEL_BUFFERED) { 277 | return NGX_AGAIN; 278 | } 279 | 280 | return NGX_OK; 281 | } 282 | 283 | 284 | static ngx_int_t 285 | ngx_srt_write_filter_init(ngx_conf_t *cf) 286 | { 287 | ngx_srt_top_filter = ngx_srt_write_filter; 288 | 289 | return NGX_OK; 290 | } 291 | -------------------------------------------------------------------------------- /src/ngx_stream_srt_proxy_module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "ngx_srt.h" 5 | 6 | 7 | typedef struct { 8 | ngx_url_t *url; 9 | ngx_msec_t connect_timeout; 10 | ngx_msec_t timeout; 11 | size_t buffer_size; 12 | ngx_stream_complex_value_t *stream_id; 13 | ngx_stream_complex_value_t *passphrase; 14 | } ngx_stream_srt_proxy_srv_conf_t; 15 | 16 | 17 | typedef struct { 18 | ngx_stream_upstream_state_t *state; 19 | ngx_msec_t start_time; 20 | } ngx_stream_srt_proxy_upstream_t; 21 | 22 | 23 | static void ngx_stream_srt_proxy_srt_handler(ngx_event_t *ev); 24 | static void ngx_stream_srt_proxy_ngx_handler(ngx_event_t *ev); 25 | static void ngx_stream_srt_proxy_connect_handler(ngx_event_t *ev); 26 | static ngx_int_t ngx_stream_srt_proxy_test_finalize(ngx_srt_conn_t *sc, 27 | ngx_uint_t from_upstream); 28 | static u_char *ngx_stream_srt_proxy_log_error(ngx_log_t *log, u_char *buf, 29 | size_t len); 30 | static void ngx_stream_srt_proxy_log_session(void *data); 31 | static void ngx_stream_srt_proxy_cleanup(void *data); 32 | 33 | static void *ngx_stream_srt_proxy_create_srv_conf(ngx_conf_t *cf); 34 | static char *ngx_stream_srt_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, 35 | void *child); 36 | static char *ngx_stream_srt_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, 37 | void *conf); 38 | 39 | 40 | static ngx_command_t ngx_stream_srt_proxy_commands[] = { 41 | 42 | { ngx_string("srt_proxy_pass"), 43 | NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, 44 | ngx_stream_srt_proxy_pass, 45 | NGX_STREAM_SRV_CONF_OFFSET, 46 | 0, 47 | NULL }, 48 | 49 | { ngx_string("srt_proxy_connect_timeout"), 50 | NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, 51 | ngx_conf_set_msec_slot, 52 | NGX_STREAM_SRV_CONF_OFFSET, 53 | offsetof(ngx_stream_srt_proxy_srv_conf_t, connect_timeout), 54 | NULL }, 55 | 56 | { ngx_string("srt_proxy_timeout"), 57 | NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, 58 | ngx_conf_set_msec_slot, 59 | NGX_STREAM_SRV_CONF_OFFSET, 60 | offsetof(ngx_stream_srt_proxy_srv_conf_t, timeout), 61 | NULL }, 62 | 63 | { ngx_string("srt_proxy_buffer_size"), 64 | NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, 65 | ngx_conf_set_size_slot, 66 | NGX_STREAM_SRV_CONF_OFFSET, 67 | offsetof(ngx_stream_srt_proxy_srv_conf_t, buffer_size), 68 | NULL }, 69 | 70 | { ngx_string("srt_proxy_stream_id"), 71 | NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, 72 | ngx_stream_set_complex_value_slot, 73 | NGX_STREAM_SRV_CONF_OFFSET, 74 | offsetof(ngx_stream_srt_proxy_srv_conf_t, stream_id), 75 | NULL }, 76 | 77 | { ngx_string("srt_proxy_passphrase"), 78 | NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, 79 | ngx_stream_set_complex_value_slot, 80 | NGX_STREAM_SRV_CONF_OFFSET, 81 | offsetof(ngx_stream_srt_proxy_srv_conf_t, passphrase), 82 | NULL }, 83 | 84 | ngx_null_command 85 | }; 86 | 87 | 88 | static ngx_stream_module_t ngx_stream_srt_proxy_module_ctx = { 89 | NULL, /* preconfiguration */ 90 | NULL, /* postconfiguration */ 91 | 92 | NULL, /* create main configuration */ 93 | NULL, /* init main configuration */ 94 | 95 | ngx_stream_srt_proxy_create_srv_conf, /* create server configuration */ 96 | ngx_stream_srt_proxy_merge_srv_conf /* merge server configuration */ 97 | }; 98 | 99 | 100 | ngx_module_t ngx_stream_srt_proxy_module = { 101 | NGX_MODULE_V1, 102 | &ngx_stream_srt_proxy_module_ctx, /* module context */ 103 | ngx_stream_srt_proxy_commands, /* module directives */ 104 | NGX_STREAM_MODULE, /* module type */ 105 | NULL, /* init master */ 106 | NULL, /* init module */ 107 | NULL, /* init process */ 108 | NULL, /* init thread */ 109 | NULL, /* exit thread */ 110 | NULL, /* exit process */ 111 | NULL, /* exit master */ 112 | NGX_MODULE_V1_PADDING 113 | }; 114 | 115 | 116 | static void 117 | ngx_stream_srt_proxy_handler(ngx_stream_session_t *s) 118 | { 119 | u_char *p; 120 | ngx_str_t stream_id; 121 | ngx_str_t passphrase; 122 | ngx_chain_t *cl; 123 | ngx_srt_conn_t *sc; 124 | ngx_connection_t *c, *pc; 125 | ngx_srt_stream_t *st; 126 | ngx_pool_cleanup_t *cln; 127 | ngx_stream_upstream_state_t *state; 128 | ngx_stream_srt_proxy_upstream_t *u; 129 | ngx_stream_srt_proxy_srv_conf_t *pscf; 130 | 131 | c = s->connection; 132 | 133 | pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_srt_proxy_module); 134 | 135 | ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, 136 | "ngx_stream_srt_proxy_handler: called"); 137 | 138 | if (pscf->stream_id) { 139 | if (ngx_stream_complex_value(s, pscf->stream_id, &stream_id) 140 | != NGX_OK) 141 | { 142 | ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); 143 | return; 144 | } 145 | 146 | } else { 147 | stream_id.len = 0; 148 | } 149 | 150 | if (pscf->passphrase) { 151 | if (ngx_stream_complex_value(s, pscf->passphrase, &passphrase) 152 | != NGX_OK) 153 | { 154 | ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); 155 | return; 156 | } 157 | 158 | } else { 159 | passphrase.len = 0; 160 | } 161 | 162 | c->log->action = "connecting to upstream"; 163 | 164 | sc = ngx_srt_conn_create_connect(s->connection->log, pscf->url, 165 | pscf->buffer_size, &stream_id, &passphrase); 166 | if (sc == NULL) { 167 | ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); 168 | return; 169 | } 170 | 171 | cln = ngx_pool_cleanup_add(sc->connection->pool, 0); 172 | if (cln == NULL) { 173 | ngx_srt_conn_finalize(sc, NGX_STREAM_INTERNAL_SERVER_ERROR); 174 | ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); 175 | return; 176 | } 177 | 178 | cln->handler = ngx_stream_srt_proxy_cleanup; 179 | cln->data = sc; 180 | 181 | sc->log_session = ngx_stream_srt_proxy_log_session; 182 | 183 | ngx_stream_set_ctx(s, sc, ngx_stream_srt_proxy_module); 184 | sc->session = s; 185 | 186 | st = ngx_pcalloc(c->pool, sizeof(ngx_srt_stream_t)); 187 | if (st == NULL) { 188 | ngx_srt_conn_finalize(sc, NGX_STREAM_INTERNAL_SERVER_ERROR); 189 | return; 190 | } 191 | 192 | /* Note: not setting close_conn - connection owned by stream module */ 193 | st->connection = c; 194 | st->connected = 1; 195 | 196 | sc->stream = st; 197 | 198 | if (c->buffer && c->buffer->pos < c->buffer->last) { 199 | ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, 200 | "stream srt proxy add preread buffer: %uz", 201 | c->buffer->last - c->buffer->pos); 202 | 203 | cl = ngx_chain_get_free_buf(c->pool, &st->free); 204 | if (cl == NULL) { 205 | ngx_srt_conn_finalize(sc, NGX_STREAM_INTERNAL_SERVER_ERROR); 206 | return; 207 | } 208 | 209 | *cl->buf = *c->buffer; 210 | 211 | cl->buf->tag = (ngx_buf_tag_t) &ngx_srt_module; 212 | cl->buf->temporary = 1; 213 | cl->buf->flush = 1; 214 | 215 | cl->next = st->out; 216 | st->out = cl; 217 | 218 | st->received += cl->buf->last - cl->buf->pos; 219 | } 220 | 221 | u = ngx_pcalloc(c->pool, sizeof(ngx_stream_srt_proxy_upstream_t)); 222 | if (u == NULL) { 223 | ngx_srt_conn_finalize(sc, NGX_STREAM_INTERNAL_SERVER_ERROR); 224 | return; 225 | } 226 | 227 | u->start_time = ngx_current_msec; 228 | 229 | sc->upstream = u; 230 | 231 | s->log_handler = ngx_stream_srt_proxy_log_error; 232 | 233 | c->read->handler = ngx_stream_srt_proxy_ngx_handler; 234 | c->write->handler = ngx_stream_srt_proxy_ngx_handler; 235 | 236 | s->upstream_states = ngx_array_create(c->pool, 1, 237 | sizeof(ngx_stream_upstream_state_t)); 238 | if (s->upstream_states == NULL) { 239 | ngx_srt_conn_finalize(sc, NGX_STREAM_INTERNAL_SERVER_ERROR); 240 | return; 241 | } 242 | 243 | state = ngx_array_push(s->upstream_states); 244 | if (state == NULL) { 245 | ngx_srt_conn_finalize(sc, NGX_STREAM_INTERNAL_SERVER_ERROR); 246 | return; 247 | } 248 | 249 | ngx_memzero(state, sizeof(*state)); 250 | 251 | state->connect_time = (ngx_msec_t) -1; 252 | state->first_byte_time = (ngx_msec_t) -1; 253 | state->response_time = (ngx_msec_t) -1; 254 | state->peer = &pscf->url->host; 255 | 256 | u->state = state; 257 | 258 | if (st->buf.start == NULL) { 259 | p = ngx_pnalloc(s->connection->pool, pscf->buffer_size); 260 | if (p == NULL) { 261 | ngx_srt_conn_finalize(sc, NGX_STREAM_INTERNAL_SERVER_ERROR); 262 | return; 263 | } 264 | 265 | st->buf.start = p; 266 | st->buf.end = p + pscf->buffer_size; 267 | st->buf.pos = p; 268 | st->buf.last = p; 269 | } 270 | 271 | pc = sc->connection; 272 | 273 | pc->read->handler = ngx_stream_srt_proxy_connect_handler; 274 | pc->write->handler = ngx_stream_srt_proxy_connect_handler; 275 | 276 | ngx_add_timer(pc->write, pscf->connect_timeout); 277 | 278 | ngx_stream_srt_proxy_ngx_handler(c->read); 279 | } 280 | 281 | 282 | static void 283 | ngx_stream_srt_proxy_init_upstream(ngx_stream_session_t *s) 284 | { 285 | ngx_srt_conn_t *sc; 286 | ngx_connection_t *c, *pc; 287 | ngx_stream_srt_proxy_upstream_t *u; 288 | 289 | sc = ngx_stream_get_module_ctx(s, ngx_stream_srt_proxy_module); 290 | c = s->connection; 291 | pc = sc->connection; 292 | 293 | if (c->log->log_level >= NGX_LOG_INFO) { 294 | ngx_str_t str; 295 | u_char addr[NGX_SOCKADDR_STRLEN]; 296 | 297 | str.data = addr; 298 | str.len = ngx_sock_ntop(pc->local_sockaddr, pc->local_socklen, 299 | str.data, NGX_SOCKADDR_STRLEN, 1); 300 | 301 | ngx_log_error(NGX_LOG_INFO, c->log, 0, 302 | "ngx_stream_srt_proxy_init_upstream: srtproxy %V connected to %V", 303 | &str, &pc->addr_text); 304 | } 305 | 306 | u = sc->upstream; 307 | u->state->connect_time = ngx_current_msec - u->start_time; 308 | 309 | pc->read->handler = ngx_stream_srt_proxy_srt_handler; 310 | pc->write->handler = ngx_stream_srt_proxy_srt_handler; 311 | 312 | sc->connected = 1; 313 | 314 | ngx_stream_srt_proxy_srt_handler(pc->write); 315 | } 316 | 317 | 318 | static void 319 | ngx_stream_srt_proxy_ngx_handler(ngx_event_t *ev) 320 | { 321 | ngx_uint_t from_srt; 322 | ngx_srt_conn_t *sc; 323 | ngx_connection_t *c; 324 | ngx_stream_session_t *s; 325 | ngx_stream_srt_proxy_srv_conf_t *pscf; 326 | 327 | c = ev->data; 328 | s = c->data; 329 | sc = ngx_stream_get_module_ctx(s, ngx_stream_srt_proxy_module); 330 | 331 | if (c->close) { 332 | ngx_log_error(NGX_LOG_INFO, c->log, 0, 333 | "ngx_stream_srt_proxy_ngx_handler: shutdown timeout"); 334 | ngx_srt_conn_finalize(sc, NGX_SRT_OK); 335 | return; 336 | } 337 | 338 | if (ev->timedout) { 339 | ev->timedout = 0; 340 | 341 | ngx_connection_error(c, NGX_ETIMEDOUT, 342 | "ngx_stream_srt_proxy_ngx_handler: connection timed out"); 343 | 344 | ngx_srt_conn_finalize(sc, NGX_SRT_OK); 345 | return; 346 | } 347 | 348 | from_srt = ev->write; 349 | 350 | if (from_srt) { 351 | ngx_srt_proxy_process_srt_to_ngx(sc); 352 | 353 | if (ngx_stream_srt_proxy_test_finalize(sc, 0) == NGX_OK) { 354 | return; 355 | } 356 | 357 | if (ngx_handle_write_event(ev, 0) != NGX_OK) { 358 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 359 | return; 360 | } 361 | 362 | } else { 363 | ngx_srt_proxy_process_ngx_to_srt(sc); 364 | 365 | if (ngx_stream_srt_proxy_test_finalize(sc, 1) == NGX_OK) { 366 | return; 367 | } 368 | 369 | if (ngx_handle_read_event(ev, 0) != NGX_OK) { 370 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 371 | return; 372 | } 373 | } 374 | 375 | if (sc->connected) { 376 | pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_srt_proxy_module); 377 | 378 | ngx_add_timer(c->write, pscf->timeout); 379 | } 380 | } 381 | 382 | 383 | static void 384 | ngx_stream_srt_proxy_srt_handler(ngx_event_t *ev) 385 | { 386 | ngx_srt_conn_t *sc; 387 | ngx_connection_t *c, *pc; 388 | ngx_stream_session_t *s; 389 | ngx_stream_srt_proxy_upstream_t *u; 390 | ngx_stream_srt_proxy_srv_conf_t *pscf; 391 | 392 | pc = ev->data; 393 | sc = pc->data; 394 | 395 | s = sc->session; 396 | c = s->connection; 397 | 398 | if (ev->write) { 399 | ngx_srt_proxy_process_ngx_to_srt(sc); 400 | 401 | if (ngx_handle_read_event(c->read, 0) != NGX_OK) { 402 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 403 | return; 404 | } 405 | 406 | } else { 407 | ngx_srt_proxy_process_srt_to_ngx(sc); 408 | 409 | if (sc->received) { 410 | u = sc->upstream; 411 | if (u->state->first_byte_time == (ngx_msec_t) -1) { 412 | u->state->first_byte_time = ngx_current_msec 413 | - u->start_time; 414 | } 415 | } 416 | 417 | if (ngx_handle_write_event(c->write, 0) != NGX_OK) { 418 | ngx_srt_conn_finalize(sc, NGX_SRT_INTERNAL_SERVER_ERROR); 419 | return; 420 | } 421 | } 422 | 423 | if (sc->connected) { 424 | pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_srt_proxy_module); 425 | 426 | ngx_add_timer(c->write, pscf->timeout); 427 | } 428 | } 429 | 430 | 431 | static void 432 | ngx_stream_srt_proxy_connect_handler(ngx_event_t *ev) 433 | { 434 | ngx_srt_conn_t *sc; 435 | ngx_connection_t *pc; 436 | ngx_stream_session_t *s; 437 | 438 | pc = ev->data; 439 | sc = pc->data; 440 | s = sc->session; 441 | 442 | if (ev->timedout) { 443 | ngx_log_error(NGX_LOG_ERR, pc->log, NGX_ETIMEDOUT, 444 | "ngx_stream_srt_proxy_connect_handler: upstream timed out"); 445 | ngx_srt_conn_finalize(sc, NGX_STREAM_BAD_GATEWAY); 446 | return; 447 | } 448 | 449 | ngx_del_timer(pc->write); 450 | 451 | ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, 452 | "ngx_stream_srt_proxy_connect_handler: called"); 453 | 454 | ngx_stream_srt_proxy_init_upstream(s); 455 | } 456 | 457 | 458 | static ngx_int_t 459 | ngx_stream_srt_proxy_test_finalize(ngx_srt_conn_t *sc, 460 | ngx_uint_t from_upstream) 461 | { 462 | ngx_srt_stream_t *st; 463 | ngx_connection_t *c, *pc; 464 | ngx_log_handler_pt handler; 465 | 466 | st = sc->stream; 467 | c = st->connection; 468 | pc = sc->connected ? sc->connection : NULL; 469 | 470 | c->log->action = "proxying connection"; 471 | 472 | if (pc == NULL 473 | || (!c->read->eof && !pc->read->eof) 474 | || (!c->read->eof && c->buffered) 475 | || (!pc->read->eof && pc->buffered)) 476 | { 477 | return NGX_DECLINED; 478 | } 479 | 480 | handler = c->log->handler; 481 | c->log->handler = NULL; 482 | 483 | ngx_log_error(NGX_LOG_INFO, c->log, 0, 484 | "ngx_stream_srt_proxy_test_finalize: %s disconnected" 485 | ", bytes from/to client:%O/%O" 486 | ", bytes from/to upstream:%O/%O", 487 | from_upstream ? "upstream" : "client", 488 | st->received, c->sent, sc->received, pc ? pc->sent : 0); 489 | 490 | c->log->handler = handler; 491 | 492 | ngx_srt_conn_finalize(sc, NGX_STREAM_OK); 493 | 494 | return NGX_OK; 495 | } 496 | 497 | 498 | static void 499 | ngx_stream_srt_proxy_log_session(void *data) 500 | { 501 | ngx_srt_conn_t *sc; 502 | ngx_connection_t *pc; 503 | ngx_stream_srt_proxy_upstream_t *u; 504 | 505 | sc = data; 506 | u = sc->upstream; 507 | 508 | if (u->state->response_time == (ngx_msec_t) -1) { 509 | u->state->response_time = ngx_current_msec - u->start_time; 510 | } 511 | 512 | pc = sc->connection; 513 | if (pc) { 514 | u->state->bytes_received = sc->received; 515 | u->state->bytes_sent = pc->sent; 516 | } 517 | } 518 | 519 | 520 | static void 521 | ngx_stream_srt_proxy_cleanup(void *data) 522 | { 523 | ngx_srt_conn_t *sc; 524 | ngx_stream_session_t *s; 525 | 526 | sc = data; 527 | s = sc->session; 528 | 529 | ngx_stream_finalize_session(s, sc->status); 530 | } 531 | 532 | 533 | static u_char * 534 | ngx_stream_srt_proxy_log_error(ngx_log_t *log, u_char *buf, size_t len) 535 | { 536 | u_char *p; 537 | ngx_srt_conn_t *sc; 538 | ngx_connection_t *pc; 539 | ngx_srt_stream_t *st; 540 | ngx_stream_session_t *s; 541 | ngx_stream_srt_proxy_upstream_t *u; 542 | 543 | s = log->data; 544 | 545 | sc = ngx_stream_get_module_ctx(s, ngx_stream_srt_proxy_module); 546 | 547 | p = buf; 548 | 549 | u = sc->upstream; 550 | 551 | if (u->state && u->state->peer) { 552 | p = ngx_snprintf(p, len, ", upstream: \"%V\"", u->state->peer); 553 | len -= p - buf; 554 | } 555 | 556 | pc = sc->connection; 557 | st = sc->stream; 558 | 559 | p = ngx_snprintf(p, len, 560 | ", bytes from/to client:%O/%O" 561 | ", bytes from/to upstream:%O/%O", 562 | st->received, s->connection->sent, 563 | sc->received, pc ? pc->sent : 0); 564 | 565 | return p; 566 | } 567 | 568 | 569 | static void * 570 | ngx_stream_srt_proxy_create_srv_conf(ngx_conf_t *cf) 571 | { 572 | ngx_stream_srt_proxy_srv_conf_t *conf; 573 | 574 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_srt_proxy_srv_conf_t)); 575 | if (conf == NULL) { 576 | return NULL; 577 | } 578 | 579 | conf->connect_timeout = NGX_CONF_UNSET_MSEC; 580 | conf->timeout = NGX_CONF_UNSET_MSEC; 581 | conf->buffer_size = NGX_CONF_UNSET_SIZE; 582 | 583 | return conf; 584 | } 585 | 586 | 587 | static char * 588 | ngx_stream_srt_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) 589 | { 590 | ngx_stream_srt_proxy_srv_conf_t *prev = parent; 591 | ngx_stream_srt_proxy_srv_conf_t *conf = child; 592 | 593 | ngx_conf_merge_msec_value(conf->connect_timeout, 594 | prev->connect_timeout, 60000); 595 | 596 | ngx_conf_merge_msec_value(conf->timeout, 597 | prev->timeout, 10 * 60000); 598 | 599 | ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 64 * 1024); 600 | 601 | if (conf->stream_id == NULL) { 602 | conf->stream_id = prev->stream_id; 603 | } 604 | 605 | if (conf->passphrase == NULL) { 606 | conf->passphrase = prev->passphrase; 607 | } 608 | 609 | return NGX_CONF_OK; 610 | } 611 | 612 | 613 | static char * 614 | ngx_stream_srt_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 615 | { 616 | size_t add; 617 | ngx_url_t *u; 618 | ngx_str_t *value; 619 | ngx_stream_core_srv_conf_t *cscf; 620 | ngx_stream_srt_proxy_srv_conf_t *pscf = conf; 621 | 622 | cscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_core_module); 623 | 624 | if (cscf->handler) { 625 | return "is duplicate"; 626 | } 627 | 628 | cscf->handler = ngx_stream_srt_proxy_handler; 629 | 630 | value = cf->args->elts; 631 | 632 | u = ngx_pcalloc(cf->pool, sizeof(ngx_url_t)); 633 | if (u == NULL) { 634 | return NULL; 635 | } 636 | 637 | add = 0; 638 | if (ngx_strncasecmp(value[1].data, (u_char *) "srt://", 6) == 0) { 639 | add = 6; 640 | } 641 | 642 | u->url.len = value[1].len - add; 643 | u->url.data = value[1].data + add; 644 | u->default_port = 80; 645 | u->uri_part = 1; 646 | 647 | if (ngx_parse_url(cf->pool, u) != NGX_OK) { 648 | if (u->err) { 649 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 650 | "%s in url \"%V\"", u->err, &u->url); 651 | } 652 | return NULL; 653 | } 654 | 655 | pscf->url = u; 656 | 657 | return NGX_CONF_OK; 658 | } 659 | --------------------------------------------------------------------------------