httpzreader.h 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. #pragma once
  2. #include "httpheader.h"
  3. #include "httpparser.h"
  4. #include "exthttpcodes.h"
  5. #include <util/system/defaults.h>
  6. #include <util/generic/yexception.h>
  7. #include <contrib/libs/zlib/zlib.h>
  8. #include <errno.h>
  9. #ifndef ENOTSUP
  10. #define ENOTSUP 45
  11. #endif
  12. template <class Reader>
  13. class TCompressedHttpReader: public THttpReader<Reader> {
  14. typedef THttpReader<Reader> TBase;
  15. public:
  16. using TBase::AssumeConnectionClosed;
  17. using TBase::Header;
  18. using TBase::ParseGeneric;
  19. using TBase::State;
  20. static constexpr size_t DefaultBufSize = 64 << 10;
  21. static constexpr unsigned int DefaultWinSize = 15;
  22. TCompressedHttpReader()
  23. : CompressedInput(false)
  24. , BufSize(0)
  25. , CurContSize(0)
  26. , MaxContSize(0)
  27. , Buf(nullptr)
  28. , ZErr(0)
  29. , ConnectionClosed(0)
  30. , IgnoreTrailingGarbage(true)
  31. {
  32. memset(&Stream, 0, sizeof(Stream));
  33. }
  34. ~TCompressedHttpReader() {
  35. ClearStream();
  36. if (Buf) {
  37. free(Buf);
  38. Buf = nullptr;
  39. }
  40. }
  41. void SetConnectionClosed(int cc) {
  42. ConnectionClosed = cc;
  43. }
  44. void SetIgnoreTrailingGarbage(bool ignore) {
  45. IgnoreTrailingGarbage = ignore;
  46. }
  47. int Init(
  48. THttpHeader* H,
  49. int parsHeader,
  50. const size_t maxContSize = Max<size_t>(),
  51. const size_t bufSize = DefaultBufSize,
  52. const unsigned int winSize = DefaultWinSize,
  53. bool headRequest = false)
  54. {
  55. ZErr = 0;
  56. CurContSize = 0;
  57. MaxContSize = maxContSize;
  58. int ret = TBase::Init(H, parsHeader, ConnectionClosed, headRequest);
  59. if (ret)
  60. return ret;
  61. ret = SetCompression(H->compression_method, bufSize, winSize);
  62. return ret;
  63. }
  64. long Read(void*& buf) {
  65. if (!CompressedInput) {
  66. long res = TBase::Read(buf);
  67. if (res > 0) {
  68. CurContSize += (size_t)res;
  69. if (CurContSize > MaxContSize) {
  70. ZErr = E2BIG;
  71. return -1;
  72. }
  73. }
  74. return res;
  75. }
  76. while (true) {
  77. if (Stream.avail_in == 0) {
  78. void* tmpin = Stream.next_in;
  79. long res = TBase::Read(tmpin);
  80. Stream.next_in = (Bytef*)tmpin;
  81. if (res <= 0)
  82. return res;
  83. Stream.avail_in = (uInt)res;
  84. }
  85. Stream.next_out = Buf;
  86. Stream.avail_out = (uInt)BufSize;
  87. buf = Buf;
  88. int err = inflate(&Stream, Z_SYNC_FLUSH);
  89. //Y_ASSERT(Stream.avail_in == 0);
  90. switch (err) {
  91. case Z_OK:
  92. // there is no data in next_out yet
  93. if (BufSize == Stream.avail_out)
  94. continue;
  95. [[fallthrough]]; // don't break or return; continue with Z_STREAM_END case
  96. case Z_STREAM_END:
  97. if (Stream.total_out > MaxContSize) {
  98. ZErr = E2BIG;
  99. return -1;
  100. }
  101. if (!IgnoreTrailingGarbage && BufSize == Stream.avail_out && Stream.avail_in > 0) {
  102. Header->error = EXT_HTTP_GZIPERROR;
  103. ZErr = EFAULT;
  104. Stream.msg = (char*)"trailing garbage";
  105. return -1;
  106. }
  107. return long(BufSize - Stream.avail_out);
  108. case Z_NEED_DICT:
  109. case Z_DATA_ERROR:
  110. Header->error = EXT_HTTP_GZIPERROR;
  111. ZErr = EFAULT;
  112. return -1;
  113. case Z_MEM_ERROR:
  114. ZErr = ENOMEM;
  115. return -1;
  116. default:
  117. ZErr = EINVAL;
  118. return -1;
  119. }
  120. }
  121. return -1;
  122. }
  123. const char* ZMsg() const {
  124. return Stream.msg;
  125. }
  126. int ZError() const {
  127. return ZErr;
  128. }
  129. size_t GetCurContSize() const {
  130. return CompressedInput ? Stream.total_out : CurContSize;
  131. }
  132. protected:
  133. int SetCompression(const int compression, const size_t bufSize,
  134. const unsigned int winSize) {
  135. ClearStream();
  136. int winsize = winSize;
  137. switch ((enum HTTP_COMPRESSION)compression) {
  138. case HTTP_COMPRESSION_UNSET:
  139. case HTTP_COMPRESSION_IDENTITY:
  140. CompressedInput = false;
  141. return 0;
  142. case HTTP_COMPRESSION_GZIP:
  143. CompressedInput = true;
  144. winsize += 16; // 16 indicates gzip, see zlib.h
  145. break;
  146. case HTTP_COMPRESSION_DEFLATE:
  147. CompressedInput = true;
  148. winsize = -winsize; // negative indicates raw deflate stream, see zlib.h
  149. break;
  150. case HTTP_COMPRESSION_COMPRESS:
  151. case HTTP_COMPRESSION_ERROR:
  152. default:
  153. CompressedInput = false;
  154. ZErr = ENOTSUP;
  155. return -1;
  156. }
  157. if (bufSize != BufSize) {
  158. if (Buf)
  159. free(Buf);
  160. Buf = (ui8*)malloc(bufSize);
  161. if (!Buf) {
  162. ZErr = ENOMEM;
  163. return -1;
  164. }
  165. BufSize = bufSize;
  166. }
  167. int err = inflateInit2(&Stream, winsize);
  168. switch (err) {
  169. case Z_OK:
  170. Stream.total_in = 0;
  171. Stream.total_out = 0;
  172. Stream.avail_in = 0;
  173. return 0;
  174. case Z_DATA_ERROR: // never happens, see zlib.h
  175. CompressedInput = false;
  176. ZErr = EFAULT;
  177. return -1;
  178. case Z_MEM_ERROR:
  179. CompressedInput = false;
  180. ZErr = ENOMEM;
  181. return -1;
  182. default:
  183. CompressedInput = false;
  184. ZErr = EINVAL;
  185. return -1;
  186. }
  187. }
  188. void ClearStream() {
  189. if (CompressedInput) {
  190. inflateEnd(&Stream);
  191. CompressedInput = false;
  192. }
  193. }
  194. z_stream Stream;
  195. bool CompressedInput;
  196. size_t BufSize;
  197. size_t CurContSize, MaxContSize;
  198. ui8* Buf;
  199. int ZErr;
  200. int ConnectionClosed;
  201. bool IgnoreTrailingGarbage;
  202. };
  203. class zlib_exception: public yexception {
  204. };
  205. template <class Reader>
  206. class SCompressedHttpReader: public TCompressedHttpReader<Reader> {
  207. typedef TCompressedHttpReader<Reader> TBase;
  208. public:
  209. using TBase::ZError;
  210. using TBase::ZMsg;
  211. SCompressedHttpReader()
  212. : TBase()
  213. {
  214. }
  215. int Init(
  216. THttpHeader* H,
  217. int parsHeader,
  218. const size_t maxContSize = Max<size_t>(),
  219. const size_t bufSize = TBase::DefaultBufSize,
  220. const unsigned int winSize = TBase::DefaultWinSize,
  221. bool headRequest = false)
  222. {
  223. int ret = TBase::Init(H, parsHeader, maxContSize, bufSize, winSize, headRequest);
  224. return (int)HandleRetValue((long)ret);
  225. }
  226. long Read(void*& buf) {
  227. long ret = TBase::Read(buf);
  228. return HandleRetValue(ret);
  229. }
  230. protected:
  231. long HandleRetValue(long ret) {
  232. switch (ZError()) {
  233. case 0:
  234. return ret;
  235. case ENOMEM:
  236. ythrow yexception() << "SCompressedHttpReader: not enough memory";
  237. case EINVAL:
  238. ythrow yexception() << "SCompressedHttpReader: zlib error: " << ZMsg();
  239. case ENOTSUP:
  240. ythrow yexception() << "SCompressedHttpReader: unsupported compression method";
  241. case EFAULT:
  242. ythrow zlib_exception() << "SCompressedHttpReader: " << ZMsg();
  243. case E2BIG:
  244. ythrow zlib_exception() << "SCompressedHttpReader: Content exceeds maximum length";
  245. default:
  246. ythrow yexception() << "SCompressedHttpReader: unknown error";
  247. }
  248. }
  249. };