ngx_escape_uri

定义在 src/core/ngx_string.h

uintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size,
    ngx_uint_t type);

定义在 src\core\ngx_string.c 

uintptr_t
ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
{
    ngx_uint_t      n;
    uint32_t       *escape;
    static u_char   hex[] = "0123456789ABCDEF";

    /*
     * Per RFC 3986 only the following chars are allowed in URIs unescaped:
     *
     * unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
     * gen-delims    = ":" / "/" / "?" / "#" / "[" / "]" / "@"
     * sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
     *               / "*" / "+" / "," / ";" / "="
     *
     * And "%" can appear as a part of escaping itself.  The following
     * characters are not allowed and need to be escaped: %00-%1F, %7F-%FF,
     * " ", """, "<", ">", "\", "^", "`", "{", "|", "}".
     */

                    /* " ", "#", "%", "?", not allowed */

    static uint32_t   uri[] = {
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
        0xd000002d, /* 1101 0000 0000 0000  0000 0000 0010 1101 */

                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
        0x50000000, /* 0101 0000 0000 0000  0000 0000 0000 0000 */

                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
        0xb8000001, /* 1011 1000 0000 0000  0000 0000 0000 0001 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
    };

                    /* " ", "#", "%", "&", "+", ";", "?", not allowed */

    static uint32_t   args[] = {
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
        0xd800086d, /* 1101 1000 0000 0000  0000 1000 0110 1101 */

                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
        0x50000000, /* 0101 0000 0000 0000  0000 0000 0000 0000 */

                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
        0xb8000001, /* 1011 1000 0000 0000  0000 0000 0000 0001 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
    };

                    /* not ALPHA, DIGIT, "-", ".", "_", "~" */

    static uint32_t   uri_component[] = {
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
        0xfc009fff, /* 1111 1100 0000 0000  1001 1111 1111 1111 */

                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
        0x78000001, /* 0111 1000 0000 0000  0000 0000 0000 0001 */

                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
        0xb8000001, /* 1011 1000 0000 0000  0000 0000 0000 0001 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
    };

                    /* " ", "#", """, "%", "'", not allowed */

    static uint32_t   html[] = {
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
        0x500000ad, /* 0101 0000 0000 0000  0000 0000 1010 1101 */

                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
        0x50000000, /* 0101 0000 0000 0000  0000 0000 0000 0000 */

                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
        0xb8000001, /* 1011 1000 0000 0000  0000 0000 0000 0001 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
    };

                    /* " ", """, "'", not allowed */

    static uint32_t   refresh[] = {
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
        0x50000085, /* 0101 0000 0000 0000  0000 0000 1000 0101 */

                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
        0x50000000, /* 0101 0000 0000 0000  0000 0000 0000 0000 */

                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
        0xd8000001, /* 1011 1000 0000 0000  0000 0000 0000 0001 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
    };

                    /* " ", "%", %00-%1F */

    static uint32_t   memcached[] = {
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
        0x00000021, /* 0000 0000 0000 0000  0000 0000 0010 0001 */

                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */

                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */

        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
    };

                    /* mail_auth is the same as memcached */

    static uint32_t  *map[] =
        { uri, args, uri_component, html, refresh, memcached, memcached };


    escape = map[type];

    if (dst == NULL) {

        /* find the number of the characters to be escaped */

        n = 0;

        while (size) {
            if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
                n++;
            }
            src++;
            size--;
        }

        return (uintptr_t) n;
    }

    while (size) {
        if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
            *dst++ = '%';
            *dst++ = hex[*src >> 4];
            *dst++ = hex[*src & 0xf];
            src++;

        } else {
            *dst++ = *src++;
        }
        size--;
    }

    return (uintptr_t) dst;
}

ngx_escape_uri 是 Nginx 中用于对 URI 或相关字符串进行转义(编码)的函数,根据不同的规则将特殊字符转换为 %XX 形式的十六进制编码


    /*
     * Per RFC 3986 only the following chars are allowed in URIs unescaped:
     *
     * unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
     * gen-delims    = ":" / "/" / "?" / "#" / "[" / "]" / "@"
     * sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
     *               / "*" / "+" / "," / ";" / "="
     *
     * And "%" can appear as a part of escaping itself.  The following
     * characters are not allowed and need to be escaped: %00-%1F, %7F-%FF,
     * " ", """, "<", ">", "\", "^", "`", "{", "|", "}".
     */

 RFC 3986 是 URI(统一资源标识符)的官方规范,定义了 URI 中允许直接出现(无需转义)的字符范围。这些字符分为三类:

非保留字符(unreserved)
包括:

字母(A-Za-z)

数字(0-9)

特殊符号:-._~
用途 :这些字符在 URI 中无特殊语义,可直接使用

通用分隔符(gen-delims)
包括::/?#[]@
用途 :用于分隔 URI 的不同组成部分(如协议、路径、查询参数等),需保留原义。

子分隔符(sub-delims)
包括:!$&'()*+,;=
用途 :在 URI 的特定上下文中作为分隔符(如查询参数中的 &=),需根据场景决定是否转义。

需转义的字符范围

根据注释,以下字符必须被转义(编码为 %XX 形式):

  • 控制字符
    ASCII 码范围:%00-%1F(0-31,控制字符)、%7F-%FF(127-255,含 DEL 和扩展字符)。
    原因 :这些字符非可打印字符,可能引发协议解析错误或安全风险。

  • 特殊符号
    包括:空格()、双引号(")、尖括号(<>)、反斜杠(\)、脱字符(^)、反引号(`)、花括号({})、竖线(|)。
    原因 :这些字符在 URI 中无明确定义,可能被服务器或浏览器错误解析(如空格可能被误解为分隔符)。

  • 保留字符的转义场景

    • % :若其本身不用于编码(如 %20 中的 %),则需转义为 %25
    • #?&:在 URI 的特定部分(如路径或查询参数)中需转义,以避免改变 URI 的语义。
                    /* " ", "#", "%", "?", not allowed */

    static uint32_t   uri[] = {
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
        0xd000002d, /* 1101 0000 0000 0000  0000 0000 0010 1101 */

                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
        0x50000000, /* 0101 0000 0000 0000  0000 0000 0000 0000 */

                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
        0xb8000001, /* 1011 1000 0000 0000  0000 0000 0000 0001 */

        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
        0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
    };

uri[] 是一个 位图(Bitmap) ,用于定义哪些 ASCII 字符需要被转义(编码为 %XX 格式),适用于 URI 场景 (即 type 参数为 URI 时)。
核心目标 :根据 RFC 3986 标准,快速判断输入字符是否需要转义。

每个 uint32_t 的二进制位表示对应字符是否需要转义(1 表示需转义,0 表示保留)

每个 uint32_t 元素对应 32 个连续的 ASCII 字符

计算字符在数组中的位置
block = (c >> 5);      // 确定字符属于哪个 uint32_t 元素(0 ~ 7)
offset = (c & 0x1F);   // 确定字符在元素中的位偏移(0 ~ 31)
  • 示例
    • 字符 ' '(空格,ASCII 0x20):
      block = 0x20 >> 5 = 1 → 对应 uri[1]
      offset = 0x20 & 0x1F = 0 → 检查 uri[1] 的第 0 位(最右侧位)。
判断是否需要转义
  • 规则
    uri[block] 的二进制第 offset 位为 1,则字符需转义;否则保留。

以下通过示例说明关键字符在 uri[] 中的标记情况:

(1) uri[0] = 0xffffffff(覆盖 0x00 ~ 0x1F
  • 所有位均为 1 :表示所有控制字符(0x00-0x1F)均需转义。
escape = map[type];

根据 type 参数选择对应的转义规则 ,将规则数组的指针赋值给 escape 变量

核心目标 :动态切换字符转义策略,以适配不同场景(如 URI、查询参数、HTML 等)。

此时 传入的 type 参数是 type=0 

对应的是 uri 场景

    if (dst == NULL) {

该条件判断用于 区分两种工作模式

  • 统计模式 :当 dst == NULL 时,仅计算需转义的字符数量,不执行实际转义操作。
  • 执行模式 :当 dst != NULL 时,执行转义并将结果写入目标缓冲区。

此时 传入的参数    dst=NULL

条件成立

        /* find the number of the characters to be escaped */

        n = 0;

        while (size) {
            if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
                n++;
            }
            src++;
            size--;
        }

        return (uintptr_t) n;
    }

/* find the number of the characters to be escaped */

  • 作用 :注释说明当前代码块的功能。
  • 逻辑 :明确后续代码的目标是统计需要转义的字符数量。

n = 0;

  • 作用 :初始化计数器变量 n
  • 逻辑 :将 n 置为 0,用于累计需转义的字符数。
  • 意义 :确保统计从零开始,避免脏数据。

while (size) { ... }

  • 作用 :循环遍历输入字符串 src 的每个字符。
  • 逻辑 :当 size > 0 时持续循环,每次处理一个字符。
  • 意义 :确保所有字符都被检查,直到处理完 size 指定的长度。

此时 传入的参数 size=1

if (escape[*src >> 5] & (1U << (*src & 0x1f))) { ... }

  • 作用 :判断当前字符是否需要转义。
  • 逻辑
    1. 定位规则块 *src >> 5 将字符 ASCII 码右移 5 位(等效于除以 32),确定该字符属于 escape 数组的哪个元素(块)。
    2. 定位位偏移 *src & 0x1f 取 ASCII 码低 5 位(0-31),确定字符在块中的具体位置。
    3. 位掩码检查 escape[...] & (1U << ...) 检查对应位是否为 1(1 表示需转义)。
  • 1U :无符号整数 1,确保左移操作在无符号数上进行。
  • 1U << (*src & 0x1f) :将 1 左移 (*src & 0x1f) 位,生成一个 单比特掩码

n++;

  • 作用 :增加需转义字符的计数。
  • 逻辑 :当字符需转义时,计数器 n 自增 1

src++;

  • 作用 :移动源指针到下一个字符。
  • 逻辑 src 指针递增,指向下一个待处理字符。

7. size--;

  • 作用 :减少剩余待处理字符的计数。
  • 逻辑 size 递减,直到归零时退出循环。

8. return (uintptr_t) n;

  • 作用 :返回需转义的字符数。
  • 逻辑 :将整数 n 转换为 uintptr_t 类型(无符号整数,兼容指针宽度)

此时 返回的结果是 n=0

 

你可能感兴趣的:(websocket,网络协议,网络)