okhttp源码分析,Builder.ParseResult.parse(null, url) HttpUrl.parse(url) 方法详细分析

2023-05-16


在使用okhttp3时,以下方式具体对url进行了怎样的处理,查了许多资料没有发现有关介绍查询源码进行分析添加相关方法介绍
Request request = new Request.Builder().url(" https://www.bejson.com/mam/app/download?path=/data/web/file/提莫/新y%u123+-*/ @##$提莫.txt").get().build();
input 建议传入编码后的url,之后截取url时"+"默认是已经编码后的之后方法会提到

最后结论是:不会对"+"号进行编码,"#"号之后的不会进行编码(因为此例#做为query框架认为他是query和fragment的分隔符
截取点 如果#有特殊意义需要进行处理,可以参考,
url的划分规范,每个字段的含义) ,具体可以查看之后对类的具体分析



// https://www.bejson.com/mam/app/download?path=/data/web/file/提莫/y%u123+-*/ @##$提莫.txt

ParseResult parse(@Nullable HttpUrl base, String input) {

    // 获取input有效开始位置,过滤 case '\t': case '\n':case '\f': case '\r':case ' ':
    int pos = skipLeadingAsciiWhitespace(input, 0, input.length());

    // 获取input有效结束位置 case '\t': case '\n':case '\f': case '\r':case ' ':
    int limit = skipTrailingAsciiWhitespace(input, pos, input.length());

    // Scheme.
    //  ':' 的位置,不存在或者不合法 返回-1
    int schemeDelimiterOffset = schemeDelimiterOffset(input, pos, limit);

    // 判断超文本传输协议是否合法并截取,更新有效开始位置
    if (schemeDelimiterOffset != -1) {
        if (input.regionMatches(true, pos, "https:", 0, 6)) {
            this.scheme = "https";
            pos += "https:".length();
        } else if (input.regionMatches(true, pos, "http:", 0, 5)) {
            this.scheme = "http";
            pos += "http:".length();
        } else {
            return ParseResult.UNSUPPORTED_SCHEME; // Not an HTTP scheme.
        }
    } else if (base != null) {
        this.scheme = base.scheme;
    } else {
        return ParseResult.MISSING_SCHEME; // No scheme.
    }

    // Authority.
    boolean hasUsername = false;
    boolean hasPassword = false;
    //  获取 / 或者 \ 的个数
    int slashCount = slashCount(input, pos, limit);

    if (slashCount >= 2 || base == null || !base.scheme.equals(this.scheme)) {
        // Read an authority if either:
        //  * The input starts with 2 or more slashes. These follow the scheme if it exists.
        //  * The input scheme exists and is different from the base URL's scheme.
        //
        // The structure of an authority is:
        //   username:password@host:port
        //
        // Username, password and port are optional.
        //   [username[:password]@]host[:port]
        pos += slashCount;
        authority:
        while (true) {

            //  @/\?#  其中之一符号的位置
            int componentDelimiterOffset = delimiterOffset(input, pos, limit, "@/\\?#");
            int c = componentDelimiterOffset != limit
                    ? input.charAt(componentDelimiterOffset)
                    : -1;
            switch (c) {
                case '@'://: 截取用户名密码  目前有敏感信息都使用post 可以不用考虑
                    // User info precedes.
                    if (!hasPassword) {
                        //获取:的位置
                        int passwordColonOffset = delimiterOffset(
                                input, pos, componentDelimiterOffset, ':');

                        String canonicalUsername = canonicalize(
                                input, pos, passwordColonOffset, USERNAME_ENCODE_SET,
                                true, false, false, true, null);

                        this.encodedUsername = hasUsername
                                ? this.encodedUsername + "%40" + canonicalUsername
                                : canonicalUsername;

                        if (passwordColonOffset != componentDelimiterOffset) {
                            hasPassword = true;
                            this.encodedPassword = canonicalize(
                                    input, passwordColonOffset + 1, componentDelimiterOffset,
                                    PASSWORD_ENCODE_SET, true, false,
                                    false, true, null);
                        }
                        hasUsername = true;
                    } else {
                        this.encodedPassword = this.encodedPassword + "%40" + canonicalize(
                                input, pos, componentDelimiterOffset,
                                PASSWORD_ENCODE_SET, true, false,
                                false, true, null);
                    }
                    pos = componentDelimiterOffset + 1;
                    break;

                case -1:
                case '/':
                case '\\':
                case '?':
                case '#':
                    //截取 host port
                    // Host info precedes.
                    int portColonOffset = portColonOffset(input, pos, componentDelimiterOffset);
                    if (portColonOffset + 1 < componentDelimiterOffset) {
                        this.host = canonicalizeHost(input, pos, portColonOffset);
                        this.port = parsePort(input, portColonOffset + 1, componentDelimiterOffset);
                        if (this.port == -1)
                            return ParseResult.INVALID_PORT; // Invalid port.
                    } else {
                        this.host = canonicalizeHost(input, pos, portColonOffset);
                        this.port = defaultPort(this.scheme);
                    }
                    if (this.host == null) return ParseResult.INVALID_HOST; // Invalid host.
                    // 移动位置
                    pos = componentDelimiterOffset;
                    break authority;
            }
        }
    } else {
        // This is a relative link. Copy over all authority components. Also maybe the path & query.
        this.encodedUsername = base.encodedUsername();
        this.encodedPassword = base.encodedPassword();
        this.host = base.host;
        this.port = base.port;
        this.encodedPathSegments.clear();
        this.encodedPathSegments.addAll(base.encodedPathSegments());
        if (pos == limit || input.charAt(pos) == '#') {
            encodedQuery(base.encodedQuery());
        }
    }
    
    //mam/app/download?path=/data/web/file/提莫/y%u123+-*/ @##$提莫.txt
    // Resolve the relative path.
    int pathDelimiterOffset = delimiterOffset(input, pos, limit, "?#");
    resolvePath(input, pos, pathDelimiterOffset);
    pos = pathDelimiterOffset;

    // Query.
//此时截取的是"?" "#"之间的内容会进行编码处理但是不会对"+"编码具体看此方法canonicalize()后面会进行介绍
// ?path=/data/web/file/提莫/y%u123+-*/ @#
        if (pos < limit && input.charAt(pos) == '?') {
        int queryDelimiterOffset = delimiterOffset(input, pos, limit, '#');
        this.encodedQueryNamesAndValues = queryStringToNamesAndValues(canonicalize(
                input, pos + 1, queryDelimiterOffset, QUERY_ENCODE_SET, true,
                false, true, true, null));
        pos = queryDelimiterOffset;
    }

    // Fragment.
    //#$提莫.txt  获取到"#"号之后的内容但是不会进行编码处理
    if (pos < limit && input.charAt(pos) == '#') {
        this.encodedFragment = canonicalize(
                input, pos + 1, limit, FRAGMENT_ENCODE_SET, true, false, false, false, null);
    }

    return ParseResult.SUCCESS;
}


以下两方法是针对是否编码处理判断如果是"+" plusIsSpace = true 并且

  alreadyEncoded = false  才会对"+"进行编码 



/* @param alreadyEncoded true to leave '%' as-is; false to convert it to '%25'.
 * @param strict         true to encode '%' if it is not the prefix of a valid percent encoding.
 * @param plusIsSpace    true to encode '+' as "%2B" if it is not already encoded.
 * @param asciiOnly      true to encode all non-ASCII codepoints.
 * @param charset        which charset to use, null equals UTF-8.
 */
static String canonicalize(String input, int pos, int limit, String encodeSet,
                           boolean alreadyEncoded, boolean strict, boolean plusIsSpace, boolean asciiOnly,
                           Charset charset) {
    int codePoint;
    for (int i = pos; i < limit; i += Character.charCount(codePoint)) {
        codePoint = input.codePointAt(i);
        if (codePoint < 0x20
                || codePoint == 0x7f
                || codePoint >= 0x80 && asciiOnly
                || encodeSet.indexOf(codePoint) != -1
                || codePoint == '%' && (!alreadyEncoded || strict && !percentEncoded(input, i, limit))
                || codePoint == '+' && plusIsSpace) {
            // Slow path: the character at i requires encoding!
            Buffer out = new Buffer();
            out.writeUtf8(input, pos, i);
            canonicalize(out, input, i, limit, encodeSet, alreadyEncoded, strict, plusIsSpace,
                    asciiOnly, charset);
            return out.readUtf8();
        }
    }

    // Fast path: no characters in [pos..limit) required encoding.
    return input.substring(pos, limit);
}  





  




alreadyEncoded  传入的是true 之后不会针对 "+"进行编码,其他非特殊字符会进行编码
static void canonicalize(Buffer out, String input, int pos, int limit, String encodeSet,
                         boolean alreadyEncoded, boolean strict, boolean plusIsSpace, boolean asciiOnly,
                         Charset charset) {
    Buffer encodedCharBuffer = null; // Lazily allocated.
    int codePoint;
    for (int i = pos; i < limit; i += Character.charCount(codePoint)) {
        codePoint = input.codePointAt(i);
        if (alreadyEncoded
                && (codePoint == '\t' || codePoint == '\n' || codePoint == '\f' || codePoint == '\r')) {
            // Skip this character.
        } else if (codePoint == '+' && plusIsSpace) {
            // Encode '+' as '%2B' since we permit ' ' to be
            // encoded as either '+' or '%20'.
            out.writeUtf8(alreadyEncoded ? "+" : "%2B");
        } else if (codePoint < 0x20
                || codePoint == 0x7f
                || codePoint >= 0x80 && asciiOnly
                || encodeSet.indexOf(codePoint) != -1
                || codePoint == '%' && (!alreadyEncoded || strict && !percentEncoded(input, i, limit))) {
            // Percent encode this character.
            if (encodedCharBuffer == null) {
                encodedCharBuffer = new Buffer();
            }

            if (charset == null || charset.equals(Util.UTF_8)) {
                encodedCharBuffer.writeUtf8CodePoint(codePoint);
            } else {
                encodedCharBuffer.writeString(input, i, i + Character.charCount(codePoint), charset);
            }

            while (!encodedCharBuffer.exhausted()) {
                int b = encodedCharBuffer.readByte() & 0xff;
                out.writeByte('%');
                out.writeByte(HEX_DIGITS[(b >> 4) & 0xf]);
                out.writeByte(HEX_DIGITS[b & 0xf]);
            }
        } else {
            // This character doesn't need encoding. Just copy it over.
            out.writeUtf8CodePoint(codePoint);
        }
    }
}












本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

okhttp源码分析,Builder.ParseResult.parse(null, url) HttpUrl.parse(url) 方法详细分析 的相关文章

  • Swift 中的构建器模式设置方法

    我刚刚从 Android 基础转移到 ios 在 swift 中寻找构建器模式 get 和 set 方法 但找不到类似的东西 仅发现以下内容 var ptype String get return self ptype set ptype
  • Eclipse - Builder 到底是什么?

    我不明白CDT中的构建器到底是什么 与 C C Build 内容有什么关系 我在 C C Build 配置中设置了SCons 它确实有效 我做了两个配置 发布和调试 并且启动了我的 SCons 脚本 但当我现在尝试调试时 我正在研究这些 B
  • Swift - 在带有可选参数的泛型函数中以 Nil 作为参数

    我正在尝试创建一个可以接受可选参数的通用函数 这是我到目前为止所拥有的 func somethingGeneric
  • StringBuilder append() 和 null 值

    我有一个清单Strings 我想将它们与中间的空格连接起来 所以我正在使用StringBuilder 现在如果有任何一个Strings are null 它们被存储在StringBuilder字面意思是 空 下面用一个小程序来说明这个问题
  • 测试shared_ptr是否为NULL

    我有以下代码片段 std vector lt boost shared ptr
  • Spring中指定默认属性值为NULL

    我想在 Spring XML 配置文件中定义默认属性值 我希望这个默认值是null 像这样的东西
  • 当方法尝试使用可以为空的字段时抛出哪个异常? [复制]

    这个问题在这里已经有答案了 我实际上正在从事框架开发 这意味着需要非常强大的编码方法 我面临一个问题 我不知道需要抛出哪个 System Exception 派生类 基本上情况是 我有一个类 其中的字段可以由构造函数选择性地初始化 并且具有
  • 如何处理 nil 值变量

    我有如下模型 struc Info Decodable var firstName String var lastName String 在表格视图单元格中显示时 我正在做的事情如下 personName text personArray
  • 使用 jQuery getJSON 时 JSON Feed 返回 null

    http portlandonline com shared cfm json cfm c 27321 http portlandonline com shared cfm json cfm c 27321 它返回 null 我真的无权接触
  • 比较()中的空字段该怎么办?

    在Java中 我使用一个类 其中一些字段可以null 例如 class Foo String bar 我想为这个类写一个BarComparator private static class BarComparator implements
  • OkHTTPClient代理认证如何进行?

    问题 如何向 OkHTTP 添加授权代理 我知道 OkHTTP 的构建者是否支持代理 http square github io okhttp 3 x okhttp 虽然我很难设置一个 Given a Url and a base64 en
  • 使用 Retrofit 2.0 POST 方法获取请求正文内容

    我需要在执行之前获取请求正文并使用 Retrofit 2 0 执行一些逻辑操作enque手术 但不幸的是 我无法从我的服务呼叫中获取帖子正文内容 目前 经过大量搜索后 我发现只有一种解决方案 例如logging the request我使用
  • 使用 OkHttp 时是否可以限制带宽?

    使用 OkHttp 是否可以限制带宽 可能使用网络拦截器 您可以通过两种方式使其发挥作用 手动发送请求并读取流 并在读取时进行节流 添加拦截器 使用OkHttp最好的方法是Interceptor 还有几个简单的步骤 继承Intercepto
  • 从 pandas 数据框中删除具有空值的行

    我正在尝试从数据框中删除一行 其中其中一列的值为空 我能找到的大部分帮助都与删除 NaN 值有关 到目前为止 这对我不起作用 我在这里创建了数据框 successfully crated data frame df1 ut get data
  • 在 Hive 中获取空值 使用 REGEX 创建和加载查询

    我有一个日志文件 我需要在其中使用 REGEX 存储数据 我尝试了下面的查询 但加载了所有 NULL 值 我已经检查了 REGEXhttp www regexr com http www regexr com 它对我的 数据工作正常 CRE
  • 无法检查 int 是否为 null

    我正在尝试使用字典 每当我想检查字典中是否存在某个元素时 我都会这样做 int value results get aKeyThatMayOrMayNotBePresent if value null 但编译器说我无法比较int to a
  • Hibernate 排序依据最后为空

    Hibernate 与 PostgreSQL DB 一起使用时 按列对 desc 进行排序时 空值会高于非空值 SQL99 标准提供关键字 NULLS LAST 来声明空值应低于非空值 使用 Hibernate 的 Criteria API
  • java方法中的可选参数

    我想制作一个需要 1 个必需参数和 1 个可选参数的方法 但我发现如何制作一个可选数组 方法是在参数 int b 中制作 但这是一个数组 我想制作它只是这个值是 null 或用户输入它 我可以通过创建 2 个同名的方法来实现它 但一个具有单
  • Mysql AVG 忽略零

    我需要对一列执行平均值 但我知道该列中的大多数值都为零 在所有可能的行中 只有两行可能具有正值 我如何告诉 mySQL 忽略零并仅平均实际值 假设您可能不想完全排除此类行 也许它们在您想要聚合的其他列中具有值 SELECT AVG NULL
  • 如何检查 GAS 中是否存在文件(通过 id)

    我知道有关如何检查文件是否存在的问答by name using hasnext 不过我需要检查一下按文件 ID 最好没有高级 Drive API 披露 我写了一个基于错误处理的解决方案 function ifFileExists id tr

随机推荐

  • C/C++程序编译成可执行程序步骤图文源码详解

    一个C 43 43 程序被编译为目标程序的过程中经历了四个部分 xff0c 分别是预处理 编译 汇编 链接 下面将通过一个简单的C 43 43 代码分别执行预处理 编译 汇编 链接四个步骤后的结果和基本原理讲解 注意 xff1a 博主是在u
  • 【linux】程序找不到动态库.so的解决办法|查看.so动态库信息|.so动态库加载顺序

    目录 找不到 so解决方法 方法一 xff1a 添加环境变量 方法二 xff1a 复制so文件到lib路径 方法三 xff1a xff08 推荐 xff09 添加ldconfig寻找路径 方法四 xff1a 在编译目标代码时指定该程序的动态
  • 使用Arduino开发ESP32(08):TCP Client与TCP Server使用

    文章目录 目的TCP Client使用说明常用方法基础使用演示作为WEB Client使用 TCP Server使用说明常用方法基础使用演示作为WEB Server使用 总结 目的 TCP是网络应用中常用的功能 xff0c 很多高级功能也是
  • ModBus学习笔记

    一 什么是ModBus xff1f 1 预备知识 xff08 1 xff09 什么是通讯协议 xff1f 通信协议是指双方实体完成通信或服务所必须遵循的规则和约定 通过通信信道和设备互连起来的多个不同地理位置的数据通信系统 xff0c 要使
  • Jetson TX2 将系统迁移到SD卡,系统文件修改方式

    系统迁移步骤 xff1a 格式化SD卡 复制系统到SD卡 修改系统文件 1 在原系统盘内 cd boot extlinux sudo vim extlinux conf 该文件初始内容如下 xff1a TIMEOUT 30 DEFAULT
  • svn中打标签的一种方法

    SVN创建标签的方法 方法一 xff1a TortoiseSVN客户端浏览创建 选中需要创建标签的目录 xff0c 右键 gt copy to 在弹出框中输入新建标签所在的URL地址 xff0c 填写log信息 xff0c 确定 方法二 x
  • (图解 HTTP)一篇文章带你深入了解 HTTP 协议

    文章目录 一 了解客户端和服务器通讯的过程二 HTTP 是不保存状态的协议三 请求 URI 定位资源四 告知服务器意图的 HTTP 方法1 GET xff1a 获取资源2 POST xff1a 传输实体主体3 PUT xff1a 传输文件4
  • VC编译选项

    C 在预处理输出中保留注释语句 c 只编译 xff0c 不连接 xff0c 相当于在 34 Build 34 菜单下选择了 34 Compile 34 D 定义常量和宏 xff0c 与源程序里的 define 有相同效果 E 预处理C C
  • C语言中String库函数

    为了以后学习以及查阅方便 xff0c 转贴在此 xff0c 若有雷同 xff0c 敬请包含 文中内容摘自 C程序设计教程 xff08 美 xff09 H M Deitel P J Deitel著 xff0c 薛万鹏等译 xff0c 机械工业
  • JAVA与海康威视人脸机对接,使用ISUP方式

    1下载DEMO包 下载地址 JAVA海康威视人脸机isup方式对接demo包 Java文档类资源 CSDN下载 2设置依赖 需要把examples jar和jna jar引入项目 3配置本地 config properties 把ip地址设
  • Keil工程

    文章目录 1 Keil工程添加源文件和头文件 xff08 c和 h xff09 的方法1 方式一2 方式二 2 keil工程生成的MAP文件取消优化 1 Keil工程添加源文件和头文件 xff08 c和 h xff09 的方法 1 方式一
  • 2020-09-28

    通用异步收发器 xff08 Universal Asynchronous Receiver Transmitter xff0c 通常称作UART xff0c 是一种串行 异步 全双工的通信协议 xff0c 在嵌入式领域应用的非常广泛 UAR
  • 【cmake】CMakeList添加库|添加头文件|添加路径|add_executable、add_library、target_link_libraries|添加编译选项|宏开关

    目录 官网查阅 开胃菜例子 CMakeLists生成和添加依赖库 CMakeLists更多小例子 生成 so共享库文件 调用 so共享库文件 生成一个可执行程序的 CMakeList 生成一个 so动态库的 CMakeList add li
  • TCP连接的建立

    前言 xff1a TCP的问题已然困惑我很久了 xff0c 一直是一知半解 xff0c 靠记忆来记住TCP连接的过程 xff0c 不能根本上理解 xff0c 漏洞百出 xff0c 最近抽时间把TCP经典书籍 TCP IP详解 阅读了一下 废
  • 【Nokov】动作捕捉系统培训笔记

    Nokov度量科技 简介 xff1a Nokov是一种光学三维动作捕捉系统 xff0c 采用红外镜头捕捉被动发光标记点 xff0c 构建三维数据的动作采集与分析系统 xff0c 运用于运动分析 步态康复 模拟训练 机械仿生 机器人 无人机
  • 【Nokov】动作捕捉系统标定与机械臂各坐标系的说明

    导语 xff1a 这一周的工作先是完成了度量系统Nokov的标定 xff0c 然后对机械臂自身的编码器得到的坐标值与动作捕捉系统Nokov测得的坐标值进行了比较 xff0c 来观察二者之间的误差 在这个过程中我对Nokov软件Seeker的
  • 【Nokov】关于动捕系统获取刚体姿态的说明

    动作捕捉系统Nokov获取刚体的姿态信息 前言 xff1a 对于动捕系统软件Seeker的基本使用以及获取单个Marker的位置操作已经比较熟悉了 xff0c 对于机械臂而言 xff0c 接下来就是获取它的姿态信息 xff0c 经过昨天下午
  • 【机器人】机械臂与动捕Nokov的深入了解

    导语 xff1a 每次的实践操作后 xff0c 总能刷新我对机械臂以及Nokov的认识 xff0c 既让我惊喜不已 xff0c 同时也让我知道我掌握的还远远不够 xff0c 需要不断的学习 关于机械臂 示教器上NOA姿态表示方式 xff1a
  • Ubuntu18.04+ROS+kalibr标定工具箱安装编译

    目录 前言 一 安装ROS 1 设置镜像源 2 更新软件包索引 3 安装ROS 4 测试ROS是否安装成功 二 安装kalibr melodic 1 kalibr简介 2 安装kalibr 3 测试kalibr 参考文献 xff1a 前言
  • okhttp源码分析,Builder.ParseResult.parse(null, url) HttpUrl.parse(url) 方法详细分析

    在使用okhttp3时 以下方式具体对url进行了怎样的处理 查了许多资料没有发现有关介绍查询源码进行分析添加相关方法介绍 Request request 61 new Request Builder url 34 https www be