我正在使用 IndyTIdTCPClient呼叫 HTTPS,但服務器超時等待我的 HTTP 命令。在 Wireshark 中,我可以看到 TLS 握手已完成,但WriteLn()服務器沒有回應任何命令。
我知道使用TIdHTTP會更容易,但我還有另一個目的。下面是附上的代碼:
procedure TForm1.Button1Click(Sender: TObject);
var
sid, result: string;
lParam: TStringList;
TCPC: TIdTCPClient;
OnSSL: TIdSSLIOHandlerSocketOpenSSL;
begin
begin
lParam := TStringList.Create;
TCPC := TIdTCPClient.Create();
OnSSL := TIdSSLIOHandlerSocketOpenSSL.Create();
try
OnSSL.SSLOptions.Method := sslvSSLv23;
OnSSL.SSLOptions.SSLVersions := [sslvSSLv2, sslvSSLv3, sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
OnSSL.UseNagle := True;
OnSSL.PassThrough := False;
TCPC.Host := 'xxxxxxx.edu';
TCPC.Port := 443;
TCPC.ConnectTimeout := 100000;
TCPC.ReadTimeout := 500000;
TCPC.IOHandler := OnSSL;
TCPC.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
TCPC.Connect;
try
logging(TCPC.IOHandler.ReadLn);
if TCPC.IOHandler.Connected then
begin
TCPC.IOHandler.WriteLn('GET / HTTP/1.1', IndyTextEncoding_UTF8);
TCPC.IOHandler.WriteLn('Host: xxxxxxx.edu', IndyTextEncoding_UTF8);
TCPC.IOHandler.WriteLn('Connection: keep-alive', IndyTextEncoding_UTF8);
TCPC.IOHandler.WriteLn('Pragma: no-cache', IndyTextEncoding_UTF8);
TCPC.IOHandler.WriteLn('Cache-Control: no-cache', IndyTextEncoding_UTF8);
TCPC.IOHandler.Write('sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"', IndyTextEncoding_UTF8);
TCPC.IOHandler.WriteLn('sec-ch-ua-mobile: ?0', IndyTextEncoding_UTF8);
TCPC.IOHandler.WriteLn('sec-ch-ua-platform: "Windows"', IndyTextEncoding_UTF8);
TCPC.IOHandler.WriteLn('Upgrade-Insecure-Requests: 1', IndyTextEncoding_UTF8);
TCPC.IOHandler.WriteLn('User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36', IndyTextEncoding_UTF8);
TCPC.IOHandler.Writeln('Accept: text/html,application/xhtml xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', IndyTextEncoding_UTF8);
TCPC.IOHandler.WriteLn('Sec-Fetch-Site: none', IndyTextEncoding_UTF8);
TCPC.IOHandler.WriteLn('Sec-Fetch-Mode: navigate', IndyTextEncoding_UTF8);
TCPC.IOHandler.WriteLn('Sec-Fetch-User: ?1', IndyTextEncoding_UTF8);
TCPC.IOHandler.WriteLn('Sec-Fetch-Dest: document', IndyTextEncoding_UTF8);
TCPC.IOHandler.WriteLn('Accept-Encoding: gzip, deflate, br', IndyTextEncoding_UTF8);
TCPC.IOHandler.WriteLn('Accept-Language: en-US,en;q=0.9', IndyTextEncoding_UTF8);
end;
result := TCPC.IOHandler.AllData(IndyTextEncoding_UTF8);
logging(result);
finally
TCPC.Disconnect;
end;
finally
lParam.Free;
OnSSL.Free;
TCPC.Free;
end;
end;
end;
end.
uj5u.com熱心網友回復:
你的代碼有很多問題:
浪費記憶體來創建一個未使用的
TStringList物件。啟用不再安全的過時 SSL v2 和 v3。
IOHandler.ReadLn()在發送 HTTP 請求之前呼叫。HTTP/S 協議不涉及初始問候語。IOHandler.Write()對sec-ch-ua標題進行錯誤呼叫。應該IOHandler.WriteLn()改用它。請求允許壓縮 HTTP 正文,當您實際上并未準備解壓縮它時。
沒有正確終止 HTTP 請求。您需要在標題后發送一個空行。這是服務器超時錯誤的原因。
通過
IOHandler.AllData()讀取HTTP回應,這將讀到服務器斷開連接,但你也啟用HTTP保持連接,這要求服務器不會斷開連接。即使沒有 HTTP keepalive(因為您在讀取 ??HTTP 回應后破壞了連接,所以使用 keepalive 沒有任何意義),AllData()仍然是讀取 HTTP 回應的錯誤方式。(我過去已經發布了許多關于讀取 HTTP 回應的正確方法的答案)。冗余使用
IOHandler.DefStringEncoding屬性和AByteEncoding引數。使用前者時不需要使用后者,反之亦然。
試試這個:
procedure TForm1.Button1Click(Sender: TObject);
var
Result: string;
TCPC: TIdTCPClient;
OnSSL: TIdSSLIOHandlerSocketOpenSSL;
begin
TCPC := TIdTCPClient.Create();
try
OnSSL := TIdSSLIOHandlerSocketOpenSSL.Create(TCPC);
OnSSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
OnSSL.PassThrough := False;
OnSSL.DefStringEncoding := IndyTextEncoding_UTF8;
TCPC.Host := 'xxxxxxx.edu';
TCPC.Port := 443;
TCPC.ConnectTimeout := 100000;
TCPC.ReadTimeout := 500000;
TCPC.IOHandler := OnSSL;
TCPC.Connect;
try
TCPC.IOHandler.WriteLn('GET / HTTP/1.1');
TCPC.IOHandler.WriteLn('Host: xxxxxxx.edu');
TCPC.IOHandler.WriteLn('Connection: close');
TCPC.IOHandler.WriteLn('Pragma: no-cache');
TCPC.IOHandler.WriteLn('Cache-Control: no-cache');
TCPC.IOHandler.WriteLn('sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"');
TCPC.IOHandler.WriteLn('sec-ch-ua-mobile: ?0');
TCPC.IOHandler.WriteLn('sec-ch-ua-platform: "Windows"');
TCPC.IOHandler.WriteLn('Upgrade-Insecure-Requests: 1');
TCPC.IOHandler.WriteLn('User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36');
TCPC.IOHandler.Writeln('Accept: text/html,application/xhtml xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9');
TCPC.IOHandler.WriteLn('Sec-Fetch-Site: none');
TCPC.IOHandler.WriteLn('Sec-Fetch-Mode: navigate');
TCPC.IOHandler.WriteLn('Sec-Fetch-User: ?1');
TCPC.IOHandler.WriteLn('Sec-Fetch-Dest: document');
//TCPC.IOHandler.WriteLn('Accept-Encoding: gzip, deflate, br');
TCPC.IOHandler.WriteLn('Accept-Language: en-US,en;q=0.9');
TCPC.IOHandler.WriteLn();
// I'm leaving this here only because I don't have the time right now
// to rewrite it properly. Go read my previous answers on this topic...
Result := TCPC.IOHandler.AllData();
logging(Result);
finally
TCPC.Disconnect;
end;
finally
TCPC.Free;
end;
end;
話雖這么說,你是正確的,使用TIdHTTP會多簡單,例如:
procedure TForm1.Button1Click(Sender: TObject);
var
Result: string;
HTTP: TIdHTTP;
OnSSL: TIdSSLIOHandlerSocketOpenSSL;
begin
HTTP := TIdHTTP.Create();
try
OnSSL := TIdSSLIOHandlerSocketOpenSSL.Create(TCPC);
OnSSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
HTTP.ConnectTimeout := 100000;
HTTP.ReadTimeout := 500000;
HTTP.IOHandler := OnSSL;
HTTP.Request.Connection := 'close';
HTTP.Request.Pragma := 'no-cache';
HTTP.Request.CacheControl := 'no-cache';
HTTP.Request.UserAgent := 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36';
HTTP.Request.Accept := 'text/html,application/xhtml xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9';
HTTP.Request.AcceptLanguage := 'en-US,en;q=0.9';
HTTP.Request.CustomHeaders.Values['sec-ch-ua'] :='" Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"';
HTTP.Request.CustomHeaders.Values['sec-ch-ua-mobile'] := '?0';
HTTP.Request.CustomHeaders.Values['sec-ch-ua-platform'] := '"Windows"';
HTTP.Request.CustomHeaders.Values['Upgrade-Insecure-Requests'] := '1';
HTTP.Request.CustomHeaders.Values['Sec-Fetch-Site'] := 'none';
HTTP.Request.CustomHeaders.Values['Sec-Fetch-Mode'] := 'navigate';
HTTP.Request.CustomHeaders.Values['Sec-Fetch-User'] := '?1';
HTTP.Request.CustomHeaders.Values['Sec-Fetch-Dest'] := 'document';
// if you want to support compressed responses, assign a
// TIdZLibCompressorBase-derived component to the
// TIdHTTP.Compressor property...
Result := HTTP.Get('https://xxxxxxx.edu/');
logging(Result);
finally
HTTP.Free;
end;
end;
TIdTCPClient直接使用而不是使用的理由很少TIdHTTP。
uj5u.com熱心網友回復:
首先,您需要發送一個空行來標記 HTTP 元資料/標頭的結尾。
TCPC.IOHandler.Writeln('',IndyTextEncoding_UTF8);
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/375541.html
