/************************************************************************* > File Name: http2_common.cpp > Author: > Mail: > Created Time: 2018年09月21日 星期五 13时59分03秒 ************************************************************************/ #include #include #include #include #include #include #include #include #include RTLogInit2Data logging_sc_lid = { }; RTLogInit2Data *logger() { return &logging_sc_lid; }; Http2Plugin g_http2_plugin = { }; Http2Plugin *http2_plugin() { return &g_http2_plugin; } int http2_header_str_to_val(const char *str, size_t slen, const char * map[], unsigned int map_size) { size_t normlen = 0; for (unsigned int i = 2; i < map_size; i++) { normlen = strlen(map[i]); if ( (slen == normlen) && !strncasecmp(str, map[i], normlen)) { return i; } } // in http [TFE_HTTP_UNKNOWN_FIELD] = NULL; [TFE_HTTP_HOST] = "Host", // in http2 [TFE_HTTP_UNKNOWN_FIELD] = "unknown"; [TFE_HTTP_HOST] = ":authority" normlen = strlen(":authority"); if ( (slen == normlen) && !strncasecmp(str, ":authority", normlen)) { return TFE_HTTP_HOST; } else { return TFE_HTTP_UNKNOWN_FIELD; } } const char* http2_header_val_to_str(unsigned int val, const char * map[], unsigned int map_size) { const static char * host = ":authority"; const static char * unknown = "unknown"; if (val < map_size && val != TFE_HTTP_HOST && val != TFE_HTTP_UNKNOWN_FIELD) { return map[val]; } // in http [TFE_HTTP_UNKNOWN_FIELD] = NULL; [TFE_HTTP_HOST] = "Host", // in http2 [TFE_HTTP_UNKNOWN_FIELD] = "unknown"; [TFE_HTTP_HOST] = ":authority" if (val == TFE_HTTP_HOST) { return host; } else { return unknown; } } const char * try_val_to_str_idx(const unsigned int val, const struct value_string *vs, int *idx) { int i = 0; if (idx == NULL){ goto finish; } if(vs) { while (vs[i].strptr) { if (vs[i].value == val) { *idx = i; return(vs[i].strptr); } i++; } } finish: *idx = -1; return NULL; } /* Find the index of a string in a value_string, or -1 when not present */ int str_to_val_idx(const char *val, const struct value_string *vs) { int i = 0; if(vs) { while (vs[i].strptr) { if (strcmp(vs[i].strptr, val) == 0) { return i; } i++; } } return 0; } const char* val_to_str(unsigned int val, const struct value_string *vs) { int ignore_me; return try_val_to_str_idx(val, vs, &ignore_me); } int str_to_val(const char *val, const struct value_string *vs) { return str_to_val_idx(val, vs); } static int gzip_decompress_init(struct http2_codec_ctx **codec_ctx, int encode) { if (*codec_ctx != NULL) { return Z_OK; } *codec_ctx = ALLOC(struct http2_codec_ctx, 1); if(*codec_ctx == NULL) { *codec_ctx=NULL; return -1; } /* ZSTREAM */ (*codec_ctx)->zst.zalloc = NULL; (*codec_ctx)->zst.zfree = NULL; (*codec_ctx)->zst.opaque = NULL; (*codec_ctx)->zst.avail_in = 0; (*codec_ctx)->zst.next_in = Z_NULL; // Z_OK stand for 0; Z_ERRNO stand for -1. int ret = Z_ERRNO; if (encode == HTTP2_CONTENT_ENCODING_GZIP) { ret = inflateInit2(&((*codec_ctx)->zst), MAX_WBITS + 16); } if (encode == HTTP2_CONTENT_ENCODING_DEFLATE) { ret = inflateInit2(&((*codec_ctx)->zst), -MAX_WBITS); } return ret; } static int gzip_decompress_decode(struct http2_codec_ctx **strm, char **dest, int *outlen) { #define CHUNK (1024 * 1024 * 4) int ret = -1; unsigned have; unsigned char out[CHUNK]; int totalsize = 0; /* run inflate() on input until output buffer not full */ do { (*strm)->zst.avail_out = CHUNK; (*strm)->zst.next_out = out; ret = inflate(&((*strm)->zst), Z_NO_FLUSH); switch (ret) { case Z_STREAM_ERROR: ret = Z_STREAM_ERROR; case Z_NEED_DICT: ret = Z_DATA_ERROR; /* and fall through */ case Z_DATA_ERROR: case Z_MEM_ERROR: inflateEnd(&((*strm)->zst)); return ret; } have = CHUNK - (*strm)->zst.avail_out; totalsize += have; *dest = (char *)realloc(*dest,totalsize); memcpy(*dest + totalsize - have,out,have); *outlen = have; } while ((*strm)->zst.avail_out == 0); return ret; } static int brotli_decompress_init(struct http2_codec_ctx **codec_ctx) { int ret = Z_ERRNO; if (*codec_ctx != NULL) { return Z_OK; } *codec_ctx = ALLOC(struct http2_codec_ctx, 1); if(*codec_ctx == NULL) { *codec_ctx=NULL; return -1; } (*codec_ctx)->brdec_state = BrotliDecoderCreateInstance(NULL, NULL, NULL); if ((*codec_ctx)->brdec_state != NULL) { return Z_OK; } return ret; } static int brotli_decompress_decode(struct http2_codec_ctx **strm, const uint8_t *source, int len, char **dest, int *outlen) { #define CHUNK (1024 * 1024 * 4) unsigned char out[CHUNK]; int totalsize = 0 ,ret = -1; size_t available_out; unsigned char * next_out; size_t available_in = len; const unsigned char * next_in = source; for ( ; ; ){ available_out = CHUNK; next_out = out; ret = BrotliDecoderDecompressStream((*strm)->brdec_state, &available_in, &next_in, &available_out, &next_out, 0); size_t have = CHUNK - available_out; if (have > 0){ totalsize += have; *dest = (char *)realloc(*dest,totalsize); memcpy(*dest + totalsize - have, out, have); *outlen = have; } if (ret == BROTLI_DECODER_RESULT_SUCCESS || ret == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT){ ret = 1; goto finish; } if (ret == BROTLI_DECODER_RESULT_ERROR){ ret = -1; goto finish; } assert(ret == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT); } finish: return ret; } static int zstd_decompress_init(struct http2_codec_ctx **codec_ctx) { int ret = Z_ERRNO; if (*codec_ctx != NULL) { return Z_OK; } *codec_ctx = ALLOC(struct http2_codec_ctx, 1); if(*codec_ctx == NULL) { *codec_ctx=NULL; return -1; } (*codec_ctx)->dctx = ZSTD_createDCtx(); if((*codec_ctx)->dctx != NULL) { ret = Z_OK; } return ret; } static int zstd_decompress_decode(struct http2_codec_ctx **strm, const uint8_t *source, int len, char **dest, int *outlen) { int chunk_sz = 1024 * 1024 * 4; unsigned char out[chunk_sz]; int totalsize = 0 ,ret = -1; size_t available_out; unsigned char * next_out=NULL; size_t available_in = len; const unsigned char * next_in = source; ZSTD_inBuffer input = {next_in, available_in, 0 }; for ( ; ; ){ available_out = CHUNK; next_out = out; ZSTD_outBuffer output = {next_out, available_out, 0 }; ret = ZSTD_decompressStream((*strm)->dctx, &output, &input); //printf("error name = %s\n", ZSTD_getErrorName(ret)); size_t have = CHUNK - (output.size - output.pos); if (have > 0) { totalsize += have; *dest = (char *)realloc(*dest,totalsize); memcpy(*dest + totalsize - have, out, have); *outlen = have; } if(ret >= 0) { ret=1; goto finish; } if(ret < 0) { ret = -1; goto finish; } } finish: return ret; } static int gzip_compress_init(struct http2_codec_ctx **codec_ctx, int encode) { if (*codec_ctx != NULL) { return Z_OK; } *codec_ctx = ALLOC(struct http2_codec_ctx, 1); if(*codec_ctx == NULL) { *codec_ctx=NULL; return -1; } (*codec_ctx)->zst.zalloc = (alloc_func)0; (*codec_ctx)->zst.zfree = (free_func)0; (*codec_ctx)->zst.opaque = (voidpf)0; (*codec_ctx)->zst.avail_in = 0; (*codec_ctx)->zst.next_in = Z_NULL; int wbits = 0; if (encode == HTTP2_CONTENT_ENCODING_GZIP) { wbits = MAX_WBITS + 16; } if (encode == HTTP2_CONTENT_ENCODING_DEFLATE) { wbits = -MAX_WBITS; } return deflateInit2(&((*codec_ctx)->zst), Z_DEFAULT_COMPRESSION, Z_DEFLATED, wbits, 8, Z_DEFAULT_STRATEGY); } static int gzip_compress_encode(struct http2_codec_ctx **strm, const uint8_t *source, int slen, struct evbuffer * evbuf, int end) { #define SZ_IOVEC 2 int ret = 0; unsigned int i = 0; struct evbuffer_iovec io[SZ_IOVEC]; size_t max = slen > 8192 ? slen : 8192; int iov_count = evbuffer_reserve_space(evbuf, max, io, SZ_IOVEC); if (iov_count < 1 || iov_count > SZ_IOVEC) { return -1; } (*strm)->zst.next_in = (unsigned char *) source; (*strm)->zst.avail_in = (unsigned int) slen; (*strm)->zst.next_out = (unsigned char *) io[i].iov_base; (*strm)->zst.avail_out = (unsigned int) io[i].iov_len; int flush = end ? Z_FINISH : Z_NO_FLUSH; do { ret = deflate(&((*strm)->zst), flush); assert(ret != Z_STREAM_ERROR); assert(i < SZ_IOVEC); if ((*strm)->zst.avail_out == 0 || (*strm)->zst.avail_in == 0) { unsigned int len = (unsigned int) io[i].iov_len - (*strm)->zst.avail_out; io[i].iov_len = (size_t) len; i++; (*strm)->zst.next_out = (unsigned char *) io[i].iov_base; (*strm)->zst.avail_out = (unsigned int) io[i].iov_len; } } while ((*strm)->zst.avail_in > 0); assert(end == 0 || ret == Z_STREAM_END); (void)ret; return evbuffer_commit_space(evbuf, io, iov_count); } static int brotli_compress_init(struct http2_codec_ctx **codec_ctx) { if (*codec_ctx != NULL) { return Z_OK; } *codec_ctx = ALLOC(struct http2_codec_ctx, 1); if(*codec_ctx == NULL) { *codec_ctx=NULL; return -1; } (*codec_ctx)->brenc_state = BrotliEncoderCreateInstance(NULL, NULL, NULL); if ((*codec_ctx)->brenc_state == NULL) { return -1; } BrotliEncoderSetParameter((*codec_ctx)->brenc_state, BROTLI_PARAM_QUALITY, 3); return 0; } static int brotli_compress_encode(struct http2_codec_ctx **strm, const uint8_t *source, int slen, struct evbuffer * evbuf, int end) { struct evbuffer_iovec v[1]; size_t __sz_reserve_chunk = slen > 8192 ? slen : 8192; int iov_count = evbuffer_reserve_space(evbuf, __sz_reserve_chunk, v, 1); if (iov_count != 1) return -1; const unsigned char * next_in = source; size_t avail_in = slen; unsigned char * next_out = (unsigned char *)v[0].iov_base; size_t avail_out = v[0].iov_len; enum BrotliEncoderOperation op = end ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS; int ret = 0; do { ret = BrotliEncoderCompressStream((*strm)->brenc_state, op, &avail_in, &next_in, &avail_out, &next_out, NULL); if(unlikely(ret == BROTLI_FALSE)){ return ret; } if (avail_out == 0 || avail_in == 0) { v[0].iov_len = v[0].iov_len - avail_out; ret = evbuffer_commit_space(evbuf, v, iov_count); if(ret < 0) return -2; if(avail_out == 0){ iov_count = evbuffer_reserve_space(evbuf, __sz_reserve_chunk, v, 1); if(unlikely(iov_count != 1)) return -3; next_out = (unsigned char *) v[0].iov_base; avail_out = (unsigned int) v[0].iov_len; } } }while (avail_in > 0); return 0; } static int zstd_compress_init(struct http2_codec_ctx **codec_ctx) { if (*codec_ctx != NULL) { return Z_OK; } *codec_ctx = ALLOC(struct http2_codec_ctx, 1); if(*codec_ctx == NULL) { *codec_ctx=NULL; return -1; } (*codec_ctx)->cctx = ZSTD_createCCtx(); if((*codec_ctx)->cctx == NULL) { return -1; } ZSTD_CCtx_setParameter((*codec_ctx)->cctx, ZSTD_c_compressionLevel, 1); ZSTD_CCtx_setParameter((*codec_ctx)->cctx, ZSTD_c_checksumFlag, 1); return 0; } static int zstd_compress_encode(struct http2_codec_ctx **strm, const uint8_t *source, int slen, struct evbuffer * evbuf, int end) { size_t toRead = 0; struct evbuffer_iovec v[1] = {0}; ZSTD_outBuffer output = {0}; const unsigned char *next_in = NULL; size_t remaining=0; size_t const buffInSize = slen; size_t const buffOutSize = ZSTD_CStreamOutSize(); //printf("slen = %d\n", slen); size_t __sz_reserve_chunk = (size_t)slen > buffOutSize ? (size_t)slen : buffOutSize; int iov_count = evbuffer_reserve_space(evbuf, __sz_reserve_chunk, v, 1); if (iov_count != 1) { return -1; } unsigned char * next_out = (unsigned char *)v[0].iov_base; size_t avail_out = v[0].iov_len; output = (ZSTD_outBuffer){next_out, avail_out, 0}; ZSTD_EndDirective const mode = end ? ZSTD_e_end : ZSTD_e_continue; //ZSTD_EndDirective const mode = end ? ZSTD_e_flush : ZSTD_e_continue; if (source != NULL) { next_in = source; while (next_in < source + slen) { toRead = (next_in + buffInSize < source + slen) ? buffInSize : (source + slen - next_in); ZSTD_inBuffer input = {next_in, toRead, 0}; if (output.pos == output.size && output.pos != 0 && output.size != 0) { if (evbuffer_reserve_space(evbuf, __sz_reserve_chunk, v, 1) == -1) { return -1; } next_out = (unsigned char *)v[0].iov_base; avail_out = v[0].iov_len; output = (ZSTD_outBuffer){next_out, avail_out, 0}; } remaining = ZSTD_compressStream2((*strm)->cctx, &output, &input, mode); if (ZSTD_isError(remaining)) { return -1; } if (output.pos > 0) { v[0].iov_len = output.pos; if (evbuffer_commit_space(evbuf, v, 1) == -1) { return -1; } output.pos = 0; } next_in += toRead; } } else { ZSTD_inBuffer input = {source, (size_t)slen, 0}; remaining = ZSTD_compressStream2((*strm)->cctx, &output, &input, mode); if (ZSTD_isError(remaining)) { return -1; } if (output.pos > 0) { v[0].iov_len = output.pos; if (evbuffer_commit_space(evbuf, v, 1) == -1) { return -1; } } } return 0; } int http2_decompress_stream(const uint8_t *source, int len, char **dest, int *outlen, struct http2_codec_ctx **codec_ctx, int encode) { int ret = -1; // Z_OK stand for 0; Z_ERRNO stand for -1. switch(encode) { case HTTP2_CONTENT_ENCODING_GZIP: case HTTP2_CONTENT_ENCODING_DEFLATE: ret = gzip_decompress_init(codec_ctx, encode); if(ret != Z_OK) { goto finish; } (*codec_ctx)->zst.avail_in = len; (*codec_ctx)->zst.next_in = (Bytef *)source; ret = gzip_decompress_decode(codec_ctx, dest, outlen); break; case HTTP2_CONTENT_ENCODING_BR: ret = brotli_decompress_init(codec_ctx); if(ret != Z_OK) { goto finish; } ret =brotli_decompress_decode(codec_ctx, source, len, dest, outlen); break; case HTTP2_CONTENT_ENCODING_ZSTD: ret = zstd_decompress_init(codec_ctx); if(ret != Z_OK) { goto finish; } ret = zstd_decompress_decode(codec_ctx, source, len, dest, outlen);; break; default: break; } return ret; finish: if(*codec_ctx) { FREE(codec_ctx); } return ret; } int http2_compress_stream(struct http2_codec_ctx **codec_ctx, const uint8_t *source, int slen, struct evbuffer * evbuf, int encode, int mode) { int ret = -1; // Z_OK stand for 0; Z_ERRNO stand for -1. switch(encode) { case HTTP2_CONTENT_ENCODING_GZIP: case HTTP2_CONTENT_ENCODING_DEFLATE: ret = gzip_compress_init(codec_ctx, encode); if(ret != Z_OK) { goto finish; } ret = gzip_compress_encode(codec_ctx, source, slen, evbuf, mode); break; case HTTP2_CONTENT_ENCODING_BR: ret = brotli_compress_init(codec_ctx); if(ret != Z_OK) { goto finish; } ret = brotli_compress_encode(codec_ctx, source, slen, evbuf, mode); break; case HTTP2_CONTENT_ENCODING_ZSTD: ret = zstd_compress_init(codec_ctx); if(ret != Z_OK) { goto finish; } ret = zstd_compress_encode(codec_ctx, source, slen, evbuf, mode); break; default: break; } return ret; finish: if(*codec_ctx) { FREE(codec_ctx); } return ret; } void http2_decompress_finished(struct http2_codec_ctx **codec_ctx) { if (*codec_ctx != NULL) { if ((*codec_ctx)->brdec_state) { BrotliDecoderDestroyInstance((*codec_ctx)->brdec_state); (*codec_ctx)->brdec_state = NULL; goto finish; } if ((*codec_ctx)->dctx) { ZSTD_freeDCtx((*codec_ctx)->dctx); (*codec_ctx)->dctx = NULL; goto finish; } (void)inflateEnd(&((*codec_ctx)->zst)); finish: free(*codec_ctx); *codec_ctx = NULL; } } void http2_compress_finished(struct http2_codec_ctx **codec_ctx) { if (*codec_ctx != NULL) { if ((*codec_ctx)->brenc_state) { BrotliEncoderDestroyInstance((*codec_ctx)->brenc_state); (*codec_ctx)->brenc_state = NULL; goto finish; } if ((*codec_ctx)->cctx) { ZSTD_freeCCtx((*codec_ctx)->cctx); (*codec_ctx)->cctx = NULL; goto finish; } (void) deflateEnd(&((*codec_ctx)->zst)); finish: free(*codec_ctx); *codec_ctx = NULL; } }