技术库 > Nginx

Nginx 中的Url 编码实现

技术库:tec.5lulu.com

1 url 编码  

from:tec.5lulu.com

url编码是一种传输url的机制, 我们都知道有许多字符在url中是有特殊含义的, 比如?作为参数开始的标志, 又比如&作为参数切分标志. 因此如果想要让这些 字符保留字符原本含义就需要对它们进行url编码. 另外一个作用是用来传递非可视字符. url编码的主要内容是: 0-9A-Za-z以及-, ~, ., ~无需通过编码即可传输(普通字符), 而以下字符(保留字符):

! * ( ) ; : @ & = + $ , / ? # [ ]

如果要当作字符常量对待则需要经过编码. 另外其他的字符都需要进过url编码来进行传输. 编码形式是%后跟对应的十六进制形式.

这里需要注意的有两点:

  1. 编码并非是强制性, 即使是普通字符我们也可以进行编码然后进行传输
  2. 在参数部分, 空格会被编码成+而非%25, 参见这里的The application/x-www-form-urlencoded type一节, 所以会造成经过base64编码的内容直接传输时, 加号在经过解码之后变为空格. 针对这个问题衍生出了其他的base64方式(主要是替换了/, +)

这里再推荐一下阮一峰大哥的这篇文章, 写的挺好的~

2 nginx 的实现

从原理上讲url编码还是蛮容易的, 编码就是遇到非普通字符就开始转换, 解码就是遇到百分号就开始转换(这里可能还需要考虑到各种情况, 诸如上面提到的+号的情况).

如果现在让我来实现, 我可能就直接按字符的asc码比较来进行转换了. 学编程的进步方法之一就是看优秀的代码, 那我们不妨来看看nginx中是如何实现url编码的(因为nginx考虑的url编码情况比较多, 甚至涵盖了memcached的情况, 这里就对最普通的url编码进行解析):

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";

    /* " ", "#", "%", "?", %00-%1F, %7F-%FF */

    /* 位图法判断是否需要进行编码 */
    static uint32_t   uri[] = {
        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

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

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

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

        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 */
    };

    /* 省略其他type */

    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] & (1 << (*src & 0x1f))) {    /* 判断 */
                n++;
            }
            src++;
            size--;
        }

        return (uintptr_t) n;
    }

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

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

    return (uintptr_t) dst;


代码很精简, 同时也很高效. 这里利用bitmap法来判断字符是否需要进行url编码(赞). 最关键的一句是: 

    /* 取組, 做除法 */      /* 取偏移, 求余 */
if (escape[*src >> 5] & (1 << (*src & 0x1f))) {

} 如果我利用位图法我会将字符先分组(除以32), 然后再与上取其偏移量(模32)来取得bitmap的值. 上面就是在用位运算做这个事情, 真心学习了. 而转十六进制的时候同样使用了这个方法. 强大的位运算~


Nginx 中的Url 编码实现


标签: nginx 解码 十六进制 map uri本文链接 http://tec.5lulu.com/detail/105dcn2h986hw8s59.html

我来评分 :6.1
1

转载注明:转自5lulu技术库

本站遵循:署名-非商业性使用-禁止演绎 3.0 共享协议

www.5lulu.com