背景

使用fphttpclient调用一个http接口,但接口返回的响应体是经gzip压缩过的。这本是一个很常用,也很基础的功能,不想却如历劫般充满磨难。

历劫

初劫

场景很常用,也很基础,本着能偷懒就偷懒的原则,向AI进行提问。

AI给出的答案是使用ZStream单元的Tdecompressionstream。但是,AI给的示例第二个传参的数据类型怎么和方法声明不一样?继续问AIAI就道歉,再给示例。

本以为几轮勾通下来问题就解决了,不曾想,恶梦才刚刚开始。AI每次都先道歉,然后信誓旦旦的说这次肯定可以,结果每次都不行。到后来,AI一会说是版本问题,一会说是数据问题……其实那些问题一开始就是排除掉的,但AI就是死鸭子嘴硬,真是气得令人口吐芬芳。结果。到最后,这货果然开始摆烂、甩锅,说什么它只是个模型,反正它尽力了,它不知道,问他不如你自己去网上查靠谱……浪费一上午时间,结果就来个让自己去网上查,那要你AI有个卵用!

再劫

午饭后,换个方式,先让AI去官方的gitlab查下有没有相关bug,结果果然有!然后顺藤摸瓜再查下解决方案,但是没有查到明确的解决方式,反倒是AI又开始一本正经胡说八道了,一不小心,半下午又没了!

罢了,一直被这货瞎忽悠,还不如自己静下心来深入了解下。一番了解加测试后,发现是Tdecompressionstream并不能识别gzip的格式。

复劫

既然问题出在gzip的格式上,那就先简单了解下gzip格式吧,懒得自己去搜,又问了AI,这次倒是说得有模有样,而且和测试的数据也都对得上,于是就追问了下如何解决。结果,又不知不觉被这货一本正经胡说八道忽悠瘸了,等发觉时已经深夜了。

劫中劫

官方的不能直接用,那就自己写个能用的。Tdecompressionstream是基于zlib的,而且有现成的底层功能可调用,那就仿一个支持gzip的,思路也很简单:把gzip的特征剥离后,剩下的就是原始的deflate,这个zlib肯定是支持的。

历经磨难,终于还是没写出能用的,看来一知半解就去搞还是行不通的,总不能像当初搞sm3sm4那样对着规范研究几天再说吧!

劫散

对啊!虽说当初确实研究了好几天规范,但最终的实现还是参考了现成的c代码,fpc是小众中的小众,但c却是软件世界的基石啊,写pascal的话AI会一本正经胡说八道,那写c应该就只是代码搬运了吧。

AI给了基于zlib的解压gzip的示例,自己对照着翻译成pascal,结果果然成功了!

仅是解压http的响应,够用了:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
uses
  ZBase, PasZLib;

function GZipDecompression(Input, Output: TStream): boolean;
var
  z:      z_stream;
  ret:    integer;
  inBuf, outBuf: TBytes;
  n:      uint64;
  offset: integer;
  xlen:   uint16;
begin
  Result := False;
  //数据太短
  if Input.Size < 18 then
    Exit;
  SetLength(inBuf, Input.Size);
  Input.Position := 0;
  Input.Read(inBuf[0], Input.Size);
  //检查GZip魔数
  if (inBuf[0] <> $1F) or (inBuf[1] <> $8B) then
    Exit;
  //不支持的压缩方法
  if inBuf[2] <> $08 then
    Exit;
  offset := 10;
  //额外字段
  if (inBuf[3] and $04) <> 0 then
  begin
    if offset + 2 > Input.Size then
      Exit;
    xlen := inBuf[offset] or (inBuf[offset + 1] shl 8);
    offset := offset + 2 + xlen;
  end;
  //文件名
  if (inBuf[3] and $08) <> 0 then
  begin
    while (offset < Input.Size) and (inBuf[offset] <> 0) do
      Inc(offset);
    if offset < Input.Size then  //跳过0
      Inc(offset);
  end;
  //注释
  if (inBuf[3] and $10) <> 0 then
  begin
    while (offset < Input.Size) and (inBuf[offset] <> 0) do
      Inc(offset);
    if offset < Input.Size then  //跳过0
      Inc(offset);
  end;
  //头部CRC
  if (inBuf[3] and $02) <> 0 then
  begin
    offset := offset + 2;
  end;
  //头部错误
  if offset >= Input.Size then
    Exit;
  //解压后大小
  Input.Position := Input.Size - 4;
  Input.Read(n, 4);
  SetLength(outBuf, n);

  FillChar(z, SizeOf(z), 0);
  //按原始deflate解压
  ret := inflateInit2(z, -MAX_WBITS);
  if ret <> Z_OK then
    Exit;

  z.avail_in := Input.Size - offset - 8;
  z.next_in := @inBuf[offset];
  z.avail_out := n;
  z.next_out := @outBuf[0];

  ret := inflate(z, Z_FINISH);
  if ret <> Z_STREAM_END then
  begin  //解压失败
    inflateEnd(z);
    Exit;
  end;
  n := z.total_out;
  inflateEnd(z);
  Output.Write(outBuf[0], n);
  Result := True;
end;

总结

  • AI摆烂的能力一点也不输一本正经胡说八道的能力

  • AI在小众领域、非主流领域内,最擅长的就是一本正经胡说八道,不亲自尝试很难分辨真假,所以,在小众领域、非主流领域不要相信AI

  • AI不是银弹,吹虚AI无所不能的,不是蠢就是坏