IdCookieManager 剪切了包含 quotechar 的服务器 cookie 的一部分
IdCookieManager cut part of server cookie which contains quotechar
IdCookieManager 有问题。当值包含 "
的服务器 returns cookie 时,它会将第一次出现的 "
识别为值的结尾。您可以使用下一个代码轻松重现它:
procedure TSomeObject.Test;
var
HTTP: TIdHTTP;
Cookie: TIdCookieManager;
i: Integer;
begin
HTTP := TIdHTTP.Create(nil);
Cookie := TIdCookieManager.Create(HTTP);
HTTP.CookieManager := Cookie;
HTTP.HandleRedirects := True;
HTTP.Get('http://httpbin.org/cookies/set?test_cookie1=' +
TIdURI.ParamsEncode('{"key": 123}') + '&test_cookie2=&test_cookie3=value');
for i := 0 to Cookie.CookieCollection.Count - 1 do
Form1.Memo1.Lines.Add(Cookie.CookieCollection[i].CookieName + ' = ' +
Cookie.CookieCollection[i].Value);
HTTP.Free;
end;
深入研究 Indy 资源,我发现问题出在 TIdCookie.ParseServerCookie()
中。它使用 IdGlobal.Fetch()
来提取引号之间的值并且...它会做它所做的事情。
你能推荐我如何让它解析整个值吗?
没有完美的解决方案(至少我还没有找到),所以我添加了 OnHeadersAvailable
事件侦听器到 TIdHTTP
重写TIdCookie.ParseServerCookie()
执行后的 cookie 值,并通过向 TIdCookieManager
.
添加同名的新 cookie 来替换之前解析的 cookie
代码 (请勿使用此处理程序,下面还有一个):
procedure TSomeObject.FixCookies(Sender: TObject; AHeaders: TIdHeaderList;
var VContinue: Boolean);
const
CookieDelimiter = ';';
QuoteChar = '"';
SpaceChar = ' ';
var
RawHeader, RawCookie, RawCookieValue: string;
i, CookieDelimiterPos, CookieNamePos, CookieValuePos: Integer;
Cookie: TIdCookie;
begin
for i := 0 to AHeaders.Count - 1 do
begin
RawHeader := AHeaders[i];
if Pos('Set-Cookie', RawHeader) = 1 then // starts with "Set-Cookie"
begin
for CookieNamePos := Length('Set-Cookie') + 2 to Length(RawHeader) do
if RawHeader[CookieNamePos] <> SpaceChar then
Break;
RawCookie := Copy(RawHeader, CookieNamePos,
Length(RawHeader) - CookieNamePos + 1);
CookieDelimiterPos := Pos(CookieDelimiter, RawCookie);
CookieValuePos := Pos('=', RawCookie);
if (CookieDelimiterPos > 0) and (CookieValuePos > 0) then
begin
RawCookieValue := Copy(RawCookie, CookieValuePos + 1,
CookieDelimiterPos - CookieValuePos - 1);
if (Length(RawCookieValue) > 0) and (RawCookieValue[1] = QuoteChar) and
(RawCookieValue[Length(RawCookieValue)] = QuoteChar) then
RawCookieValue := Copy(RawCookieValue, 2, Length(RawCookieValue) - 2);
with (Sender as TIdHTTP) do
begin
Cookie := TIdCookie.Create(nil);
if Cookie.ParseServerCookie(RawCookie, URL) then
begin
Cookie.Value := RawCookieValue;
CookieManager.CookieCollection.AddCookie(Cookie, URL);
end
else
Cookie.Free;
end;
end;
end;
end;
VContinue := True;
end;
用法:
HTTP.OnHeadersAvailable := FixCookies; // Set it after object initialization
P.S. 我已经很久没有使用 delphi 了,所以我确信代码并不完美,还有很多改进可以做到(欢迎评论),但是它有效。
我不知道它是怎么发生的,但是使用这个事件处理程序以某种方式阻止了 TIdCookieManager
和 Cookie.CookieCollection.Count
returns 0
,而收集中有成功的 cookie在进一步的请求中添加..
我没有心情花更多时间挖掘 Indy 源代码,所以我更改了处理程序以修改现有 cookie,而不是替换它们。它不会制动任何东西 (可能):
procedure TSomeObject.FixCookies(Sender: TObject; AHeaders: TIdHeaderList;
var VContinue: Boolean);
const
CookieDelimiter = ';';
QuoteChar = '"';
SpaceChar = ' ';
var
RawHeader, RawCookie, RawCookieValue, RawCookieName: string;
i, CookieDelimiterPos, CookieNamePos, CookieValuePos, CookieIndex: Integer;
begin
for i := 0 to AHeaders.Count - 1 do
begin
RawHeader := AHeaders[i];
if Pos('Set-Cookie', RawHeader) = 1 then // starts with "Set-Cookie"
begin
for CookieNamePos := Length('Set-Cookie') + 2 to Length(RawHeader) do
if RawHeader[CookieNamePos] <> SpaceChar then
Break;
RawCookie := Copy(RawHeader, CookieNamePos,
Length(RawHeader) - CookieNamePos + 1);
CookieDelimiterPos := Pos(CookieDelimiter, RawCookie);
CookieValuePos := Pos('=', RawCookie);
if (CookieDelimiterPos > 0) and (CookieValuePos > 0) then
begin
RawCookieName := Copy(RawCookie, 1, CookieValuePos - 1);
RawCookieValue := Copy(RawCookie, CookieValuePos + 1,
CookieDelimiterPos - CookieValuePos - 1);
if (Length(RawCookieValue) > 0) and (RawCookieValue[1] = QuoteChar) and
(RawCookieValue[Length(RawCookieValue)] = QuoteChar) then
RawCookieValue := Copy(RawCookieValue, 2, Length(RawCookieValue) - 2);
with (Sender as TIdHTTP) do
begin
CookieIndex := CookieManager.CookieCollection.GetCookieIndex
(RawCookieName);
if CookieIndex >= 0 then
CookieManager.CookieCollection[CookieIndex].Value := RawCookieValue
end;
end;
end;
end;
VContinue := True;
end;
P.S. 有 Indy 开发者吗?我只是有点困惑第一个处理程序如何让 CookieCollection 为 return 零。
IdCookieManager 有问题。当值包含 "
的服务器 returns cookie 时,它会将第一次出现的 "
识别为值的结尾。您可以使用下一个代码轻松重现它:
procedure TSomeObject.Test;
var
HTTP: TIdHTTP;
Cookie: TIdCookieManager;
i: Integer;
begin
HTTP := TIdHTTP.Create(nil);
Cookie := TIdCookieManager.Create(HTTP);
HTTP.CookieManager := Cookie;
HTTP.HandleRedirects := True;
HTTP.Get('http://httpbin.org/cookies/set?test_cookie1=' +
TIdURI.ParamsEncode('{"key": 123}') + '&test_cookie2=&test_cookie3=value');
for i := 0 to Cookie.CookieCollection.Count - 1 do
Form1.Memo1.Lines.Add(Cookie.CookieCollection[i].CookieName + ' = ' +
Cookie.CookieCollection[i].Value);
HTTP.Free;
end;
深入研究 Indy 资源,我发现问题出在 TIdCookie.ParseServerCookie()
中。它使用 IdGlobal.Fetch()
来提取引号之间的值并且...它会做它所做的事情。
你能推荐我如何让它解析整个值吗?
没有完美的解决方案(至少我还没有找到),所以我添加了 OnHeadersAvailable
事件侦听器到 TIdHTTP
重写TIdCookie.ParseServerCookie()
执行后的 cookie 值,并通过向 TIdCookieManager
.
代码 (请勿使用此处理程序,下面还有一个):
procedure TSomeObject.FixCookies(Sender: TObject; AHeaders: TIdHeaderList;
var VContinue: Boolean);
const
CookieDelimiter = ';';
QuoteChar = '"';
SpaceChar = ' ';
var
RawHeader, RawCookie, RawCookieValue: string;
i, CookieDelimiterPos, CookieNamePos, CookieValuePos: Integer;
Cookie: TIdCookie;
begin
for i := 0 to AHeaders.Count - 1 do
begin
RawHeader := AHeaders[i];
if Pos('Set-Cookie', RawHeader) = 1 then // starts with "Set-Cookie"
begin
for CookieNamePos := Length('Set-Cookie') + 2 to Length(RawHeader) do
if RawHeader[CookieNamePos] <> SpaceChar then
Break;
RawCookie := Copy(RawHeader, CookieNamePos,
Length(RawHeader) - CookieNamePos + 1);
CookieDelimiterPos := Pos(CookieDelimiter, RawCookie);
CookieValuePos := Pos('=', RawCookie);
if (CookieDelimiterPos > 0) and (CookieValuePos > 0) then
begin
RawCookieValue := Copy(RawCookie, CookieValuePos + 1,
CookieDelimiterPos - CookieValuePos - 1);
if (Length(RawCookieValue) > 0) and (RawCookieValue[1] = QuoteChar) and
(RawCookieValue[Length(RawCookieValue)] = QuoteChar) then
RawCookieValue := Copy(RawCookieValue, 2, Length(RawCookieValue) - 2);
with (Sender as TIdHTTP) do
begin
Cookie := TIdCookie.Create(nil);
if Cookie.ParseServerCookie(RawCookie, URL) then
begin
Cookie.Value := RawCookieValue;
CookieManager.CookieCollection.AddCookie(Cookie, URL);
end
else
Cookie.Free;
end;
end;
end;
end;
VContinue := True;
end;
用法:
HTTP.OnHeadersAvailable := FixCookies; // Set it after object initialization
P.S. 我已经很久没有使用 delphi 了,所以我确信代码并不完美,还有很多改进可以做到(欢迎评论),但是它有效。
我不知道它是怎么发生的,但是使用这个事件处理程序以某种方式阻止了 TIdCookieManager
和 Cookie.CookieCollection.Count
returns 0
,而收集中有成功的 cookie在进一步的请求中添加..
我没有心情花更多时间挖掘 Indy 源代码,所以我更改了处理程序以修改现有 cookie,而不是替换它们。它不会制动任何东西 (可能):
procedure TSomeObject.FixCookies(Sender: TObject; AHeaders: TIdHeaderList;
var VContinue: Boolean);
const
CookieDelimiter = ';';
QuoteChar = '"';
SpaceChar = ' ';
var
RawHeader, RawCookie, RawCookieValue, RawCookieName: string;
i, CookieDelimiterPos, CookieNamePos, CookieValuePos, CookieIndex: Integer;
begin
for i := 0 to AHeaders.Count - 1 do
begin
RawHeader := AHeaders[i];
if Pos('Set-Cookie', RawHeader) = 1 then // starts with "Set-Cookie"
begin
for CookieNamePos := Length('Set-Cookie') + 2 to Length(RawHeader) do
if RawHeader[CookieNamePos] <> SpaceChar then
Break;
RawCookie := Copy(RawHeader, CookieNamePos,
Length(RawHeader) - CookieNamePos + 1);
CookieDelimiterPos := Pos(CookieDelimiter, RawCookie);
CookieValuePos := Pos('=', RawCookie);
if (CookieDelimiterPos > 0) and (CookieValuePos > 0) then
begin
RawCookieName := Copy(RawCookie, 1, CookieValuePos - 1);
RawCookieValue := Copy(RawCookie, CookieValuePos + 1,
CookieDelimiterPos - CookieValuePos - 1);
if (Length(RawCookieValue) > 0) and (RawCookieValue[1] = QuoteChar) and
(RawCookieValue[Length(RawCookieValue)] = QuoteChar) then
RawCookieValue := Copy(RawCookieValue, 2, Length(RawCookieValue) - 2);
with (Sender as TIdHTTP) do
begin
CookieIndex := CookieManager.CookieCollection.GetCookieIndex
(RawCookieName);
if CookieIndex >= 0 then
CookieManager.CookieCollection[CookieIndex].Value := RawCookieValue
end;
end;
end;
end;
VContinue := True;
end;
P.S. 有 Indy 开发者吗?我只是有点困惑第一个处理程序如何让 CookieCollection 为 return 零。