diff --git a/library/objective-c/EnvoyEngineImpl.m b/library/objective-c/EnvoyEngineImpl.m index e199b3a732..592f93ebcf 100644 --- a/library/objective-c/EnvoyEngineImpl.m +++ b/library/objective-c/EnvoyEngineImpl.m @@ -10,277 +10,342 @@ #import static void ios_on_engine_running(void *context) { - EnvoyEngineImpl *engineImpl = (__bridge EnvoyEngineImpl *)context; - if (engineImpl.onEngineRunning) { - engineImpl.onEngineRunning(); + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block + // is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + EnvoyEngineImpl *engineImpl = (__bridge EnvoyEngineImpl *)context; + if (engineImpl.onEngineRunning) { + engineImpl.onEngineRunning(); + } } } -static void ios_on_exit(void *context) { NSLog(@"[Envoy] library is exiting"); } +static void ios_on_exit(void *context) { + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block + // is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + NSLog(@"[Envoy] library is exiting"); + } +} static void ios_on_log(envoy_data data, void *context) { - EnvoyLogger *logger = (__bridge EnvoyLogger *)context; - logger.log(to_ios_string(data)); + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block + // is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + EnvoyLogger *logger = (__bridge EnvoyLogger *)context; + logger.log(to_ios_string(data)); + } } static void ios_on_logger_release(void *context) { CFRelease(context); } static const void *ios_http_filter_init(const void *context) { - envoy_http_filter *c_filter = (envoy_http_filter *)context; - EnvoyHTTPFilterFactory *filterFactory = - (__bridge EnvoyHTTPFilterFactory *)c_filter->static_context; - EnvoyHTTPFilter *filter = filterFactory.create(); - - // Unset static functions on the c_struct based on the created filter - if (filter.onRequestHeaders == nil) { - c_filter->on_request_headers = NULL; - } - if (filter.onRequestData == nil) { - c_filter->on_request_data = NULL; - } - if (filter.onRequestTrailers == nil) { - c_filter->on_request_trailers = NULL; - } + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block + // is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + envoy_http_filter *c_filter = (envoy_http_filter *)context; - if (filter.onResponseHeaders == nil) { - c_filter->on_response_headers = NULL; - } - if (filter.onResponseData == nil) { - c_filter->on_response_data = NULL; - } - if (filter.onResponseTrailers == nil) { - c_filter->on_response_trailers = NULL; - } + EnvoyHTTPFilterFactory *filterFactory = + (__bridge EnvoyHTTPFilterFactory *)c_filter->static_context; + EnvoyHTTPFilter *filter = filterFactory.create(); - if (filter.setRequestFilterCallbacks == nil) { - c_filter->set_request_callbacks = NULL; - } - if (filter.onResumeRequest == nil) { - c_filter->on_resume_request = NULL; - } + // Unset static functions on the c_struct based on the created filter + if (filter.onRequestHeaders == nil) { + c_filter->on_request_headers = NULL; + } + if (filter.onRequestData == nil) { + c_filter->on_request_data = NULL; + } + if (filter.onRequestTrailers == nil) { + c_filter->on_request_trailers = NULL; + } - if (filter.setResponseFilterCallbacks == nil) { - c_filter->set_response_callbacks = NULL; - } - if (filter.onResumeResponse == nil) { - c_filter->on_resume_response = NULL; - } + if (filter.onResponseHeaders == nil) { + c_filter->on_response_headers = NULL; + } + if (filter.onResponseData == nil) { + c_filter->on_response_data = NULL; + } + if (filter.onResponseTrailers == nil) { + c_filter->on_response_trailers = NULL; + } - if (filter.onCancel == nil) { - c_filter->on_cancel = NULL; - } - if (filter.onError == nil) { - c_filter->on_error = NULL; - } + if (filter.setRequestFilterCallbacks == nil) { + c_filter->set_request_callbacks = NULL; + } + if (filter.onResumeRequest == nil) { + c_filter->on_resume_request = NULL; + } + + if (filter.setResponseFilterCallbacks == nil) { + c_filter->set_response_callbacks = NULL; + } + if (filter.onResumeResponse == nil) { + c_filter->on_resume_response = NULL; + } - return CFBridgingRetain(filter); + if (filter.onCancel == nil) { + c_filter->on_cancel = NULL; + } + if (filter.onError == nil) { + c_filter->on_error = NULL; + } + + return CFBridgingRetain(filter); + } } static envoy_filter_headers_status ios_http_filter_on_request_headers(envoy_headers headers, bool end_stream, const void *context) { - // TODO(goaway): optimize unmodified case - EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; - if (filter.onRequestHeaders == nil) { - return (envoy_filter_headers_status){/*status*/ kEnvoyFilterHeadersStatusContinue, - /*headers*/ headers}; - } + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block + // is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + // TODO(goaway): optimize unmodified case + EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; + if (filter.onRequestHeaders == nil) { + return (envoy_filter_headers_status){/*status*/ kEnvoyFilterHeadersStatusContinue, + /*headers*/ headers}; + } - EnvoyHeaders *platformHeaders = to_ios_headers(headers); - // TODO(goaway): consider better solution for compound return - NSArray *result = filter.onRequestHeaders(platformHeaders, end_stream); - return (envoy_filter_headers_status){/*status*/ [result[0] intValue], - /*headers*/ toNativeHeaders(result[1])}; + EnvoyHeaders *platformHeaders = to_ios_headers(headers); + // TODO(goaway): consider better solution for compound return + NSArray *result = filter.onRequestHeaders(platformHeaders, end_stream); + return (envoy_filter_headers_status){/*status*/ [result[0] intValue], + /*headers*/ toNativeHeaders(result[1])}; + } } static envoy_filter_headers_status ios_http_filter_on_response_headers(envoy_headers headers, bool end_stream, const void *context) { - // TODO(goaway): optimize unmodified case - EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; - if (filter.onResponseHeaders == nil) { - return (envoy_filter_headers_status){/*status*/ kEnvoyFilterHeadersStatusContinue, - /*headers*/ headers}; - } + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block + // is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + // TODO(goaway): optimize unmodified case + EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; + if (filter.onResponseHeaders == nil) { + return (envoy_filter_headers_status){/*status*/ kEnvoyFilterHeadersStatusContinue, + /*headers*/ headers}; + } - EnvoyHeaders *platformHeaders = to_ios_headers(headers); - NSArray *result = filter.onResponseHeaders(platformHeaders, end_stream); - return (envoy_filter_headers_status){/*status*/ [result[0] intValue], - /*headers*/ toNativeHeaders(result[1])}; + EnvoyHeaders *platformHeaders = to_ios_headers(headers); + NSArray *result = filter.onResponseHeaders(platformHeaders, end_stream); + return (envoy_filter_headers_status){/*status*/ [result[0] intValue], + /*headers*/ toNativeHeaders(result[1])}; + } } static envoy_filter_data_status ios_http_filter_on_request_data(envoy_data data, bool end_stream, const void *context) { - EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; - if (filter.onRequestData == nil) { - return (envoy_filter_data_status){/*status*/ kEnvoyFilterDataStatusContinue, - /*data*/ data, - /*pending_headers*/ NULL}; - } + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block + // is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; + if (filter.onRequestData == nil) { + return (envoy_filter_data_status){/*status*/ kEnvoyFilterDataStatusContinue, + /*data*/ data, + /*pending_headers*/ NULL}; + } - NSData *platformData = to_ios_data(data); - NSArray *result = filter.onRequestData(platformData, end_stream); - // Result is typically a pair of status and entity, but uniquely in the case of - // ResumeIteration it will (optionally) contain additional pending elements. - envoy_headers *pending_headers = toNativeHeadersPtr(result.count == 3 ? result[2] : nil); - return (envoy_filter_data_status){/*status*/ [result[0] intValue], - /*data*/ toNativeData(result[1]), - /*pending_headers*/ pending_headers}; + NSData *platformData = to_ios_data(data); + NSArray *result = filter.onRequestData(platformData, end_stream); + // Result is typically a pair of status and entity, but uniquely in the case of + // ResumeIteration it will (optionally) contain additional pending elements. + envoy_headers *pending_headers = toNativeHeadersPtr(result.count == 3 ? result[2] : nil); + return (envoy_filter_data_status){/*status*/ [result[0] intValue], + /*data*/ toNativeData(result[1]), + /*pending_headers*/ pending_headers}; + } } static envoy_filter_data_status ios_http_filter_on_response_data(envoy_data data, bool end_stream, const void *context) { - EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; - if (filter.onResponseData == nil) { - return (envoy_filter_data_status){/*status*/ kEnvoyFilterDataStatusContinue, - /*data*/ data, - /*pending_headers*/ NULL}; - } + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block + // is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; + if (filter.onResponseData == nil) { + return (envoy_filter_data_status){/*status*/ kEnvoyFilterDataStatusContinue, + /*data*/ data, + /*pending_headers*/ NULL}; + } - NSData *platformData = to_ios_data(data); - NSArray *result = filter.onResponseData(platformData, end_stream); - // Result is typically a pair of status and entity, but uniquely in the case of - // ResumeIteration it will (optionally) contain additional pending elements. - envoy_headers *pending_headers = toNativeHeadersPtr(result.count == 3 ? result[2] : nil); - return (envoy_filter_data_status){/*status*/ [result[0] intValue], - /*data*/ toNativeData(result[1]), - /*pending_headers*/ pending_headers}; + NSData *platformData = to_ios_data(data); + NSArray *result = filter.onResponseData(platformData, end_stream); + // Result is typically a pair of status and entity, but uniquely in the case of + // ResumeIteration it will (optionally) contain additional pending elements. + envoy_headers *pending_headers = toNativeHeadersPtr(result.count == 3 ? result[2] : nil); + return (envoy_filter_data_status){/*status*/ [result[0] intValue], + /*data*/ toNativeData(result[1]), + /*pending_headers*/ pending_headers}; + } } static envoy_filter_trailers_status ios_http_filter_on_request_trailers(envoy_headers trailers, const void *context) { - EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; - if (filter.onRequestTrailers == nil) { - return (envoy_filter_trailers_status){/*status*/ kEnvoyFilterTrailersStatusContinue, - /*trailers*/ trailers, - /*pending_headers*/ NULL, - /*pending_trailers*/ NULL}; - } + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block + // is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; + if (filter.onRequestTrailers == nil) { + return (envoy_filter_trailers_status){/*status*/ kEnvoyFilterTrailersStatusContinue, + /*trailers*/ trailers, + /*pending_headers*/ NULL, + /*pending_trailers*/ NULL}; + } - EnvoyHeaders *platformTrailers = to_ios_headers(trailers); - NSArray *result = filter.onRequestTrailers(platformTrailers); - envoy_headers *pending_headers = NULL; - envoy_data *pending_data = NULL; - // Result is typically a pair of status and entity, but uniquely in the case of - // ResumeIteration it will (optionally) contain additional pending elements. - if (result.count == 4) { - pending_headers = toNativeHeadersPtr(result[2]); - pending_data = toNativeDataPtr(result[3]); + EnvoyHeaders *platformTrailers = to_ios_headers(trailers); + NSArray *result = filter.onRequestTrailers(platformTrailers); + envoy_headers *pending_headers = NULL; + envoy_data *pending_data = NULL; + // Result is typically a pair of status and entity, but uniquely in the case of + // ResumeIteration it will (optionally) contain additional pending elements. + if (result.count == 4) { + pending_headers = toNativeHeadersPtr(result[2]); + pending_data = toNativeDataPtr(result[3]); + } + return (envoy_filter_trailers_status){/*status*/ [result[0] intValue], + /*trailers*/ toNativeHeaders(result[1]), + /*pending_headers*/ pending_headers, + /*pending_data*/ pending_data}; } - return (envoy_filter_trailers_status){/*status*/ [result[0] intValue], - /*trailers*/ toNativeHeaders(result[1]), - /*pending_headers*/ pending_headers, - /*pending_data*/ pending_data}; } static envoy_filter_trailers_status ios_http_filter_on_response_trailers(envoy_headers trailers, const void *context) { - EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; - if (filter.onResponseTrailers == nil) { - return (envoy_filter_trailers_status){/*status*/ kEnvoyFilterTrailersStatusContinue, - /*trailers*/ trailers, - /*pending_headers*/ NULL, - /*pending_data*/ NULL}; - } + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block + // is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; + if (filter.onResponseTrailers == nil) { + return (envoy_filter_trailers_status){/*status*/ kEnvoyFilterTrailersStatusContinue, + /*trailers*/ trailers, + /*pending_headers*/ NULL, + /*pending_data*/ NULL}; + } - EnvoyHeaders *platformTrailers = to_ios_headers(trailers); - NSArray *result = filter.onResponseTrailers(platformTrailers); - envoy_headers *pending_headers = NULL; - envoy_data *pending_data = NULL; - // Result is typically a pair of status and entity, but uniquely in the case of - // ResumeIteration it will (optionally) contain additional pending elements. - if (result.count == 4) { - pending_headers = toNativeHeadersPtr(result[2]); - pending_data = toNativeDataPtr(result[3]); + EnvoyHeaders *platformTrailers = to_ios_headers(trailers); + NSArray *result = filter.onResponseTrailers(platformTrailers); + envoy_headers *pending_headers = NULL; + envoy_data *pending_data = NULL; + // Result is typically a pair of status and entity, but uniquely in the case of + // ResumeIteration it will (optionally) contain additional pending elements. + if (result.count == 4) { + pending_headers = toNativeHeadersPtr(result[2]); + pending_data = toNativeDataPtr(result[3]); + } + return (envoy_filter_trailers_status){/*status*/ [result[0] intValue], + /*trailers*/ toNativeHeaders(result[1]), + /*pending_headers*/ pending_headers, + /*pending_data*/ pending_data}; } - return (envoy_filter_trailers_status){/*status*/ [result[0] intValue], - /*trailers*/ toNativeHeaders(result[1]), - /*pending_headers*/ pending_headers, - /*pending_data*/ pending_data}; } static envoy_filter_resume_status ios_http_filter_on_resume_request(envoy_headers *headers, envoy_data *data, envoy_headers *trailers, bool end_stream, const void *context) { - EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; - if (filter.onResumeRequest == nil) { - return (envoy_filter_resume_status){/*status*/ kEnvoyFilterResumeStatusResumeIteration, - /*pending_headers*/ headers, - /*pending_data*/ data, - /*pending_trailers*/ trailers}; - } + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block + // is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; + if (filter.onResumeRequest == nil) { + return (envoy_filter_resume_status){/*status*/ kEnvoyFilterResumeStatusResumeIteration, + /*pending_headers*/ headers, + /*pending_data*/ data, + /*pending_trailers*/ trailers}; + } - EnvoyHeaders *pendingHeaders = headers ? to_ios_headers(*headers) : nil; - NSData *pendingData = data ? to_ios_data(*data) : nil; - EnvoyHeaders *pendingTrailers = trailers ? to_ios_headers(*trailers) : nil; - NSArray *result = - filter.onResumeRequest(pendingHeaders, pendingData, pendingTrailers, end_stream); - return (envoy_filter_resume_status){/*status*/ [result[0] intValue], - /*pending_headers*/ toNativeHeadersPtr(result[1]), - /*pending_data*/ toNativeDataPtr(result[2]), - /*pending_trailers*/ toNativeHeadersPtr(result[3])}; + EnvoyHeaders *pendingHeaders = headers ? to_ios_headers(*headers) : nil; + NSData *pendingData = data ? to_ios_data(*data) : nil; + EnvoyHeaders *pendingTrailers = trailers ? to_ios_headers(*trailers) : nil; + NSArray *result = + filter.onResumeRequest(pendingHeaders, pendingData, pendingTrailers, end_stream); + return (envoy_filter_resume_status){/*status*/ [result[0] intValue], + /*pending_headers*/ toNativeHeadersPtr(result[1]), + /*pending_data*/ toNativeDataPtr(result[2]), + /*pending_trailers*/ toNativeHeadersPtr(result[3])}; + } } static envoy_filter_resume_status ios_http_filter_on_resume_response(envoy_headers *headers, envoy_data *data, envoy_headers *trailers, bool end_stream, const void *context) { - EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; - if (filter.onResumeResponse == nil) { - return (envoy_filter_resume_status){/*status*/ kEnvoyFilterResumeStatusResumeIteration, - /*pending_headers*/ headers, - /*pending_data*/ data, - /*pending_trailers*/ trailers}; - } + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block + // is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; + if (filter.onResumeResponse == nil) { + return (envoy_filter_resume_status){/*status*/ kEnvoyFilterResumeStatusResumeIteration, + /*pending_headers*/ headers, + /*pending_data*/ data, + /*pending_trailers*/ trailers}; + } - EnvoyHeaders *pendingHeaders = headers ? to_ios_headers(*headers) : nil; - NSData *pendingData = data ? to_ios_data(*data) : nil; - EnvoyHeaders *pendingTrailers = trailers ? to_ios_headers(*trailers) : nil; - NSArray *result = - filter.onResumeResponse(pendingHeaders, pendingData, pendingTrailers, end_stream); - return (envoy_filter_resume_status){/*status*/ [result[0] intValue], - /*pending_headers*/ toNativeHeadersPtr(result[1]), - /*pending_data*/ toNativeDataPtr(result[2]), - /*pending_trailers*/ toNativeHeadersPtr(result[3])}; + EnvoyHeaders *pendingHeaders = headers ? to_ios_headers(*headers) : nil; + NSData *pendingData = data ? to_ios_data(*data) : nil; + EnvoyHeaders *pendingTrailers = trailers ? to_ios_headers(*trailers) : nil; + NSArray *result = + filter.onResumeResponse(pendingHeaders, pendingData, pendingTrailers, end_stream); + return (envoy_filter_resume_status){/*status*/ [result[0] intValue], + /*pending_headers*/ toNativeHeadersPtr(result[1]), + /*pending_data*/ toNativeDataPtr(result[2]), + /*pending_trailers*/ toNativeHeadersPtr(result[3])}; + } } static void ios_http_filter_set_request_callbacks(envoy_http_filter_callbacks callbacks, const void *context) { - EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; - if (filter.setRequestFilterCallbacks == nil) { - return; - } + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block + // is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; + if (filter.setRequestFilterCallbacks == nil) { + return; + } - EnvoyHTTPFilterCallbacksImpl *requestFilterCallbacks = - [[EnvoyHTTPFilterCallbacksImpl alloc] initWithCallbacks:callbacks]; - filter.setRequestFilterCallbacks(requestFilterCallbacks); + EnvoyHTTPFilterCallbacksImpl *requestFilterCallbacks = + [[EnvoyHTTPFilterCallbacksImpl alloc] initWithCallbacks:callbacks]; + filter.setRequestFilterCallbacks(requestFilterCallbacks); + } } static void ios_http_filter_set_response_callbacks(envoy_http_filter_callbacks callbacks, const void *context) { - EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; - if (filter.setResponseFilterCallbacks == nil) { - return; - } + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block + // is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; + if (filter.setResponseFilterCallbacks == nil) { + return; + } - EnvoyHTTPFilterCallbacksImpl *responseFilterCallbacks = - [[EnvoyHTTPFilterCallbacksImpl alloc] initWithCallbacks:callbacks]; - filter.setResponseFilterCallbacks(responseFilterCallbacks); + EnvoyHTTPFilterCallbacksImpl *responseFilterCallbacks = + [[EnvoyHTTPFilterCallbacksImpl alloc] initWithCallbacks:callbacks]; + filter.setResponseFilterCallbacks(responseFilterCallbacks); + } } static void ios_http_filter_on_cancel(const void *context) { - EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; - if (filter.onCancel == nil) { - return; + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block + // is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; + if (filter.onCancel == nil) { + return; + } + filter.onCancel(); } - filter.onCancel(); } static void ios_http_filter_on_error(envoy_error error, const void *context) { - EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; - if (filter.onError == nil) { - error.message.release(error.message.context); - return; - } - + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block + // is necessary to act as a breaker for any Objective-C allocation that happens. @autoreleasepool { + EnvoyHTTPFilter *filter = (__bridge EnvoyHTTPFilter *)context; + if (filter.onError == nil) { + error.message.release(error.message.context); + return; + } + NSString *errorMessage = [[NSString alloc] initWithBytes:error.message.bytes length:error.message.length encoding:NSUTF8StringEncoding];