URI

本文详细介绍了URI的结构组成及libevent库中用于解析URI的函数evhttp_uri_parse_with_flags的具体实现。通过源代码分析,展示了如何从原始URI字符串中提取scheme、host、path等关键部分。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

uri

uri

struct evhttp_uri {
    unsigned flags;
    char *scheme; /* scheme; e.g http, ftp etc */
    char *userinfo; /* userinfo (typically username:pass), or NULL */
    char *host; /* hostname, IP address, or NULL */
    int port; /* port, or zero */
    char *path; /* path, or "". */
    char *query; /* query, or NULL */
    char *fragment; /* fragment or NULL */
};

libevent evhttp_uri_parse_with_flags

struct evhttp_uri *
evhttp_uri_parse_with_flags(const char *source_uri, unsigned flags)
{
    char *readbuf = NULL, *readp = NULL, *token = NULL, *query = NULL;
    char *path = NULL, *fragment = NULL;
    int got_authority = 0;

    struct evhttp_uri *uri = mm_calloc(1, sizeof(struct evhttp_uri));
    if (uri == NULL) {
        event_warn("%s: calloc", __func__);
        goto err;
    }
    uri->port = -1;
    uri->flags = flags;

    readbuf = mm_strdup(source_uri);
    if (readbuf == NULL) {
        event_warn("%s: strdup", __func__);
        goto err;
    }

    readp = readbuf;
    token = NULL;

    /* We try to follow RFC3986 here as much as we can, and match
       the productions

          URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]

          relative-ref  = relative-part [ "?" query ] [ "#" fragment ]
     */

    /* 1. scheme: */
    token = strchr(readp, ':');
    if (token && scheme_ok(readp,token)) {
        *token = '\0';
        uri->scheme = mm_strdup(readp);
        if (uri->scheme == NULL) {
            event_warn("%s: strdup", __func__);
            goto err;
        }
        readp = token+1; /* eat : */
    }

    /* 2. Optionally, "//" then an 'authority' part. */
    if (readp[0]=='/' && readp[1] == '/') {
        char *authority;
        readp += 2;
        authority = readp;
        path = end_of_authority(readp);
        if (parse_authority(uri, authority, path) < 0)
            goto err;
        readp = path;
        got_authority = 1;
    }

    /* 3. Query: path-abempty, path-absolute, path-rootless, or path-empty
     */
    path = readp;
    readp = end_of_path(path, PART_PATH, flags);

    /* Query */
    if (*readp == '?') {
        *readp = '\0';
        ++readp;
        query = readp;
        readp = end_of_path(readp, PART_QUERY, flags);
    }
    /* fragment */
    if (*readp == '#') {
        *readp = '\0';
        ++readp;
        fragment = readp;
        readp = end_of_path(readp, PART_FRAGMENT, flags);
    }
    if (*readp != '\0') {
        goto err;
    }

    /* These next two cases may be unreachable; I'm leaving them
     * in to be defensive. */
    /* If you didn't get an authority, the path can't begin with "//" */
    if (!got_authority && path[0]=='/' && path[1]=='/')
        goto err;
    /* If you did get an authority, the path must begin with "/" or be
     * empty. */
    if (got_authority && path[0] != '/' && path[0] != '\0')
        goto err;
    /* (End of maybe-unreachable cases) */

    /* If there was no scheme, the first part of the path (if any) must
     * have no colon in it. */
    if (! uri->scheme && !path_matches_noscheme(path))
        goto err;

    EVUTIL_ASSERT(path);
    uri->path = mm_strdup(path);
    if (uri->path == NULL) {
        event_warn("%s: strdup", __func__);
        goto err;
    }

    if (query) {
        uri->query = mm_strdup(query);
        if (uri->query == NULL) {
            event_warn("%s: strdup", __func__);
            goto err;
        }
    }
    if (fragment) {
        uri->fragment = mm_strdup(fragment);
        if (uri->fragment == NULL) {
            event_warn("%s: strdup", __func__);
            goto err;
        }
    }

    mm_free(readbuf);

    return uri;
err:
    if (uri)
        evhttp_uri_free(uri);
    if (readbuf)
        mm_free(readbuf);
    return NULL;
}
03-15
### URI 的定义及用法 #### 什么是 URIURI(Uniform Resource Identifier),即统一资源标识符,是用来唯一标识互联网上资源的一种标准方式。它既可以用来定位资源的位置,也可以作为资源名称的一部分。URI 可分为两类:URL(Uniform Resource Locator)和 URN(Uniform Resource Name)。其中 URL 主要用于描述如何访问某个资源,而 URN 则更关注于命名资源本身。 根据引用说明[^1],`URI` 类在 Java 中被设计为管理 URL 编码和解码的核心工具,并支持通过 `toURI()` 和 `URI.toURL()` 方法实现与 URL 对象之间的相互转换。 --- #### URI 的基本结构 一个典型的 URI 结构通常由以下几个部分组成: - **scheme**: 表示协议类型,例如 http、https 或 ftp。 - **authority**: 包括主机名和端口号,有时还包括用户名和密码。 - **path**: 资源的具体路径。 - **query**: 查询参数,通常以键值对的形式存在。 - **fragment**: 片段标识符,指向文档中的某一部分。 以下是 URI 的通用语法: ``` <scheme>://<authority>/<path>?<query>#<fragment> ``` 例如,在 URI `http://example.com/path/to/resource?name=value#section1` 中: - scheme 是 `http` - authority 是 `example.com` - path 是 `/path/to/resource` - query 是 `name=value` - fragment 是 `section1` --- #### URI 的规范化 当处理 URI 时,可能需要对其进行规范化操作。这一步骤旨在消除冗余或不必要的部分,从而得到一个更为简洁的标准形式。根据引用内容[^2],如果 URI 已经是以规范形式存在的不透明 URI,则无需进一步处理;否则可以通过调用相关方法来完成路径的标准化。 --- #### 使用 URI 进行编码和解码 由于网络传输的需求,某些字符可能会引起歧义甚至安全风险,因此需要对这些特殊字符进行转义处理。Java 提供了内置的方法来进行这种编码/解码工作。例如: ```java import java.net.URI; public class UriExample { public static void main(String[] args) throws Exception { String rawUrl = "https://example.com/search?q=hello world"; URI uri = new URI(rawUrl); System.out.println("Raw URI: " + rawUrl); System.out.println("Normalized URI: " + uri.normalize().toString()); } } ``` 上述代码展示了如何创建并规范化一个简单的 URI 实例。 --- #### 防御 URI 绑定攻击 需要注意的是,在实际应用中可能存在恶意构造的 URI 请求试图突破系统的权限控制逻辑。为了防范此类威胁,开发者应当采取适当的安全措施。例如,在 Nginx 配置文件中设置严格的匹配规则以及启用必要的验证流程可以有效减少潜在漏洞的影响范围[^4]。 --- #### Python 中的 URI 处理库——UriTemplate 除了传统的编程语言外,还有专门针对 URI 操作优化过的第三方库可供选用。比如 Python 社区推荐使用的 `uritemplate` 就是一个遵循 RFC 6570 标准的好例子[^3]。借助它可以轻松地动态生成复杂的 Web 地址链接地址串。 安装命令如下所示: ```bash pip install uritemplate ``` 简单示范片段: ```python from uritemplate import expand template = "{+host}/search{?q}" expanded_uri = expand(template, {"host": "https://api.example.com", "q": "keyword"}) print(expanded_uri) ``` 运行结果将是类似于这样的字符串输出:“`https://api.example.com/search?q=keyword`”。 --- #### Android 开发中的 FileProvider 与 URI 最后值得一提的是,在现代移动应用程序开发领域里,尤其是基于 HarmonyOS 或 Android 平台的应用场景下,经常需要用到名为 `FileProvider` 的组件来共享本地存储上的私有数据给其他程序读取使用。此时会涉及到将文件对象转化为合法合规的 content-type URI 形式的步骤[^5]。 典型做法如下: ```java import android.content.ContentResolver; import android.net.Uri; import androidx.core.content.FileProvider; // 假设 activity 当前上下文中已知目标文件 file String authorities = getApplicationContext().getPackageName() + ".fileprovider"; Uri contentUri = FileProvider.getUriForFile(activity, authorities, file); ContentResolver resolver = getContentResolver(); resolver.openInputStream(contentUri); // 执行后续动作... ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值