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) 方法详细分析 的相关文章

  • 如何在UITextField上自动打开键盘?

    我有一个非常简单的表格 当触摸单元格时 它会打开一个带有一个 UITextfield 的新视图 我想要的只是键盘会自动打开 而用户无需触摸 UITextfield 这一切都是在 Interface Builder 中完成的 所以我不确定如何
  • Mockito - thenReturn 始终返回 null 对象

    我正在尝试实现 Mockito 来测试特定方法 但 thenReturn 似乎总是返回一个 null 对象 而不是我想要的 CUT public class TestClassFacade injected via Spring priva
  • 如何删除非空约束?

    假设创建了一个表 如下所示 create table testTable colA int not null 您将如何删除非空约束 我正在寻找类似的东西 ALTER TABLE testTable ALTER COLUMN colA DRO
  • 如果一列没有值,MySQL 返回最大值或 null

    我尝试获取 mysql select 的最大值 但如果有一行不包含时间戳 则希望将其设置为 null empty 0 表统计数据 简化 ID CLIENT ORDER DATE CANCEL DATE 1 5 1213567200 2 5
  • Microsoft SQL:CASE WHEN 与 ISNULL/NULLIF

    除了可读性之外 在防止 SQL 中的除以 0 错误时 使用 CASE WHEN 语句与 ISNULL NULLIF 相比还有什么显着的好处吗 CASE WHEN BeginningQuantity BAdjustedQuantity 0 T
  • 面向对象编程语言中的引用默认情况下是否应该不可为空? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 何时检查未定义以及何时检查 null

    赏金编辑 我正在寻找一个很好的解释 当你应该设置 使用null or undefined以及您需要在哪里进行检查 基本上这两者的常见做法是什么 真的可以在通用的可维护代码中单独对待它们吗 我什么时候可以安全地检查 null 安全地检查 un
  • AngularJs 表单发布数据在我的 spring 控制器中给出空值

    大家好 我正在尝试使用 Angular 发布表单 但我在 Spring 控制器中收到空值 此外 在我的控制台中 我看到 sysout 的空值 此外 即使我看到 bull 打印在我的控制台上 我也会收到错误警报 我的 JS 控制器 angul
  • KDB 排除具有空值的行

    我有一个表 其中有一些带有空值的单元格 分散在数据集中 有什么简单的方法可以排除任何列中包含空值的所有行吗 我只是想避免这种情况 select from T where not null col1 not null col2 not nul
  • 堆分配对象是否有一个永不为空的唯一所有者?

    目前 我正在存储一个集合std unique ptrs 到堆分配的多态类型对象 struct Foo virtual Foo default Collection
  • 改造将多个图像上传到单个密钥

    我正在使用 Retrofit 将图像上传到我的服务器 这里我需要为一个密钥上传多个图像 我已经尝试使用 Postman 网络客户端 它运行良好 这是一个屏幕截图 以下是请求的键值对 调查图像 文件1 文件2 文件3 属性图像 文件DRA j
  • 使用 jq 过滤空值和/或 null 值

    我有一个包含 jsonlines 的文件 想找到空值 name Color TV price 1200 available name DVD player price 200 color null 并希望输出空和 或空值及其键 availa
  • Android 如何使用 OkHttp 从 Callback 获取响应字符串?

    这是我的代码 OkHttpClient okHttpClient new OkHttpClient Request request new Request Builder url http publicobject com hellowor
  • 无法索引空数组

    我正在使用一个模板 该模板根据服务器备份是否成功的条件设置单元格颜色 我有下面的代码 它不断抛出错误 无法索引到空数组 Cannot index into a null array At C Users admin Desktop new
  • 如何更改 OkHttp 响应中的正文?

    我正在使用改造 为了捕获响应 我正在使用拦截器 OkHttpClient okHttpClient new OkHttpClient okHttpClient interceptors add myinterceptor 这是拦截器的代码
  • 空合并运算符分配给 self

    我目前在 Unity 中设置了两个脚本来处理一些 UI 音频 一个是管理器 另一个是为特定 UI 元素播放声音 我所拥有的简化版本是这样的 public class AudioUIManager MonoBehaviour Only one
  • 使用空合并运算符的独特方法[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我知道使用的标准方法空合并运算符 https en wikipedia org wiki Null coalescing operator在C
  • 空字符串与NULL

    我有一个表 其中一些行有一些空白cells 我尝试使用 IS NULL 函数选择此类行 但查询选择了 0 行 select from zzz fkp registration female where fname is null 0 row
  • okHttp3 java.lang.NoSuchMethodError:没有虚拟方法 setCallWebSocket

    我已从 okhttp Retrofit 更新到 okhttp3 Retrofit2 但我的应用程序因此异常而无法启动 FATAL EXCEPTION EventThread Process appli speaky com PID 1470
  • 拒绝重新初始化 OkHttp 中先前失败的类

    我正在尝试使用 Flask 后端将图像上传到我的服务器 该后端将处理要保存在文件夹中的文件和 android 中的 OkHttp 但我在 android 中收到此错误 I art Rejecting re init on previousl

随机推荐

  • 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