当okhttp网络库遇到不规范的http状态码

news2024/11/16 11:48:05

如题,最近工作遇到的问题,我们的 Android 应用网络请求埋点报表,收集到了奇怪的网络请求异常;通过日志收集与分析,确定到是服务器返回了不规范的状态码所导致。
在这里插入图片描述
如上是根据线上的业务场景,本地写个简单的MockServer 以及一个简单的 Java 应用(使用 okhttp),重刻出的现场后 client 端的异常堆栈。我们可以看到 okhttp 对于非法的不规范的响应码,直接就抛出ProtocolException,中断http 响应报文的解析,通过onFailure回调通知上层调用者
在这里插入图片描述

示例代码

server

from flask import Flask, jsonify, make_response

app = Flask(__name__)

@app.route('/')
def not_found():
    response = {
        'error': 'Not found'
    }
    return make_response(jsonify(response), 36)  # HTTP 30

if __name__ == '__main__':
    app.run(port=8080)

client

package com.luo;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Call;
import okhttp3.Callback;

import java.io.IOException;

public class OkHttpGetRequestExample {

    public static void main(String[] args) {
        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
                .url("http://127.0.0.1:8080/")
                .build();

        // 异步请求
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
                System.out.println("请求失败:" + e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    System.out.println("请求成功:" + response.body().string());
                } else {
                    System.out.println("请求失败:" + response.code());
                }
            }
        });
    }
}

其它

如果上面那个 Server 的代码使用 python 自带的网络组件,唯一的区别是返回的响应行,只有响应码/状态码 36 ,没有状态消息
在这里插入图片描述

from http.server import BaseHTTPRequestHandler, HTTPServer
import json

class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):

    def do_GET(self):
        # 创建一个字典,包含要返回的 JSON 数据
        data = {
            'status': 'fail',
            'message': 'This is a JSON response',
            'code': 36
        }
        # 将字典转换为 JSON 字符串
        json_data = json.dumps(data)
        # 设置响应状态码为 200
        self.send_response(36)
        # 设置响应头
        self.send_header('Content-type', 'application/json')
        self.end_headers()
        # 设置响应内容
        self.wfile.write(json_data.encode('utf-8'))

# 设置服务器的端口号
server_address = ('', 8080)
httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)
print("Starting httpd on port", server_address[1])
# 开始监听
httpd.serve_forever()

如果你用 Node js 来写上面的 Mockserver,当 server 收到请求,服务就crash 了,因为代码中设置了个非法的响应码
在这里插入图片描述
对应 client 那边侧是EOFException的出现
在这里插入图片描述

上面的 Mockserver 的代码如下:

const http = require('http');

const server = http.createServer((req, res) => {
    // 检查请求是否为GET方法
    if (req.method === 'GET') {
        // 设置响应头部为JSON
        res.setHeader('Content-Type', 'application/json');
        // 设置HTTP状态码为36
        res.statusCode = 36;
        // 发送响应数据
        res.end(JSON.stringify({
            status: 0,
            message: 'Not Found'
        }));
    } else {
        // 如果不是GET请求,返回404
        res.statusCode = 404;
        res.end(JSON.stringify({
            status: -1,
            message: 'Not Found'
        }));
    }
});

const PORT = 8080; // 你可以选择任何未被占用的端口
server.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});

http 协议规范

让我们一起来复习 http协议规范中的 http 响应头这块的内容

状态行

HTTP响应行,也称为状态行,是HTTP响应报文的第一行。它包含了以下三个部分:

HTTP协议版本:指定了用于通信的HTTP协议的版本,如HTTP/1.1或HTTP/2。这告诉客户端服务器支持的HTTP版本。

状态码:一个三位数字,表示请求的结果。状态码分为五类:

1xx:指示信息,表示请求已接收,继续处理。
2xx:成功,表示请求已被成功接收、理解、并接受。
3xx:重定向,表示需要后续操作以完成请求。
4xx:客户端错误,表示请求包含语法错误或无法完成请求。
5xx:服务器错误,表示服务器在处理请求的过程中发生了错误。

状态消息:状态码的简短描述,通常是状态码的文字解释,如"OK"、"Not Found"、"Internal Server Error"等。状态消息是可选的,但通常包含在响应中以提高可读性。

状态码

HTTP状态码总是三位数字,这是HTTP协议规范的一部分。如果服务器返回的状态码不是三位数字,那么这个响应是不符合HTTP协议标准的,客户端(如浏览器或其他HTTP客户端)可能会无法正确解析和理解该响应。

HTTP状态码的第一位数字定义了响应的类别:

1xx(信息性状态码):表示接收的请求正在处理。
2xx(成功状态码):表示请求正常处理完毕。
3xx(重定向状态码):需要后续操作才能完成这一请求。
4xx(客户端错误状态码):表示请求包含语法错误或无法完成。
5xx(服务器错误状态码):服务器在处理请求的过程中发生了错误。
如果遇到非标准的响应,客户端可能会采取以下措施:

显示错误:对于无法识别的响应,客户端可能会向用户显示错误信息。
忽略响应:在某些情况下,客户端可能会忽略该响应并尝试重新发起请求。
记录日志:客户端可能会记录日志信息,以供后续分析问题。
如果你是服务器端开发者,应确保返回的HTTP状态码符合标准。如果你是客户端开发者,应确保你的客户端能够妥善处理非标准响应。

值得一提的是,虽然在理论上状态码应该是三位数字,但实际上,几乎所有的HTTP客户端和服务器都对非标准的响应码有一定的容忍度。例如,如果服务器返回了200 OK后跟了额外的非标准文字,很多客户端可能会忽略这些额外的文字。但是,遵循标准总是最佳实践。

调试技巧

上面 okhttp 抛出异常的情况下,我们可以在readResponseHeaders方法设置一个断点,然后通过headersReader.readLine() 读出整个报文
在这里插入图片描述
在这里插入图片描述

直接用浏览器访问 http://localhost:8080/ 可以在网络调试窗口看到对应的报文(响应头)
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2165353.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二进制位运算题

本期介绍🍖 主要介绍:1. 在不创建临时变量的情况下交换两个变量,2. 计算变量在内存中存放2进制位“1”的个数,3. 求两个数的二进制中不同位的个数,4. 分别打印整数的二进制中奇数位和偶数位,5. 判断一个整数…

SentencePiece进行文本分类

SentencePieces 前言 Step1:故事 SentencePiece 是一个无监督的文本分词器和 detokenizer(还原回去的?)主要用于词汇表大小是预定的文本生成系统中它拓展了原始句子的训练,实现子词单元如 BPE 和 unigram language model技术亮点 纯数据驱动&#xff…

Qemu开发ARM篇-6、emmc/SD卡AB分区镜像制作

文章目录 1、AB分区镜像制作2、uboot修改3、镜像启动 在上一篇 Qemu开发ARM篇-5、buildroot制作根文件系统并挂载启动中,我们通过buildroot制作了根文件系统,并通过 SD卡的形式将其挂载到设备并成功进行了启动,但上一章中,我们的…

车载应用的多功能需求与公安、金融等行业的应用特点

随着科技的快速发展,车载应用的功能需求也日益多样化。除了基本的视频监控功能外,现代车载应用还需满足一系列高级功能,如无线网络视频监控、GPS卫星定位、车辆调度、语音报站、行驶信息记录以及多媒体娱乐广告播放等。这些功能在公安、金融等…

2024年数字化转型与管理国际学术会议(DTM 2024)

目录 重要信息 大会简介 大会组委 征稿主题 论文出版 会议议程 参会方式 重要信息 大会官网:www.icemme.org(点击了解大会,投稿等详细信息) 大会时间:2024年11月22-24日 大会地点:中国-大连 大会…

三维重建的几何评价指标

1.三维重建的几何评价指标 1.1 Chamfer Distance Geometry quality (1) Chamfer Distance(CD) CD衡量两组点云之间的几何差异,距离越小越好。 CD是一种用于衡量两个点云之间相似度的常用几何评价指标。它计算一个点云中每个点到另一个点云的…

Qt5.15和Qt6.7配置Android开发环境

最近重新安装了Qt5.15.2和Qt6.7.2,使用Qt Creator14.0.1,配置Android开发环境时又碰到了一些问题,记录如下。 1、Qt6.7.2使用AndroidStudio的JDK 因为系统原来安装了AndroidStudio2024,系统自动检测了JDK位置,点击设置SDK,可以自动安装好相应的NDK。 打开Qt Creator14…

JavaEE——多线程的状态及线程安全问题

目录 一、线程的状态 1、NEW 2、 TERMINATED 3、RUNNABLE 4、TIMED_WAITING 5、 BLOCKED 6、WAITING 二、线程安全问题 1、线程不安全的原因 2、一个线程不安全的实例 3、加锁操作 4、产生线程不安全的原因 什么是内存可见性呢? 解决方案? 5、指令重排序…

【Linux学习】1-2 新建虚拟机ubuntu环境

1.双击打开VMware软件,点击“创建新的虚拟机”,在弹出的中选择“自定义(高级)” 2.点击下一步,自动识别ubuntu光盘映像文件,也可以点击“浏览”手动选择,点击下一步 3.设置名称及密码后&#xf…

web - RequestResponse

##Request&Response 1,Request和Response的概述 Request是请求对象,Response是响应对象。这两个对象在我们使用Servlet的时候有看到: 此时,我们就需要思考一个问题request和response这两个参数的作用是什么? request:获取请…

基于微信小程序的竞赛答题小程序开发笔记(一)

开发背景调研 中小学学科答题小程序,适合各中小学校方,老师或者家长。通过互动和参与式学习,小程序能够通过游戏化元素提升学习的积极性和参与度,从而提升学习效率,促进学生自主学习 功能规划 分类题库:…

专题八_链表_算法专题详细总结

目录 链表 1.常用技巧 1)画图!!! -> 直观 形象 便于我们理解 2)引入虚拟“头”节点 1.便于处理边界条件 2.方便我们对链表进行操作 3.不要吝啬空间,大胆定义变量 4.快慢双指针 1.判断链表是否…

redis学习(014 实战:黑马点评:优惠券秒杀——1人只可以下1单问题解决方案)

黑马程序员Redis入门到实战教程,深度透析redis底层原理redis分布式锁企业解决方案黑马点评实战项目 总时长 42:48:00 共175P 此文章包含第54p-第p55的内容 文章目录 一人一单问题分析第一种写法 查询后进行添加第二种写法 加悲观锁在用户上加悲观锁(提…

Vue 响应式监听 Watch 最佳实践

一. 前言 上一篇文章我们学习了 watch 的基础知识,了解了它的基本使用方法及注意事项,本篇文章我们继续了解在Vue 中 响应式监听 watch 的妙用。了解 watch 的基础使用请参考上一篇文章: 详解 Vue 中 Watch 的使用方法及注意事项https://bl…

53 语言模型(和之后用来训练语言模型的数据集)_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 文章目录 系列文章目录理论部分使用计数来建模N元语法总结 代码读取长序列数据随机采样顺序分区 小结练习 理论部分 在上一部分中,我们了解了如何将文本数据映射为词元,以及将这些词元可以视为一系列离散的观测,例如单词或字符…

(务必收藏)推荐市面上8款AI自动写文献综述的网站

在当前的学术研究和论文写作中,AI技术的应用已经变得越来越普遍。特别是在文献综述这一环节,AI工具能够显著提高效率并减少人工劳动。以下是市面上8款推荐的AI自动写文献综述的网站: 一、千笔-AIPassPaper 是一款备受好评的AI论文写作平台&…

java 框架组件

Java 框架是一系列预先编写好的、可复用的软件组件,它们旨在帮助开发者快速构建高质量的应用程序。Java 社区拥有众多优秀的框架,涵盖了从 Web 开发到大数据处理的各个领域。下面是一些流行的 Java 框架及其主要用途: Spring框架:…

基于丹摩智算部署SD3+ComfyUI文生图详解

目录 丹摩智算简介SD3ComfyUI文生图简介 SD3ComfyUI文生图部署步骤1.1、实例创建 操作步骤从HF-mirror下载SD3模型安装git安装ComfyUI 丹摩智算简介 丹摩智算官网:https://www.damodel.com/home 丹摩智算(DAMODEL)是一款专为AI应用打造的智…

网红挣钱太容易了

你看最近这个三只羊小Y哥,因为月饼质量问题、因为大闸蟹的问题,上了好多次热搜,掉粉了几百万。还是有很多人在赶着要买他们家的东西。 你是他的粉丝,他是你的屠夫。只要冠以“全网最低价”的名号,就会有无数的粉丝跑过…

应用层协议 --- HTTP

序言 在上一篇文章中,我们在应用层实现了一个非常简单的自定义协议,我们在我们报文的首部添加了报文的长度并且使用特定的符号分割。但是想做一个成熟,完善的协议是不简单的,今天我们就一起看看我们每天都会用到的 HTTP协议 。 UR…