diff --git a/attachments/envoy/build_template b/attachments/envoy/build_template old mode 100644 new mode 100755 index 4674809..d54bb36 --- a/attachments/envoy/build_template +++ b/attachments/envoy/build_template @@ -10,4 +10,4 @@ ENVOY_ATTACHMENT_DIR="@ENVOY_ATTACHMENT_DIR@" cd $ENVOY_ATTACHMENT_DIR # Run the go build command -CC=gcc CGO_CFLAGS="-I@ATTACHMENTS_INCLUDE_DIR@ -I@NANO_ATTACHMENT_INCLUDE_DIR@" go build -o ${ENVOY_ATTACHMENT_DIR}/libenvoy_attachment.so -buildmode=c-shared -ldflags="-extldflags '-L${SHMEM_LIBRARY_DIR} -L${NANO_ATTACHMENT_LIBRARY_DIR} -L${NANO_ATTACHMENT_UTIL_LIBRARY_DIR} ${LIBRARIES}'" +CGO_CFLAGS="-I@ATTACHMENTS_INCLUDE_DIR@ -I@NANO_ATTACHMENT_INCLUDE_DIR@" go build -o ${ENVOY_ATTACHMENT_DIR}/libenvoy_attachment.so -buildmode=c-shared -ldflags="-extldflags '-L${SHMEM_LIBRARY_DIR} -L${NANO_ATTACHMENT_LIBRARY_DIR} -L${NANO_ATTACHMENT_UTIL_LIBRARY_DIR} ${LIBRARIES}'" diff --git a/attachments/envoy/config.go b/attachments/envoy/config.go index dfac413..617975e 100755 --- a/attachments/envoy/config.go +++ b/attachments/envoy/config.go @@ -3,7 +3,6 @@ package main import ( "encoding/json" "fmt" - "io/ioutil" "net/http" "runtime" "strconv" @@ -35,53 +34,58 @@ unsigned long get_thread_id() { import "C" const Name = "cp_nano_filter" - -const Admin_api = "http://127.0.0.1:%s/server_info" +const admin_api_server_info = "http://127.0.0.1:%s/server_info" +const keep_alive_interval = 10 * time.Second var filter_id atomic.Int64 - -type nano_attachment C.struct_NanoAttachment - var attachments_map map[int]*nano_attachment = nil var thread_to_attachment_mapping map[int]int = nil var attachment_to_thread_mapping map[int]int = nil - var attachment_to_filter_request_structs map[int]*filterRequestStructs = nil - var mutex sync.Mutex - -const keep_alive_interval = 10 * time.Second - var last_keep_alive time.Time +type nano_attachment C.struct_NanoAttachment + // EnvoyServerInfo represents the structure of the JSON response from /server_info type EnvoyServerInfo struct { Concurrency int `json:"concurrency"` } -// getEnvoyConcurrency fetches and returns the concurrency level of Envoy from the admin API -func getEnvoyConcurrency(admin_api_address string) (int, error) { - resp, err := http.Get(admin_api_address) - if err != nil { - return 0, fmt.Errorf("failed to reach Envoy admin API: %w", err) - } - defer resp.Body.Close() +func getEnvoyConcurrency() int { + concurrency_method := getEnv("CONCURRENCY_CALC", "numOfCores") - if resp.StatusCode != http.StatusOK { - return 0, fmt.Errorf("unexpected status code from Envoy admin API: %d", resp.StatusCode) + if concurrency_method == "numOfCores" { + api.LogWarnf("using number of CPU cores") + return runtime.NumCPU() } - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return 0, fmt.Errorf("failed to read response body: %w", err) + var conc_number string + + switch concurrency_method { + case "istioCpuLimit": + conc_number = getEnv("ISTIO_CPU_LIMIT", "-1") + api.LogWarnf("using istioCpuLimit, conc_number %s", conc_number) + case "custom": + conc_number = getEnv("CONCURRENCY_NUMBER", "-1") + api.LogWarnf("using custom concurrency number, conc_number %s", conc_number) + default: + api.LogWarnf("unknown concurrency method %s, using number of CPU cores", concurrency_method) + return runtime.NumCPU() } - var info EnvoyServerInfo - if err := json.Unmarshal(body, &info); err != nil { - return 0, fmt.Errorf("failed to parse JSON response: %w", err) + if conc_number == "-1" { + api.LogWarnf("concurrency number is not set as an env variable, using number of CPU cores") + return runtime.NumCPU() } - return info.Concurrency, nil + conc_num, err := strconv.Atoi(conc_number) + if err != nil || conc_num <= 0 { + api.LogWarnf("error converting concurrency number %s, using number of CPU cores", conc_number) + return runtime.NumCPU() + } + + return conc_num } func configurationServer() { @@ -150,8 +154,7 @@ func configurationServer() { func init() { last_keep_alive = time.Time{} - envoyHttp.RegisterHttpFilterFactoryAndConfigParser(Name, ConfigFactory, &parser{}) - //envoyHttp.RegisterHttpFilterConfigFactoryAndParser(Name, ConfigFactory, &parser{}) + envoyHttp.RegisterHttpFilterConfigFactoryAndParser(Name, ConfigFactory, &parser{}) go configurationServer() } @@ -193,37 +196,7 @@ func (p *parser) Parse(any *anypb.Any, callbacks api.ConfigCallbackHandler) (int return conf, nil } - var num_of_workers int - concurrency_method := getEnv("CONCURRENCY_CALC", "numOfCores") - - if concurrency_method == "numOfCores" { - num_of_workers = runtime.NumCPU() - api.LogInfof("using number of cpu cores %d", num_of_workers) - } else if concurrency_method == "config" { - config_port := getEnv("CONFIG_PORT", "15000") - admin_api := fmt.Sprintf(Admin_api, config_port) - workers, err := getEnvoyConcurrency(admin_api) - if err != nil { - api.LogWarnf("unable to fetch concurrency from admin server, using cpu cores. err: %s", err.Error()) - num_of_workers = runtime.NumCPU() - } else { - num_of_workers = workers - } - } else if concurrency_method == "custom" { - conc_number := getEnv("CONCURRENCY_NUMBER", "-1") - if conc_number == "-1" { - api.LogWarnf("concurrency number is not set as an env variable, using cpu cores") - num_of_workers = runtime.NumCPU() - } else if conc_num, err := strconv.Atoi(conc_number); err == nil && conc_num > 0 { - num_of_workers = conc_num - } else { - api.LogWarnf("error converting conc_number %s, using num of cpu cores", conc_number) - num_of_workers = runtime.NumCPU() - } - } else { - api.LogWarnf("unable to fetch concurrency from %s, using cpu cores", concurrency_method) - num_of_workers = runtime.NumCPU() - } + num_of_workers := getEnvoyConcurrency() configStruct := &xds.TypedStruct{} if err := any.UnmarshalTo(configStruct); err != nil { @@ -266,87 +239,46 @@ func (p *parser) Merge(parent interface{}, child interface{}) interface{} { return &newConfig } -// func ConfigFactory(c interface{}) api.StreamFilterFactory { -// conf, ok := c.(*config) -// if !ok { -// panic("unexpected config type") -// } - -// return func(callbacks api.FilterCallbackHandler) api.StreamFilter { -// worker_thread_id := int(C.get_thread_id()) -// api.LogDebugf("worker_thread_id: %d", worker_thread_id) -// if _, ok := thread_to_attachment_mapping[int(worker_thread_id)]; !ok { -// api.LogDebugf("need to add new thread to the map") -// map_size := len(attachment_to_thread_mapping) -// if map_size < len(attachments_map) { -// attachment_to_thread_mapping[map_size] = worker_thread_id -// thread_to_attachment_mapping[worker_thread_id] = map_size -// api.LogDebugf("len(attachment_to_thread_mapping): %d", len(attachment_to_thread_mapping)) -// api.LogDebugf("thread_to_attachment_mapping: %v", thread_to_attachment_mapping) -// api.LogDebugf("attachment_to_thread_mapping: %v", attachment_to_thread_mapping) -// } else { -// panic("unexpected thread id") -// } -// } - -// worker_id := thread_to_attachment_mapping[int(worker_thread_id)] -// api.LogDebugf("worker_id: %d", worker_id) - -// filter_id.Add(1) -// session_id := filter_id.Load() -// attachment_ptr := attachments_map[worker_id] -// session_data := C.InitSessionData((*C.NanoAttachment)(attachment_ptr), C.SessionID(session_id)) - -// return &filter{ -// callbacks: callbacks, -// config: conf, -// session_id: session_id, -// cp_attachment: attachment_ptr, -// session_data: session_data, -// request_structs: attachment_to_filter_request_structs[worker_id], -// } -// } -// } - -func ConfigFactory(c interface{}, callbacks api.FilterCallbackHandler) api.StreamFilter { +func ConfigFactory(c interface{}) api.StreamFilterFactory { conf, ok := c.(*config) if !ok { panic("unexpected config type") } - worker_thread_id := int(C.get_thread_id()) - api.LogDebugf("worker_thread_id: %d", worker_thread_id) - if _, ok := thread_to_attachment_mapping[int(worker_thread_id)]; !ok { - api.LogDebugf("need to add new thread to the map") - map_size := len(attachment_to_thread_mapping) - if map_size < len(attachments_map) { - attachment_to_thread_mapping[map_size] = worker_thread_id - thread_to_attachment_mapping[worker_thread_id] = map_size - api.LogDebugf("len(attachment_to_thread_mapping): %d", len(attachment_to_thread_mapping)) - api.LogDebugf("thread_to_attachment_mapping: %v", thread_to_attachment_mapping) - api.LogDebugf("attachment_to_thread_mapping: %v", attachment_to_thread_mapping) - } else { - panic("unexpected thread id") + return func(callbacks api.FilterCallbackHandler) api.StreamFilter { + worker_thread_id := int(C.get_thread_id()) + api.LogDebugf("worker_thread_id: %d", worker_thread_id) + if _, ok := thread_to_attachment_mapping[int(worker_thread_id)]; !ok { + api.LogDebugf("need to add new thread to the map") + map_size := len(attachment_to_thread_mapping) + if map_size < len(attachments_map) { + attachment_to_thread_mapping[map_size] = worker_thread_id + thread_to_attachment_mapping[worker_thread_id] = map_size + api.LogDebugf("len(attachment_to_thread_mapping): %d", len(attachment_to_thread_mapping)) + api.LogDebugf("thread_to_attachment_mapping: %v", thread_to_attachment_mapping) + api.LogDebugf("attachment_to_thread_mapping: %v", attachment_to_thread_mapping) + } else { + panic("unexpected thread id") + } } - } - worker_id := thread_to_attachment_mapping[int(worker_thread_id)] - api.LogDebugf("worker_id: %d", worker_id) + worker_id := thread_to_attachment_mapping[int(worker_thread_id)] + api.LogDebugf("worker_id: %d", worker_id) - filter_id.Add(1) - session_id := filter_id.Load() - attachment_ptr := attachments_map[worker_id] - session_data := C.InitSessionData((*C.NanoAttachment)(attachment_ptr), C.SessionID(session_id)) + filter_id.Add(1) + session_id := filter_id.Load() + attachment_ptr := attachments_map[worker_id] + session_data := C.InitSessionData((*C.NanoAttachment)(attachment_ptr), C.SessionID(session_id)) - return &filter{ - callbacks: callbacks, - config: conf, - session_id: session_id, - cp_attachment: attachment_ptr, - session_data: session_data, - request_structs: attachment_to_filter_request_structs[worker_id], + return &filter{ + callbacks: callbacks, + config: conf, + session_id: session_id, + cp_attachment: attachment_ptr, + session_data: session_data, + request_structs: attachment_to_filter_request_structs[worker_id], + } } } - func main() {} diff --git a/attachments/envoy/filter.go b/attachments/envoy/filter.go index 78c6877..0f0549b 100755 --- a/attachments/envoy/filter.go +++ b/attachments/envoy/filter.go @@ -107,6 +107,7 @@ type filter struct { session_data *C.HttpSessionData cp_attachment *nano_attachment request_structs *filterRequestStructs + body_buffer_chunk int } type filterRequestStructs struct { @@ -230,10 +231,6 @@ func (f *filter) sendBody(buffer api.BufferInstance, is_req bool) C.AttachmentVe data := buffer.Bytes() data_len := len(data) buffer_size := 8 * 1024 - - // body_chunk := newNanoStr(data) - // body_chunk.data = (*C.uchar)(unsafe.Pointer(&data[0])) - num_of_buffers := ((data_len - 1) / buffer_size) + 1 // TO DO: FIX THIS ASAP @@ -299,20 +296,7 @@ func (f *filter) handleStartTransaction(header api.RequestHeaderMap) { } func (f *filter) sendLocalReplyInternal(ret_code int, custom_response string, headers map[string][]string) api.StatusType { - //f.callbacks.DecoderFilterCallbacks().SendLocalReply(ret_code, custom_response, headers, 0, "") // new api - // var headers_map map[string]string = nil - // if headers != nil { - // headers_map = make(map[string]string) - // for key, val := range headers { - // header_val := "" - // if len(val) > 0 { - // header_val = val[0] - // } - - // headers_map[key] = header_val - // } - // } - f.callbacks.DecoderFilterCallbacks().SendLocalReply(ret_code, custom_response, headers, 0, "") + f.callbacks.SendLocalReply(ret_code, custom_response, headers, 0, "") return api.LocalReply } @@ -428,6 +412,21 @@ func (f *filter) EncodeHeaders(header api.ResponseHeaderMap, endStream bool) api return ret } +func injectBodyChunk( + curr_modification *C.struct_NanoHttpModificationList, + body_buffer_chunk int, + buffer *api.BufferInstance) { + for curr_modification != nil { + if (int(curr_modification.modification.orig_buff_index) == body_buffer_chunk) { + mod := curr_modification.modification // type: HttpInjectData + modifications := C.GoString(curr_modification.modification_buffer) + new_buffer:= insertAtPosition((*buffer).String(), modifications, int(mod.injection_pos)) + (*buffer).SetString(new_buffer) + } + curr_modification = curr_modification.next + } +} + // EncodeData might be called multiple times during handling the response body. // The endStream is true when handling the last piece of the body. func (f *filter) EncodeData(buffer api.BufferInstance, endStream bool) api.StatusType { @@ -448,6 +447,8 @@ func (f *filter) EncodeData(buffer api.BufferInstance, endStream bool) api.Statu } res := f.sendBody(buffer, false) + injectBodyChunk(res.modifications, f.body_buffer_chunk, &buffer) + f.body_buffer_chunk++ if C.AttachmentVerdict(res.verdict) != C.ATTACHMENT_VERDICT_INSPECT { api.LogInfof("got final verict: %v", res.verdict) return f.finalizeRequest(&res) @@ -471,15 +472,15 @@ func (f *filter) EncodeTrailers(trailers api.ResponseTrailerMap) api.StatusType } // OnLog is called when the HTTP stream is ended on HTTP Connection Manager filter. -func (f *filter) OnLog(api.RequestHeaderMap, api.RequestTrailerMap, api.ResponseHeaderMap, api.ResponseTrailerMap) {} +func (f *filter) OnLog() {} // OnLogDownstreamStart is called when HTTP Connection Manager filter receives a new HTTP request // (required the corresponding access log type is enabled) -func (f *filter) OnLogDownstreamStart(api.RequestHeaderMap) {} +func (f *filter) OnLogDownstreamStart() {} // OnLogDownstreamPeriodic is called on any HTTP Connection Manager periodic log record // (required the corresponding access log type is enabled) -func (f *filter) OnLogDownstreamPeriodic(api.RequestHeaderMap, api.RequestTrailerMap, api.ResponseHeaderMap, api.ResponseTrailerMap) {} +func (f *filter) OnLogDownstreamPeriodic() {} func (f *filter) OnDestroy(reason api.DestroyReason) { freeHttpMetaDataFields(f.request_structs.http_meta_data) diff --git a/attachments/envoy/go.mod b/attachments/envoy/go.mod index 97fb593..21bb21c 100755 --- a/attachments/envoy/go.mod +++ b/attachments/envoy/go.mod @@ -1,15 +1,13 @@ module gitlab.ngen.checkpoint.com/Ngen/agent-core/attachments/envoy // the version should >= 1.18 -go 1.22 - -toolchain go1.22.5 +go 1.20 // NOTICE: these lines could be generated automatically by "go mod tidy" require ( github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa - github.com/envoyproxy/envoy v1.32.1 - google.golang.org/protobuf v1.35.1 + github.com/envoyproxy/envoy v1.29.8-0.20240702140355-f3e7e90ed021 + google.golang.org/protobuf v1.34.2 ) require github.com/go-chi/chi/v5 v5.1.0 diff --git a/attachments/envoy/go.sum b/attachments/envoy/go.sum index 0386434..e2e1e25 100755 --- a/attachments/envoy/go.sum +++ b/attachments/envoy/go.sum @@ -1,7 +1,11 @@ github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= -github.com/envoyproxy/envoy v1.32.1 h1:+HeajIC+S9PH3mjY/bVqJabjprqxA7h6pSQ+Ie1Ziww= -github.com/envoyproxy/envoy v1.32.1/go.mod h1:KGS+IUehDX1mSIdqodPTWskKOo7bZMLLy3GHxvOKcJk= +github.com/envoyproxy/envoy v1.28.1-0.20231218050644-ca5d45d1887a h1:PQveCjvjXZS400K7z+uuouN/Q/dLhLpB5a/6GIC4v34= +github.com/envoyproxy/envoy v1.28.1-0.20231218050644-ca5d45d1887a/go.mod h1:STO/nOGQMw2DNOFTaokM1VY72GJBs0mbXq0I1lJr0XQ= +github.com/envoyproxy/envoy v1.29.8-0.20240702140355-f3e7e90ed021 h1:mTisjPVHGpxlWi7Yj7PnpxD0GeoauHcWvEgch069i+M= +github.com/envoyproxy/envoy v1.29.8-0.20240702140355-f3e7e90ed021/go.mod h1:ujBFxE543X8OePZG+FbeR9LnpBxTLu64IAU7A20EB9A= +github.com/envoyproxy/envoy v1.31.0 h1:NsTo+medzu0bMffXAjl+zKaViLOShKuIZWQnKKYq0/4= +github.com/envoyproxy/envoy v1.31.0/go.mod h1:ujBFxE543X8OePZG+FbeR9LnpBxTLu64IAU7A20EB9A= github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= @@ -11,7 +15,6 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM= google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= @@ -19,5 +22,5 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= diff --git a/attachments/envoy/utils.go b/attachments/envoy/utils.go index 4eea735..0d0c90e 100755 --- a/attachments/envoy/utils.go +++ b/attachments/envoy/utils.go @@ -14,6 +14,7 @@ import ( "unsafe" "os" "runtime" + "strconv" ) func getEnv(key, defaultValue string) string { value, exists := os.LookupEnv(key) @@ -23,49 +24,48 @@ func getEnv(key, defaultValue string) string { return value } +var INSERT_POS_ERR_MSG = "Got invalid insertion position, will not insert." + func copyToSlice(dest []byte, src unsafe.Pointer, size C.size_t, location int) int { C.memcpy(unsafe.Pointer(&dest[location]), src, size) return location + int(size) } func newNanoStr(data []byte) *C.nano_str_t { - // Allocate memory for the nano_str_t struct nanoStr := (*C.nano_str_t)(C.malloc(C.size_t(unsafe.Sizeof(C.nano_str_t{})))) if nanoStr == nil { panic("failed to allocate memory for nano_str_t struct") } - // Set the length of the data nanoStr.len = C.size_t(len(data)) - - // Allocate memory for the data field and copy the Go byte slice into it - // nanoStr.data = (*C.uchar)(C.malloc(C.size_t(len(data)))) - // if nanoStr.data == nil { - // C.free(unsafe.Pointer(nanoStr)) - // panic("failed to allocate memory for data field") - // } - // copy((*[1 << 30]byte)(unsafe.Pointer(nanoStr.data))[:len(data):len(data)], data) - return nanoStr } +func insertAtPosition(buff string, injection string, pos int) string { + if pos < 0 || pos > len(buff) { + api.LogDebugf( + INSERT_POS_ERR_MSG + + " Position: " + + strconv.Itoa(pos) + + ", buffer's lenght: " + + strconv.Itoa(len(buff))) + return buff + } + return_buff := buff[:pos] + injection + buff[pos:] + return return_buff +} + func createNanoStr(str string) C.nano_str_t { c_str := C.CString(str) - // nanoStr := (*C.nano_str_t)(C.malloc(C.size_t(unsafe.Sizeof(C.nano_str_t{})))) nanoStr := C.nano_str_t{ len: C.size_t(len(str)), data: (*C.uchar)(unsafe.Pointer(c_str)), } - // nanoStr.len = C.size_t(len(str)) - // nanoStr.data = (*C.uchar)(unsafe.Pointer(c_str)) - return nanoStr } func createNanoStrWithoutCopy(str string) C.nano_str_t { - //c_str := C.CString(str) - nanoStr := C.nano_str_t{ len: C.size_t(len(str)), data: (*C.uchar)(unsafe.Pointer((*(*reflect.StringHeader)(unsafe.Pointer(&str))).Data)), @@ -105,4 +105,4 @@ func RecoverPanic(ret *api.StatusType) { *ret = api.Continue } -} \ No newline at end of file +} diff --git a/attachments/nano_attachment/CMakeLists.txt b/attachments/nano_attachment/CMakeLists.txt index 150d1f8..8fc6b10 100644 --- a/attachments/nano_attachment/CMakeLists.txt +++ b/attachments/nano_attachment/CMakeLists.txt @@ -4,9 +4,10 @@ include_directories(include) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE -lpthread -Wall") -link_directories(../../core) -link_directories(../../core/shmem_ipc) -include_directories(../../core/include/attachments) +link_directories(${ng_module_osrc_zlib_path}/lib) +link_directories(${CMAKE_BINARY_DIR}/core) +link_directories(${CMAKE_BINARY_DIR}/core/shmem_ipc) +include_directories(${PROJECT_SOURCE_DIR}/core/include/attachments) add_library( @@ -21,9 +22,10 @@ add_library( nano_attachment_sender.c nano_attachment_sender_thread.c nano_attachment_metric.c + nano_compression.c ) -target_link_libraries(nano_attachment shmem_ipc_2 nano_attachment_util) +target_link_libraries(nano_attachment shmem_ipc_2 nano_attachment_util compression_utils) # add_subdirectory(nano_attachment_ut) diff --git a/attachments/nano_attachment/include/mock_nano_compression.h b/attachments/nano_attachment/include/mock_nano_compression.h new file mode 100755 index 0000000..3d57f95 --- /dev/null +++ b/attachments/nano_attachment/include/mock_nano_compression.h @@ -0,0 +1,72 @@ +#ifndef __MOCK_NANO_COMPRESSION_H__ +#define __MOCK_NANO_COMPRESSION_H__ + +#include "cmock.h" +#include "nano_attachment_common.h" + +extern "C" { +#include "nano_compression.h" +} + +class NanoCompressionMocker : public CMockMocker +{ +public: + MOCK_METHOD3( + nano_compress_body, + HttpBody*( + NanoAttachment *attachment, + HttpBody *bodies, + HttpSessionData *session_data_p + ) + ); + + MOCK_METHOD3( + nano_decompress_body, + HttpBody*( + NanoAttachment *attachment, + HttpBody *bodies, + HttpSessionData *session_data_p + ) + ); + + MOCK_METHOD3( + nano_free_compressed_body, + void( + NanoAttachment *attachment, + HttpBody *bodies, + HttpSessionData *session_data_p + ) + ); +}; + +CMOCK_MOCK_FUNCTION3( + NanoCompressionMocker, + nano_compress_body, + HttpBody*( + NanoAttachment *attachment, + HttpBody *bodies, + HttpSessionData *session_data_p + ) +); + +CMOCK_MOCK_FUNCTION3( + NanoCompressionMocker, + nano_decompress_body, + HttpBody*( + NanoAttachment *attachment, + HttpBody *bodies, + HttpSessionData *session_data_p + ) +); + +CMOCK_MOCK_FUNCTION3( + NanoCompressionMocker, + nano_free_compressed_body, + void( + NanoAttachment *attachment, + HttpBody *bodies, + HttpSessionData *session_data_p + ) +); + +#endif // __MOCK_NANO_COMPRESSION_H__ diff --git a/attachments/nano_attachment/nano_attachment.c b/attachments/nano_attachment/nano_attachment.c index 4c1d85e..7c89ad7 100644 --- a/attachments/nano_attachment/nano_attachment.c +++ b/attachments/nano_attachment/nano_attachment.c @@ -15,17 +15,18 @@ #include "nano_utils.h" #include "attachment_types.h" #include "nano_blockpage.h" +#include "compression_utils.h" +#include "nano_compression.h" NanoAttachment * InitNanoAttachment(uint8_t attachment_type, int worker_id, int num_of_workers, int logging_fd) { - // NanoAttachment *attachment = malloc(sizeof(NanoAttachment)); - NanoAttachment *attachment = calloc(1, sizeof(NanoAttachment)); + NanoAttachment *attachment = malloc(sizeof(NanoAttachment)); if (attachment == NULL) { return NULL; } - // memset(attachment, 0, sizeof(NanoAttachment)); + memset(attachment, 0, sizeof(NanoAttachment)); attachment->shared_verdict_signal_path[0] = '\0'; attachment->worker_id = worker_id; @@ -141,6 +142,10 @@ InitSessionData(NanoAttachment *attachment, SessionID session_id) session_data->processed_req_body_size = 0; session_data->processed_res_body_size = 0; + session_data->response_data.compression_type = NO_COMPRESSION; + session_data->response_data.compression_stream = NULL; + session_data->response_data.decompression_stream = NULL; + return session_data; }; @@ -153,6 +158,16 @@ FiniSessionData(NanoAttachment *attachment, HttpSessionData *session_data) DBG_LEVEL_DEBUG, "Freeing session data for session_id" ); + + if (session_data->response_data.compression_stream != NULL) { + finiCompressionStream(session_data->response_data.compression_stream); + session_data->response_data.compression_stream = NULL; + } + + if (session_data->response_data.decompression_stream != NULL) { + finiCompressionStream(session_data->response_data.decompression_stream); + session_data->response_data.decompression_stream = NULL; + } free(session_data); }; @@ -208,13 +223,6 @@ SendDataNanoAttachment(NanoAttachment *attachment, AttachmentData *data) return response; } -// LCOV_EXCL_START Reason: Simple wrapper. -AttachmentVerdictResponse SendDataNanoAttachmentWrapper(NanoAttachment *attachment, AttachmentData data) -{ - return SendDataNanoAttachment(attachment, &data); -} -// LCOV_EXCL_STOP - /// /// @brief Connects to the keep-alive socket. /// @@ -587,3 +595,22 @@ FreeAttachmentResponseContent( return; } + +HttpBody * +compressBody(NanoAttachment *attachment, HttpSessionData *session_data, HttpBody *bodies) +{ + return nano_compress_body(attachment, bodies, session_data); +} + + +HttpBody * +decompressBody(NanoAttachment *attachment, HttpSessionData *session_data, HttpBody *bodies) +{ + return nano_decompress_body(attachment, bodies, session_data); +} + +void +freeCompressedBody(NanoAttachment *attachment, HttpSessionData *session_data, HttpBody *bodies) +{ + nano_free_compressed_body(attachment, bodies, session_data); +} diff --git a/attachments/nano_attachment/nano_attachment.h b/attachments/nano_attachment/nano_attachment.h deleted file mode 100644 index 5ef6788..0000000 --- a/attachments/nano_attachment/nano_attachment.h +++ /dev/null @@ -1,217 +0,0 @@ -#ifndef __NANO_ATTACHMENT_H__ -#define __NANO_ATTACHMENT_H__ - -#include "nano_attachment_common.h" -#include "nano_initializer.h" - -/// -/// @brief Initializes a NanoAttachment structure. -/// -/// This function initializes a NanoAttachment structure with the specified parameters and default values. -/// -/// @param attachment_type The type of attachment to initialize. -/// @param worker_id The ID of the worker associated with the attachment. -/// @param num_of_workers The total number of workers. -/// @param logging_fd The file descriptor for logging. -/// -/// @return A pointer to the initialized NanoAttachment structure if the function completes, NULL otherwise. -/// -NanoAttachment * InitNanoAttachment(uint8_t attachment_type, int worker_id, int num_of_workers, int logging_fd); - -/// -/// @brief Cleans up resources associated with a NanoAttachment structure and deallocates memory. -/// -/// This function performs cleanup operations on a NanoAttachment structure and deallocates -/// the memory associated with it. -/// The function closes the logging file descriptor associated with the NanoAttachment -/// and frees the memory allocated for the structure. -/// -/// @param attachment A pointer to the NanoAttachment structure to be cleaned up. -/// -void FiniNanoAttachment(NanoAttachment *attachment); - -/// -/// @brief Restarts the configuration of a NanoAttachment. -/// -/// @param attachment A pointer to the NanoAttachment whose configuration is to be restarted. -/// -/// @return A NanoCommunicationResult indicating the success or failure of the operation. - -NanoCommunicationResult RestartAttachmentConfiguration(NanoAttachment *attachment); - -/// -/// @brief Initializes a HttpSessionData structure with default values. -/// -/// This function dynamically allocates memory for a HttpSessionData structure -/// and initializes its fields with default values. -/// -/// @param attachment A pointer to the NanoAttachment structure associated with the session. -/// @param session_id The ID of the session to be initialized. -/// -/// @return A pointer to the initialized HttpSessionData structure if the function completes, NULL otherwise. -/// -HttpSessionData * InitSessionData(NanoAttachment *attachment, SessionID session_id); - -/// -/// @brief Cleans up and deallocates resources associated with a HttpSessionData structure. -/// -/// This function performs cleanup operations on a HttpSessionData structure and deallocates -/// the memory associated with it. It writes a debug message indicating the session ID being -/// freed, and then frees the memory allocated for the HttpSessionData structure. -/// -/// @param attachment A pointer to the NanoAttachment structure associated with the session. -/// @param session_data A pointer to the HttpSessionData structure to be cleaned up. -/// -void FiniSessionData(NanoAttachment *attachment, HttpSessionData *session_data); - -/// -/// @brief Updates a metric associated with a NanoAttachment. -/// -/// This function updates a metric associated with a NanoAttachment structure -/// based on the provided metric type and value. It delegates the actual updating -/// of the metric to the helper function updateMetricField. -/// -/// @param attachment A pointer to the NanoAttachment structure associated with the metric. -/// @param metric The type of metric to be updated. -/// @param value The value to be incorporated into the metric calculation. -/// -void UpdateMetric(NanoAttachment *attachment, AttachmentMetricType metric, uint64_t value); - -/// -/// @brief Sends metric data that been accumulated in the attachment to the service. -/// -/// @param attachment A pointer to the NanoAttachment structure associated with the metric. -/// -void SendAccumulatedMetricData(NanoAttachment *attachment); - -/// -/// @brief Processes and sends attachment data to the appropriate handlers. -/// -/// This function processes the attachment data based on its chunk type and sends -/// it to the appropriate handler functions. If the chunk type is not recognized, -/// it sets a default verdict of ATTACHMENT_VERDICT_INSPECT and returns an AttachmentVerdictResponse -/// structure containing the default verdict and the session ID from the provided AttachmentData. -/// -/// @param attachment A pointer to the NanoAttachment structure associated with the data. -/// @param data A pointer to the AttachmentData structure containing the data to be processed. -/// -/// @return An AttachmentVerdictResponse structure containing the verdict and session ID. -/// -AttachmentVerdictResponse SendDataNanoAttachment(NanoAttachment *attachment, AttachmentData *data); - -AttachmentVerdictResponse SendDataNanoAttachmentWrapper(NanoAttachment *attachment, AttachmentData data); - -/// -/// @brief Sends a keep-alive signal using a socket connection. -/// -/// @param attachment A pointer to a NanoAttachment struct containing attachment information. -/// -void SendKeepAlive(NanoAttachment *attachment); - -/// -/// @brief Checks if a session is finalized based on the session's verdict. -/// -/// @param attachment The NanoAttachment object associated with the session. -/// @param session_data The HttpSessionData object representing the session. -/// -/// @return Returns 0 if the session is not finalized, 1 otherwise. -/// -int IsSessionFinalized(NanoAttachment *attachment, HttpSessionData *session_data); - -/// -/// @brief Checks if the response contains modifications. -/// -/// This function determines whether the provided response contains modifications. -/// -/// @param attachment A pointer to a NanoAttachment structure representing the attachment. -/// @param session_data A pointer to a HttpSessionData structure containing session data. -/// @param response A pointer to an AttachmentVerdictResponse structure representing the response. -/// -/// @return 1 if the response contains modifications, 0 otherwise. -/// -int IsResponseWithModification( - NanoAttachment *attachment, - HttpSessionData *session_data, - AttachmentVerdictResponse *response -); - -/// -/// @brief Retrieves response modifications from the given attachment and session data. -/// -/// @param attachment Pointer to a NanoAttachment object. -/// @param session_data Pointer to HttpSessionData object containing session information. -/// @param response Pointer to an AttachmentVerdictResponse object. -/// -/// @return NanoResponseModifications structure containing response modifications. -/// -NanoResponseModifications GetResponseModifications( - NanoAttachment *attachment, - HttpSessionData *session_data, - AttachmentVerdictResponse *response -); - -/// -/// @brief Retrieves the type of web response associated with the given attachment and session data. -/// -/// This function checks if the provided response object contains valid web response data. -/// If the response object is null, it logs a warning and returns NO_WEB_RESPONSE. -/// Otherwise, it returns the type of web response contained in the response object. -/// -/// @param attachment Pointer to the NanoAttachment structure associated with the request. -/// @param session_data Pointer to the HttpSessionData structure containing session-related data. -/// @param response Pointer to the AttachmentVerdictResponse structure containing response data. -/// -/// @return The type of web response, or NO_WEB_RESPONSE if no response object is provided. -/// -NanoWebResponseType GetWebResponseType( - NanoAttachment *attachment, - HttpSessionData *session_data, - AttachmentVerdictResponse *response -); - -/// -/// @brief Retrieves the block page data for a response. -/// -/// @param attachment The NanoAttachment object associated with the session. -/// @param session_data The HttpSessionData object representing the session. -/// @param response The AttachmentVerdictResponse object containing the verdict. -/// -/// @return -/// -BlockPageData GetBlockPage( - NanoAttachment *attachment, - HttpSessionData *session_data, - AttachmentVerdictResponse *response -); - -/// -/// @brief Retrieves the redict page data for a response. -/// -/// @param attachment The NanoAttachment object associated with the session. -/// @param session_data The HttpSessionData object representing the session. -/// @param response The AttachmentVerdictResponse object containing the verdict. -/// -/// @return -/// -RedirectPageData GetRedirectPage( - NanoAttachment *attachment, - HttpSessionData *session_data, - AttachmentVerdictResponse *response -); - -/// -/// @brief Free allocated resources of an AttachmentVerdictResponse. -/// -/// This function frees the allocated resources of an AttachmentVerdictResponse. -/// -/// @param attachment The NanoAttachment object associated with the session. -/// @param session_data The HttpSessionData object representing the session. -/// @param response The AttachmentVerdictResponse object to be freed. -/// -void FreeAttachmentResponseContent( - NanoAttachment *attachment, - HttpSessionData *session_data, - AttachmentVerdictResponse *response -); - -#endif // __NANO_ATTACHMENT_H__ diff --git a/attachments/nano_attachment/nano_attachment_io.c b/attachments/nano_attachment/nano_attachment_io.c index 61c8ec7..e2bf1ef 100644 --- a/attachments/nano_attachment/nano_attachment_io.c +++ b/attachments/nano_attachment/nano_attachment_io.c @@ -50,6 +50,7 @@ notify_signal_to_service(NanoAttachment *attachment, uint32_t cur_session_id) s_poll.fd = attachment->comm_socket; s_poll.events = POLLOUT; + s_poll.revents = 0; res = poll(&s_poll, 1, 0); if (res > 0 && s_poll.revents & POLLHUP) { write_dbg( @@ -470,7 +471,7 @@ create_modification_node(NanoAttachment *attachment, SessionID session_id, HttpI "Injection position: %d, " "Injection size: %d, " "Original buffer index: %d, " - "Data: %s, " + "Modification data: %s, " "Should change data: %d", modification_node->modification.is_header, modification_node->modification.injection_pos, @@ -513,6 +514,15 @@ handle_inject_response( for (modification_index = 0; modification_index < modification_count; modification_index++) { // Go over the modifications and create nodes. new_modification = create_modification_node(attachment, session_id, inject_data); + write_dbg( + attachment, + session_id, + DBG_LEVEL_DEBUG, + "create modification node %d out of %d", + modification_index, + modification_count + ); + if (new_modification == NULL) { write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Failed to create modification node"); while (*modification_list) { @@ -527,6 +537,10 @@ handle_inject_response( *modification_list = new_modification; current_modification = *modification_list; } else { + while (*modification_list) { + current_modification = *modification_list; + *modification_list = (*modification_list)->next; + } current_modification->next = new_modification; current_modification = current_modification->next; } diff --git a/attachments/nano_attachment/nano_attachment_sender_thread.c b/attachments/nano_attachment/nano_attachment_sender_thread.c index 9efdc98..1f573ec 100644 --- a/attachments/nano_attachment/nano_attachment_sender_thread.c +++ b/attachments/nano_attachment/nano_attachment_sender_thread.c @@ -1,9 +1,64 @@ #include "nano_attachment_sender_thread.h" +#include +#include + #include "nano_initializer.h" #include "nano_attachment_sender.h" #include "nano_attachment_common.h" #include "nano_attachment_io.h" +#include "nano_utils.h" +#include "nano_compression.h" + +static HttpHeaderData * +get_http_header(HttpHeaders *http_headers, const char *header_name) { + size_t i; + for (i = 0; i < http_headers->headers_count; ++i) { + if (strcmp((char*)http_headers->data[i].key.data, header_name) == 0) { + return &http_headers->data[i]; + } + } + return NULL; +} + +static void +set_response_content_encoding( + NanoAttachment *attachment, + HttpSessionData *session_data_p, + HttpHeaders *http_headers +) +{ + write_dbg( + attachment, + session_data_p->session_id, + DBG_LEVEL_TRACE, + "Determining response body's content encoding" + ); + + const HttpHeaderData *content_encoding = get_http_header(http_headers, "content-encoding"); + + if (content_encoding == NULL) { + session_data_p->response_data.compression_type = NO_COMPRESSION; + return; + } + + if (strcmp((char*)content_encoding->value.data, "gzip") == 0) { + session_data_p->response_data.compression_type = GZIP; + } else if (strcmp((char*)content_encoding->value.data, "deflate") == 0) { + session_data_p->response_data.compression_type = ZLIB; + } else if (strcmp((char*)content_encoding->value.data, "identity") == 0) { + session_data_p->response_data.compression_type = NO_COMPRESSION; + } else { + write_dbg( + attachment, + session_data_p->session_id, + DBG_LEVEL_WARNING, + "Unsupported response content encoding: %.*s", + content_encoding->value.data + ); + session_data_p->response_data.compression_type = NO_COMPRESSION; + } +} void init_thread_ctx(HttpEventThreadCtx *ctx, NanoAttachment *attachment, AttachmentData *data) @@ -133,7 +188,7 @@ SendResponseHeadersThread(void *_ctx) NanoAttachment *attachment = ctx->attachment; HttpSessionData *session_data_p = ctx->session_data_p; HttpHeaders *http_headers = headers->headers; - bool is_verdict_requested = false; + bool is_verdict_requested = true; nano_send_response_code( attachment, @@ -151,6 +206,12 @@ SendResponseHeadersThread(void *_ctx) &session_data_p->remaining_messages_to_reply ); + set_response_content_encoding( + attachment, + session_data_p, + http_headers + ); + nano_header_sender( attachment, http_headers, diff --git a/attachments/nano_attachment/nano_attachment_ut/CMakeLists.txt b/attachments/nano_attachment/nano_attachment_ut/CMakeLists.txt index 2ce1d96..6dc7e70 100644 --- a/attachments/nano_attachment/nano_attachment_ut/CMakeLists.txt +++ b/attachments/nano_attachment/nano_attachment_ut/CMakeLists.txt @@ -49,3 +49,9 @@ add_unit_test( "nano_attachment_metrics_ut.cc" "nano_attachment" ) + +add_unit_test( + nano_compression_ut + "nano_compression_ut.cc" + "nano_attachment" +) diff --git a/attachments/nano_attachment/nano_attachment_ut/nano_attachment_io_ut.cc b/attachments/nano_attachment/nano_attachment_ut/nano_attachment_io_ut.cc index 85091ee..158180d 100644 --- a/attachments/nano_attachment/nano_attachment_ut/nano_attachment_io_ut.cc +++ b/attachments/nano_attachment/nano_attachment_ut/nano_attachment_io_ut.cc @@ -25,7 +25,6 @@ public: initializer_mocker, nano_attachment_init_process(_)).WillOnce(Return(NanoCommunicationResult::NANO_OK) ); - setenv("CLOUDGUARD_UID", "Testing", 1); attachment = InitNanoAttachment( static_cast(AttachmentType::NGINX_ATT_ID), 2, @@ -67,6 +66,10 @@ public: free(reply_from_service_mock); FiniSessionData(attachment, session_data); FiniNanoAttachment(attachment); + ::testing::Mock::VerifyAndClearExpectations(&initializer_mocker); + ::testing::Mock::VerifyAndClearExpectations(&mock_shmem_ipc); + ::testing::Mock::VerifyAndClearExpectations(&mock_nano_socket); + ::testing::Mock::VerifyAndClearExpectations(&mock_nano_poll); } nano_str_t @@ -156,6 +159,8 @@ public: HttpWebResponseData *web_response_data; uint32_t reply_session_id = -1; void *reply_session_id_void = &reply_session_id; + struct pollfd fds; + struct pollfd *mock_fds = &fds; const char **replay_data_mock; NanoAttachment *attachment; @@ -421,7 +426,17 @@ TEST_F(NanoAttachmentIoTest, NanoBodySender) EXPECT_CALL( mock_nano_poll, poll(_, _, _) - ).WillRepeatedly(Return(1)); + ).WillRepeatedly( + DoAll( + SaveArg<0>(&mock_fds), + InvokeWithoutArgs( + [&] () { + mock_fds[0].revents = POLLIN; + } + ), + Return(1) + ) + ); EXPECT_CALL( mock_nano_socket, diff --git a/attachments/nano_attachment/nano_attachment_ut/nano_attachment_metrics_ut.cc b/attachments/nano_attachment/nano_attachment_ut/nano_attachment_metrics_ut.cc index 6d2f6ff..783497f 100644 --- a/attachments/nano_attachment/nano_attachment_ut/nano_attachment_metrics_ut.cc +++ b/attachments/nano_attachment/nano_attachment_ut/nano_attachment_metrics_ut.cc @@ -24,7 +24,6 @@ public: ).WillOnce( Return(NanoCommunicationResult::NANO_OK) ); - setenv("CLOUDGUARD_UID", "Testing", 1); attachment = InitNanoAttachment( static_cast(AttachmentType::NGINX_ATT_ID), 2, @@ -32,6 +31,7 @@ public: STDOUT_FILENO ); EXPECT_NE(attachment, nullptr); + } void diff --git a/attachments/nano_attachment/nano_attachment_ut/nano_attachment_sender_ut.cc b/attachments/nano_attachment/nano_attachment_ut/nano_attachment_sender_ut.cc index a6ab1e0..17f9f40 100644 --- a/attachments/nano_attachment/nano_attachment_ut/nano_attachment_sender_ut.cc +++ b/attachments/nano_attachment/nano_attachment_ut/nano_attachment_sender_ut.cc @@ -26,7 +26,6 @@ public: ).WillOnce( Return(NanoCommunicationResult::NANO_OK) ); - setenv("CLOUDGUARD_UID", "Testing", 1); attachment = InitNanoAttachment( static_cast(AttachmentType::NGINX_ATT_ID), 2, diff --git a/attachments/nano_attachment/nano_attachment_ut/nano_attachment_ut.cc b/attachments/nano_attachment/nano_attachment_ut/nano_attachment_ut.cc index 441f49c..9773827 100644 --- a/attachments/nano_attachment/nano_attachment_ut/nano_attachment_ut.cc +++ b/attachments/nano_attachment/nano_attachment_ut/nano_attachment_ut.cc @@ -6,6 +6,7 @@ #include "mock_nano_initializer.h" #include "mock_nano_attachment_sender.h" #include "mock_nano_configuration.h" +#include "mock_nano_compression.h" extern "C" { #include "nano_attachment.h" @@ -26,7 +27,6 @@ public: ).WillOnce( Return(NanoCommunicationResult::NANO_OK) ); - setenv("CLOUDGUARD_UID", "Testing", 1); attachment = InitNanoAttachment( static_cast(AttachmentType::NGINX_ATT_ID), 2, @@ -199,6 +199,7 @@ public: StrictMock initializer_mocker; StrictMock socket_mocker; StrictMock configuration_mocker; + StrictMock compression_mocker; }; TEST_F(NanoAttachmentTest, InitNanoAttachment) @@ -450,3 +451,32 @@ TEST_F(NanoAttachmentTest, SendAccumulatedMetricData) EXPECT_CALL(sender_mocker, SendMetricData(attachment)).WillOnce(Return(NanoCommunicationResult::NANO_OK)); SendAccumulatedMetricData(attachment); } + +TEST_F(NanoAttachmentTest, CompressData) +{ + EXPECT_CALL( + compression_mocker, + nano_compress_body(attachment, &http_body_data, session_data) + ).WillOnce(Return(nullptr) + ); + compressBody(attachment, session_data, &http_body_data); +} + +TEST_F(NanoAttachmentTest, DecompressData) +{ + EXPECT_CALL( + compression_mocker, + nano_decompress_body(attachment, &http_body_data, session_data) + ).WillOnce(Return(nullptr) + ); + decompressBody(attachment, session_data, &http_body_data); +} + +TEST_F(NanoAttachmentTest, FreeCompressedData) +{ + EXPECT_CALL( + compression_mocker, + nano_free_compressed_body(attachment, &http_body_data, session_data) + ); + freeCompressedBody(attachment, session_data, &http_body_data); +} diff --git a/attachments/nano_attachment/nano_attachment_ut/nano_compression_ut.cc b/attachments/nano_attachment/nano_attachment_ut/nano_compression_ut.cc new file mode 100755 index 0000000..d6782dd --- /dev/null +++ b/attachments/nano_attachment/nano_attachment_ut/nano_compression_ut.cc @@ -0,0 +1,105 @@ +#include "cptest.h" +#include "nano_attachment_common.h" +#include "attachment_types.h" +#include "compression_utils.h" + +#include "mock_nano_socket.h" +#include "mock_nano_initializer.h" +#include "mock_nano_attachment_sender.h" +#include "mock_nano_configuration.h" +#include "mock_nano_compression.h" + +extern "C" { +#include "nano_attachment.h" +#include "nano_compression.h" +} + +using namespace std; +using namespace testing; + +class NanoAttachmentTest : public Test +{ +public: + void + SetUp() override + { + EXPECT_CALL( + initializer_mocker, + nano_attachment_init_process(_) + ).WillOnce( + Return(NanoCommunicationResult::NANO_OK) + ); + attachment = InitNanoAttachment( + static_cast(AttachmentType::NGINX_ATT_ID), + 2, + 4, + STDOUT_FILENO + ); + EXPECT_NE(attachment, nullptr); + + session_data = InitSessionData(attachment, 1); + EXPECT_NE(session_data, nullptr); + } + + void + TearDown() override + { + FiniSessionData(attachment, session_data); + FiniNanoAttachment(attachment); + } + + nano_str_t + create_nano_str(const char *str) + { + nano_str_t nano_str; + nano_str.data = reinterpret_cast(const_cast(str)); + nano_str.len = strlen(str); + return nano_str; + } + + nano_str_t body[3] = { + create_nano_str("Hello"), + create_nano_str("World"), + create_nano_str("!") + }; + + HttpBody http_body_data = { + body, + 3 + }; + + AttachmentData req_body_data = { + 1, + HttpChunkType::HTTP_REQUEST_BODY, + session_data, + (DataBuffer)&http_body_data + }; + + NanoAttachment *attachment; + HttpSessionData *session_data; + StrictMock initializer_mocker; +}; + +TEST_F(NanoAttachmentTest, CompressData) +{ + session_data->response_data.compression_type = CompressionType::GZIP; + + HttpBody * compressed_body_data = nullptr; + HttpBody * decompressed_body_data = nullptr; + + compressed_body_data = nano_compress_body(attachment, &http_body_data, session_data); + EXPECT_EQ(compressed_body_data->bodies_count, 3u); + + decompressed_body_data = nano_decompress_body(attachment, compressed_body_data, session_data); + EXPECT_EQ(decompressed_body_data->bodies_count, 3u); + EXPECT_EQ(decompressed_body_data->data[0].len, 5u); + EXPECT_EQ(decompressed_body_data->data[1].len, 5u); + EXPECT_EQ(decompressed_body_data->data[2].len, 1u); + + EXPECT_EQ(strncmp((char *)decompressed_body_data->data[0].data, "Hello", decompressed_body_data->data[0].len), 0); + EXPECT_EQ(strncmp((char *)decompressed_body_data->data[1].data, "World", decompressed_body_data->data[1].len), 0); + EXPECT_EQ(strncmp((char *)decompressed_body_data->data[2].data, "!", decompressed_body_data->data[2].len), 0); + + nano_free_compressed_body(attachment, compressed_body_data, session_data); + nano_free_compressed_body(attachment, decompressed_body_data, session_data); +} diff --git a/attachments/nano_attachment/nano_attachment_ut/nano_configuration_ut.cc b/attachments/nano_attachment/nano_attachment_ut/nano_configuration_ut.cc index 5a5928a..26b6d2e 100644 --- a/attachments/nano_attachment/nano_attachment_ut/nano_configuration_ut.cc +++ b/attachments/nano_attachment/nano_attachment_ut/nano_configuration_ut.cc @@ -90,7 +90,6 @@ TEST_F(NanoConfigurationTest, InitAttachmentConfiguration) res = set_logging_fd(&attachment, STDOUT_FILENO); EXPECT_EQ(res, NanoCommunicationResult::NANO_OK); - setenv("CLOUDGUARD_UID", "Testing", 1); res = set_docker_id(&attachment); EXPECT_EQ(res, NanoCommunicationResult::NANO_OK); diff --git a/attachments/nano_attachment/nano_attachment_ut/nano_sender_thread_ut.cc b/attachments/nano_attachment/nano_attachment_ut/nano_sender_thread_ut.cc index dcdbad5..ad21fcc 100644 --- a/attachments/nano_attachment/nano_attachment_ut/nano_sender_thread_ut.cc +++ b/attachments/nano_attachment/nano_attachment_ut/nano_sender_thread_ut.cc @@ -25,7 +25,6 @@ public: ).WillOnce( Return(NanoCommunicationResult::NANO_OK) ); - setenv("CLOUDGUARD_UID", "Testing", 1); attachment = InitNanoAttachment( static_cast(AttachmentType::NGINX_ATT_ID), 2, @@ -77,7 +76,7 @@ public: create_nano_str("/dogs.html") }; - HttpHeaderData http_headers[3] = { + HttpHeaderData http_headers[4] = { { create_nano_str("Host"), create_nano_str("www.nanoattachmentut.com") @@ -89,12 +88,16 @@ public: { create_nano_str("Accept"), create_nano_str("text/html") + }, + { + create_nano_str("content-encoding"), + create_nano_str("gzip") } }; HttpHeaders http_headers_data = { http_headers, - 3 + 4 }; HttpRequestFilterData request_filter_data = { @@ -348,12 +351,14 @@ TEST_F(NanoAttachmentSenderThreadTest, SendResponseHeadersThread) AttachmentDataType::RESPONSE_HEADER, session_data->session_id, &session_data->remaining_messages_to_reply, - false + true ) ); init_thread_ctx(&ctx, attachment, &res_header_data); SendResponseHeadersThread(&ctx); + + EXPECT_EQ(session_data->response_data.compression_type, CompressionType::GZIP); } TEST_F(NanoAttachmentSenderThreadTest, SendRequestBodyThread) diff --git a/attachments/nano_attachment/nano_attachment_ut/nano_thread_ut.cc b/attachments/nano_attachment/nano_attachment_ut/nano_thread_ut.cc index af46cd3..e4fa09e 100644 --- a/attachments/nano_attachment/nano_attachment_ut/nano_thread_ut.cc +++ b/attachments/nano_attachment/nano_attachment_ut/nano_thread_ut.cc @@ -27,7 +27,6 @@ public: ).WillOnce( Return(NanoCommunicationResult::NANO_OK) ); - setenv("CLOUDGUARD_UID", "Testing", 1); attachment = InitNanoAttachment( static_cast(AttachmentType::NGINX_ATT_ID), 2, diff --git a/attachments/nano_attachment/nano_attachment_util/CMakeLists.txt b/attachments/nano_attachment/nano_attachment_util/CMakeLists.txt index daa19ee..ee82f89 100644 --- a/attachments/nano_attachment/nano_attachment_util/CMakeLists.txt +++ b/attachments/nano_attachment/nano_attachment_util/CMakeLists.txt @@ -1,3 +1,7 @@ +include_directories(${Boost_INCLUDE_DIRS}) + +link_directories(${BOOST_ROOT}/lib) + add_definitions(-DUSERSPACE) add_library(nano_attachment_util SHARED nano_attachment_util.cc) target_link_libraries(nano_attachment_util http_configuration) diff --git a/attachments/nano_attachment/nano_compression.c b/attachments/nano_attachment/nano_compression.c new file mode 100755 index 0000000..75a35b3 --- /dev/null +++ b/attachments/nano_attachment/nano_compression.c @@ -0,0 +1,130 @@ +#include "nano_compression.h" + +#include + +#include "nano_attachment_common.h" +#include "nano_initializer.h" +#include "compression_utils.h" +#include "nano_utils.h" + +HttpBody * +nano_compress_body( + NanoAttachment *attachment, + HttpBody *bodies, + HttpSessionData *session_data_p +) +{ + CompressionResult compression_result; + HttpBody *compressed_body; + size_t i; + + if (session_data_p->response_data.compression_type == NO_COMPRESSION) { + return NULL; + } + write_dbg( + attachment, + session_data_p->session_id, + DBG_LEVEL_TRACE, + "Compressing body" + ); + + if (session_data_p->response_data.compression_stream == NULL) { + session_data_p->response_data.compression_stream = initCompressionStream(); + } + + compressed_body = malloc(sizeof(HttpBody)); + if (compressed_body == NULL) { + return NULL; + } + + compressed_body->bodies_count = bodies->bodies_count; + compressed_body->data = malloc(bodies->bodies_count * sizeof(nano_str_t)); + if (compressed_body->data == NULL) { + free(compressed_body); + return NULL; + } + + for (i = 0; i < bodies->bodies_count; ++i) { + compression_result = compressData( + session_data_p->response_data.compression_stream, + session_data_p->response_data.compression_type, + bodies->data[i].len, + bodies->data[i].data, + i == bodies->bodies_count - 1 + ); + compressed_body->data[i].len = compression_result.num_output_bytes; + compressed_body->data[i].data = compression_result.output; + } + + return compressed_body; +} + +HttpBody * +nano_decompress_body( + NanoAttachment *attachment, + HttpBody *bodies, + HttpSessionData *session_data_p +) +{ + DecompressionResult decompression_result; + HttpBody *decompressed_body; + size_t i; + + if (session_data_p->response_data.compression_type == NO_COMPRESSION) { + return NULL; + } + write_dbg( + attachment, + session_data_p->session_id, + DBG_LEVEL_TRACE, + "Decompressing body" + ); + + if (session_data_p->response_data.decompression_stream == NULL) { + session_data_p->response_data.decompression_stream = initCompressionStream(); + } + + decompressed_body = malloc(sizeof(HttpBody)); + if (decompressed_body == NULL) { + return NULL; + } + + decompressed_body->bodies_count = bodies->bodies_count; + decompressed_body->data = malloc(bodies->bodies_count * sizeof(nano_str_t)); + if (decompressed_body->data == NULL) { + free(decompressed_body); + return NULL; + } + + for (i = 0; i < bodies->bodies_count; ++i) { + decompression_result = decompressData( + session_data_p->response_data.decompression_stream, + bodies->data[i].len, + bodies->data[i].data + ); + decompressed_body->data[i].len = decompression_result.num_output_bytes; + decompressed_body->data[i].data = decompression_result.output; + } + + return decompressed_body; +} + +void +nano_free_compressed_body( + NanoAttachment *attachment, + HttpBody *bodies, + HttpSessionData *session_data_p +) +{ + if (bodies == NULL) { + return; + } + write_dbg( + attachment, + session_data_p->session_id, + DBG_LEVEL_TRACE, + "Freeing compressed body" + ); + free(bodies->data); + free(bodies); +} diff --git a/attachments/nano_attachment/nano_compression.h b/attachments/nano_attachment/nano_compression.h new file mode 100755 index 0000000..5108b35 --- /dev/null +++ b/attachments/nano_attachment/nano_compression.h @@ -0,0 +1,45 @@ +#ifndef __NANO_COMPRESSION_H__ +#define __NANO_COMPRESSION_H__ + +#include "nano_attachment_sender_thread.h" + +/// @brief Compresses the given HTTP body using the specified compression type in the session data. +/// +/// @param attachment Pointer to the NanoAttachment structure. +/// @param bodies Pointer to the HttpBody structure containing the data to be compressed. +/// @param session_data_p Pointer to the HttpSessionData structure containing session-specific data. +/// +/// @return Pointer to a new HttpBody structure containing the compressed data, +/// or NULL if compression is not needed or fails. +HttpBody *nano_compress_body( + NanoAttachment *attachment, + HttpBody *bodies, + HttpSessionData *session_data_p +); + +/// @brief Decompresses the given HTTP body using the specified compression type in the session data. +/// +/// @param attachment Pointer to the NanoAttachment structure. +/// @param bodies Pointer to the HttpBody structure containing the data to be decompressed. +/// @param session_data_p Pointer to the HttpSessionData structure containing session-specific data. +/// +/// @return Pointer to a new HttpBody structure containing the decompressed data, +/// or NULL if decompression is not needed or fails. +HttpBody *nano_decompress_body( + NanoAttachment *attachment, + HttpBody *bodies, + HttpSessionData *session_data_p +); + +/// @brief Frees the memory allocated for the compressed HTTP body. +/// +/// @param attachment Pointer to the NanoAttachment structure. +/// @param bodies Pointer to the HttpBody structure containing the compressed data to be freed. +/// @param session_data_p Pointer to the HttpSessionData structure containing session-specific data. +void nano_free_compressed_body( + NanoAttachment *attachment, + HttpBody *bodies, + HttpSessionData *session_data_p +); + +#endif // __NANO_COMPRESSION_H__ diff --git a/attachments/nano_attachment/nano_initializer.c b/attachments/nano_attachment/nano_initializer.c index c1f4fcc..eaab6a0 100644 --- a/attachments/nano_attachment/nano_initializer.c +++ b/attachments/nano_attachment/nano_initializer.c @@ -400,7 +400,6 @@ set_docker_id(NanoAttachment *attachment) if (!uid_read) { write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Severe error - failed to get uid!"); - return NANO_ERROR; } return NANO_OK; diff --git a/core/shmem_ipc_2/CMakeLists.txt b/core/shmem_ipc_2/CMakeLists.txt index 9620909..412ca69 100755 --- a/core/shmem_ipc_2/CMakeLists.txt +++ b/core/shmem_ipc_2/CMakeLists.txt @@ -1,3 +1,5 @@ +include_directories(${Boost_INCLUDE_DIRS}) + add_library(shmem_ipc_2 SHARED shmem_ipc.c shared_ring_queue.c) target_link_libraries(shmem_ipc_2 -lrt)