123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- #pragma once
- #include "httpfsm.h"
- #include "httpheader.h"
- #include <library/cpp/mime/types/mime.h>
- #include <util/system/yassert.h>
- #include <library/cpp/http/misc/httpcodes.h>
- template <size_t headermax = 100 << 10, size_t bodymax = 1 << 20>
- struct TFakeCheck {
- bool Check(THttpHeader* /*header*/) {
- return false;
- }
- void CheckDocPart(void* /*buf*/, size_t /*len*/, THttpHeader* /*header*/) {
- } //for every part of DocumentBody will be called
- void CheckEndDoc(THttpHeader* /*header*/) {
- }
- size_t GetMaxHeaderSize() {
- return headermax;
- }
- size_t GetMaxBodySize(THttpHeader*) {
- return bodymax;
- }
- };
- class THttpParserBase {
- public:
- enum States {
- hp_error,
- hp_eof,
- hp_in_header,
- hp_read_alive,
- hp_read_closed,
- hp_begin_chunk_header,
- hp_chunk_header,
- hp_read_chunk
- };
- States GetState() {
- return State;
- }
- void setAssumeConnectionClosed(int value) {
- AssumeConnectionClosed = value;
- }
- THttpHeader* GetHttpHeader() const {
- return Header;
- }
- protected:
- int CheckHeaders() {
- if (Header->http_status < HTTP_OK || Header->http_status == HTTP_NO_CONTENT || Header->http_status == HTTP_NOT_MODIFIED) {
- Header->content_length = 0;
- Header->transfer_chunked = 0;
- }
- if (Header->transfer_chunked < -1) {
- Header->error = HTTP_BAD_ENCODING;
- return 1;
- } else if (Header->transfer_chunked == -1) {
- Header->transfer_chunked = 0;
- }
- if (!Header->transfer_chunked && Header->content_length < -1) {
- Header->error = HTTP_BAD_CONTENT_LENGTH;
- return 1;
- }
- if (Header->http_status == HTTP_OK) {
- if (Header->compression_method != HTTP_COMPRESSION_UNSET &&
- Header->compression_method != HTTP_COMPRESSION_IDENTITY &&
- Header->compression_method != HTTP_COMPRESSION_GZIP &&
- Header->compression_method != HTTP_COMPRESSION_DEFLATE)
- {
- Header->error = HTTP_BAD_CONTENT_ENCODING;
- return 1;
- }
- }
- if (Header->connection_closed == -1)
- Header->connection_closed = (Header->http_minor == 0 ||
- AssumeConnectionClosed);
- if (!Header->transfer_chunked && !Header->connection_closed && Header->content_length < 0 && !HeadRequest) {
- Header->error = HTTP_LENGTH_UNKNOWN;
- return 1;
- }
- if (Header->http_time < 0)
- Header->http_time = 0;
- if (Header->mime_type < 0)
- Header->mime_type = MIME_UNKNOWN;
- return 0;
- }
- THttpHeaderParser HeaderParser;
- THttpChunkParser ChunkParser;
- States State;
- long ChunkSize;
- THttpHeader* Header;
- int AssumeConnectionClosed;
- bool HeadRequest;
- };
- template <int isReader, typename TCheck = TFakeCheck<>>
- class THttpParserGeneric: public THttpParserBase, public TCheck {
- protected:
- long ParseGeneric(void*& buf, long& size) {
- if (!size) {
- switch (State) {
- case hp_error:
- case hp_eof:
- break;
- case hp_read_closed:
- State = hp_eof;
- break;
- case hp_in_header:
- Header->error = HTTP_HEADER_EOF;
- State = hp_error;
- break;
- case hp_read_alive:
- case hp_read_chunk:
- if (HeadRequest)
- State = hp_eof;
- else {
- Header->error = HTTP_MESSAGE_EOF;
- State = hp_error;
- }
- break;
- case hp_begin_chunk_header:
- case hp_chunk_header:
- if (HeadRequest)
- State = hp_eof;
- else {
- Header->error = HTTP_CHUNK_EOF;
- State = hp_error;
- }
- break;
- }
- return 0;
- }
- while (size) {
- int ret;
- switch (State) {
- case hp_error:
- return 0;
- case hp_eof:
- return 0;
- case hp_in_header:
- if ((ret = HeaderParser.Execute(buf, size)) < 0) {
- Header->error = HTTP_BAD_HEADER_STRING;
- State = hp_error;
- return 0;
- } else if (ret == 2) {
- Header->header_size += i32(HeaderParser.lastchar - (char*)buf + 1);
- size -= long(HeaderParser.lastchar - (char*)buf + 1);
- buf = HeaderParser.lastchar + 1;
- State = CheckHeaders() ? hp_error
- : Header->transfer_chunked ? hp_begin_chunk_header
- : Header->content_length == 0 ? hp_eof
- : Header->content_length > 0 ? hp_read_alive
- : hp_read_closed;
- if (State == hp_begin_chunk_header) {
- // unget \n for chunk reader
- buf = (char*)buf - 1;
- size++;
- }
- if (isReader)
- return size;
- } else {
- Header->header_size += size;
- size = 0;
- }
- break;
- case hp_read_alive:
- Header->entity_size += size;
- if (Header->entity_size >= Header->content_length) {
- State = hp_eof;
- }
- TCheck::CheckDocPart(buf, size, Header);
- if (isReader)
- return size;
- size = 0;
- break;
- case hp_read_closed:
- Header->entity_size += size;
- TCheck::CheckDocPart(buf, size, Header);
- if (isReader)
- return size;
- size = 0;
- break;
- case hp_begin_chunk_header:
- ChunkParser.Init();
- State = hp_chunk_header;
- [[fallthrough]];
- case hp_chunk_header:
- if ((ret = ChunkParser.Execute(buf, size)) < 0) {
- Header->error = i16(ret == -2 ? HTTP_CHUNK_TOO_LARGE : HTTP_BAD_CHUNK);
- State = hp_error;
- return 0;
- } else if (ret == 2) {
- Header->entity_size += i32(ChunkParser.lastchar - (char*)buf + 1);
- size -= long(ChunkParser.lastchar - (char*)buf + 1);
- buf = ChunkParser.lastchar + 1;
- ChunkSize = ChunkParser.chunk_length;
- Y_ASSERT(ChunkSize >= 0);
- State = ChunkSize ? hp_read_chunk : hp_eof;
- } else {
- Header->entity_size += size;
- size = 0;
- }
- break;
- case hp_read_chunk:
- if (size >= ChunkSize) {
- Header->entity_size += ChunkSize;
- State = hp_begin_chunk_header;
- TCheck::CheckDocPart(buf, ChunkSize, Header);
- if (isReader)
- return ChunkSize;
- size -= ChunkSize;
- buf = (char*)buf + ChunkSize;
- } else {
- Header->entity_size += size;
- ChunkSize -= size;
- TCheck::CheckDocPart(buf, size, Header);
- if (isReader)
- return size;
- size = 0;
- }
- break;
- }
- }
- return size;
- }
- };
- template <class TCheck = TFakeCheck<>>
- class THttpParser: public THttpParserGeneric<0, TCheck> {
- typedef THttpParserGeneric<0, TCheck> TBaseT; //sorry avoiding gcc 3.4.6 BUG!
- public:
- void Init(THttpHeader* H, bool head_request = false) {
- TBaseT::Header = H;
- TBaseT::HeaderParser.Init(TBaseT::Header);
- TBaseT::State = TBaseT::hp_in_header;
- TBaseT::AssumeConnectionClosed = 0;
- TBaseT::HeadRequest = head_request;
- }
- void Parse(void* buf, long size) {
- TBaseT::ParseGeneric(buf, size);
- }
- };
- class TMemoReader {
- public:
- int Init(void* buf, long bufsize) {
- Buf = buf;
- Bufsize = bufsize;
- return 0;
- }
- long Read(void*& buf) {
- Y_ASSERT(Bufsize >= 0);
- if (!Bufsize) {
- Bufsize = -1;
- return 0;
- }
- buf = Buf;
- long ret = Bufsize;
- Bufsize = 0;
- return ret;
- }
- protected:
- long Bufsize;
- void* Buf;
- };
- template <class Reader>
- class THttpReader: public THttpParserGeneric<1>, public Reader {
- typedef THttpParserGeneric<1> TBaseT;
- public:
- using TBaseT::AssumeConnectionClosed;
- using TBaseT::Header;
- using TBaseT::ParseGeneric;
- using TBaseT::State;
- int Init(THttpHeader* H, int parsHeader, int assumeConnectionClosed = 0, bool headRequest = false) {
- Header = H;
- Eoferr = 1;
- Size = 0;
- AssumeConnectionClosed = assumeConnectionClosed;
- HeadRequest = headRequest;
- return parsHeader ? ParseHeader() : SkipHeader();
- }
- long Read(void*& buf) {
- long Chunk;
- do {
- if (!Size) {
- if (Eoferr != 1)
- return Eoferr;
- else if ((Size = (long)Reader::Read(Ptr)) < 0) {
- Header->error = HTTP_CONNECTION_LOST;
- return Eoferr = -1;
- }
- }
- Chunk = ParseGeneric(Ptr, Size);
- buf = Ptr;
- Ptr = (char*)Ptr + Chunk;
- Size -= Chunk;
- if (State == hp_eof) {
- Size = 0;
- Eoferr = 0;
- } else if (State == hp_error)
- return Eoferr = -1;
- } while (!Chunk);
- return Chunk;
- }
- protected:
- int ParseHeader() {
- HeaderParser.Init(Header);
- State = hp_in_header;
- while (State == hp_in_header) {
- if ((Size = (long)Reader::Read(Ptr)) < 0)
- return Eoferr = -1;
- ParseGeneric(Ptr, Size);
- }
- if (State == hp_error)
- return Eoferr = -1;
- if (State == hp_eof)
- Eoferr = 0;
- return 0;
- }
- int SkipHeader() {
- long hdrsize = Header->header_size;
- while (hdrsize) {
- if ((Size = (long)Reader::Read(Ptr)) <= 0)
- return Eoferr = -1;
- if (Size >= hdrsize) {
- Size -= hdrsize;
- Ptr = (char*)Ptr + hdrsize;
- break;
- }
- hdrsize -= Size;
- }
- State = Header->transfer_chunked ? hp_begin_chunk_header
- : Header->content_length == 0 ? hp_eof
- : Header->content_length > 0 ? hp_read_alive
- : hp_read_closed;
- Header->entity_size = 0;
- if (State == hp_eof)
- Eoferr = 0;
- else if (State == hp_begin_chunk_header) {
- // unget \n for chunk reader
- Ptr = (char*)Ptr - 1;
- ++Size;
- }
- return 0;
- }
- void* Ptr;
- long Size;
- int Eoferr;
- };
|