HttpRequest r("http://dev.alt.cloudappsportal.com/_api/web/lists"); r.KeepAlive().Execute();
case TRAILER: if(ReadingHeader()) break; header.ParseAdd(data); Finish(); break; case FINISHED:
case TRAILER: if(TcpSocket::Peek() == '\r') // if the next line is empty (then no trailer) { TcpSocket::Get(); c2 = TcpSocket::Get(); if(c2 != '\n') HttpError("missing ending CRLF"); Finish(); break; } if(ReadingHeader()) break; header.ParseAdd(data); Finish(); break; case FINISHED:
case TRAILER: if(!ReadingTrailer()) header.ParseAdd(data); Finish(); break;
case TRAILER: if(ReadingTrailer()) break; header.ParseAdd(data); Finish(); break;
the trailer part of http response is optional.
an empty line("\r\n") after the last chunk, mean that there is no trailer at all.
in this case, we should go to the next phase : Finish().
bool HttpRequest::ReadingTrailer() { for(;;) { int c = TcpSocket::Get(); if(c < 0) return !IsEof();
bool HttpRequest::ReadingHeader() { for(;;) { int c = TcpSocket::Get(); if(c < 0) return !IsEof(); else data.Cat(c); if(data.GetCount() == 2 && data[0] == '\r' && data[1] == '\n') // header is empty return false; if(data.GetCount() >= 3) { const char *h = data.Last(); if(h[0] == '\n' && h[-1] == '\r' && h[-2] == '\n') // empty ending line after non-empty header return false; } if(data.GetCount() > max_header_size) { HttpError("HTTP header exceeded " + AsString(max_header_size)); return true; } } }
if(data.GetCount() > 3) { const char *h = data.Last(); if(h[0] == '\n' && (h[-1] == '\r' && h[-2] == '\n' || h[-1] == '\n')) return false; }
bool HttpRequest::ReadingTrailer() { for(;;) { int c = TcpSocket::Get(); if(c < 0) return !IsEof(); else data.Cat(c); if(data.GetCount() == 2) { if(data[0] == '\r' && data[1] == '\n') return false; } if(data.GetCount() > 3) { const char *h = data.Last(); if(h[0] == '\n' && (h[-1] == '\r' && h[-2] == '\n' || h[-1] == '\n')) return false; } } }
#include <CtrlLib/CtrlLib.h> using namespace Upp; // base source : http://www.tcpipguide.com/free/t_HTTPDataLengthIssuesChunkedTransfersandMessageTrai-3.htm String chunked_with_trailer = "HTTP/1.1 200 OK\r\nDate: Mon, 22 Mar 2004 11:15:03 GMT\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\nTrailer: Expires\r\n\r\n29\r\n<html><body><p>The file you requested is \r\n5\r\n3,400\r\n23\r\nbytes long and was last modified:\r\n\r\n1d\r\nSat, 20 Mar 2004 21:12:00 GMT\r\n13\r\n.</p></body></html>\r\n0\r\nExpires: Sat, 27 Mar 2004 21:12:00 GMT\r\n\r\n"; // base source : https://en.wikipedia.org/wiki/Chunked_transfer_encoding?oldid=430331176 String chunked_without_trailer = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n26\r\nThis is the data in the first chunk\r\n6\r\n1C\r\nand this is the second one\r\n\r\n3\r\ncon\r\n8\r\nsequence\r\n0\r\n\r\n"; static void Server(String r) { TcpSocket server; if(server.Listen(4000, 10)) { TcpSocket socket; LOG("Waiting..."); bool b = socket.Accept(server); if(b) { LOG("Connection accepted"); HttpHeader http; http.Read(socket); socket.Put(r); socket.Close(); } } } GUI_APP_MAIN { StdLogSetup(LOG_COUT|LOG_FILE); Thread a; LOG("chunked_without_trailer"); LOG("****************"); LOG(chunked_without_trailer); LOG("---------------"); a.Run(callback1(Server, chunked_without_trailer)); HttpRequest r1("localhost:4000");/*r1.Trace();*/ LOG(r1.GET().Execute()); a.Wait(); LOG("chunked_with_trailer"); LOG("****************"); LOG(chunked_with_trailer); LOG("---------------"); a.Run(callback1(Server, chunked_with_trailer)); HttpRequest r2("localhost:4000");/*r2.Trace();*/ LOG(r2.GET().Execute()); a.Wait(); LOG("=============== OK"); }
Mirek, you are right,
when ReadingTrailer return true, we do not call Finish().
but the actual implementation of ReadingTrailer fail when a Trailer is present.
it miss the part :
if(data.GetCount() > 3) { const char *h = data.Last(); if(h[0] == '\n' && (h[-1] == '\r' && h[-2] == '\n' || h[-1] == '\n')) return false; }
(as ReadingHeader)
bool HttpRequest::ReadingTrailer() { for(;;) { int c = TcpSocket::Get(); if(c < 0) return !IsEof(); else data.Cat(c); if(data.GetCount() == 2) { if(data[0] == '\r' && data[1] == '\n') return false; } if(data.GetCount() > 3) { const char *h = data.Last(); if(h[0] == '\n' && (h[-1] == '\r' && h[-2] == '\n' || h[-1] == '\n')) return false; } } }
if(!keep_alive)Close();
HTTP/1.0 200 OK Date: Fri, 31 Dec 1999 23:59:59 GMT Server: Apache/0.8.4 Content-Type: text/html Content-Length: 0 Expires: Sat, 01 Jan 2000 00:59:59 GMT Last-modified: Fri, 09 Aug 1996 14:21:40 GMT
if(count > 0) n = (int)min((int64)n, count);
if(count == 0) return false; n = (int)min((int64)n, count);
bool HttpRequest::ReadingBody() { LLOG("HTTP reading body " << count); String s = TcpSocket::Get((int)min((int64)chunk, count)); if(s.GetCount() == 0) return !IsEof() && count;