Repository: elastic/apm-agent-python

Issue:
[gRPC] Does not work with stream-to-stream gRPC requests
**Describe the bug**: I'm trying to add elastic apm for my gRPC service with unary-to-unary requests and stream-to-stream. Everything is fine with unary-to-unary requests. But stream-to-stream requests stop working.

The service stops accepting streaming requests altogether.

I think I saw somewhere that you wrote elastic does not support stream requests for gRPC. But I didn't think it would be a service-blocking problem. 

Is there any way to get around it, to make custom processing for stream specifically?

**To Reproduce**
1. Create a gRPC service with stream-to-stream and unary-to-unary endpoints.
```python
class TestService(test_pb2_grpc.TestService):
    async def Streaming(self, request_iterator, context):
           count_of_request = 0
           async for request in request_iterator:
               count_of_request += 1
               yield test_pb2.Response(message=f"#{count_of_request } - ok")

    async def Unary(self, request, context):
           return test_pb2.Response(message="ok")
```
3. Connect elastic apm.
```python
elasticapm.instrument()

async def run_serve():
    apm_client = GRPCApmClient(
            {
                "SERVICE_NAME": "grpc-test",
                "SERVER_URL": "http://localhost:8200",
                "ENVIRONMENT": "test",
                "TRANSACTION_SAMPLE_RATE": 1.0,
                "SECRET_TOKEN": "qwerty",
            }
    )
    server = grpc.aio.server(
        futures.ThreadPoolExecutor(max_workers=10),
    )
    test_pb2_grpc.add_TestServicer_to_server(
        TestService(), server
    )
    server.add_insecure_port("[::]:50051")
    await server.start()
    await server.wait_for_termination()


if __name__ == "__main__":
    asyncio.run(run_serve())
```
5. Make test requests
6. Result: unary - works, stream - returns nothing, no logs in the service

**Environment (please complete the following information)**
- OS: Linux
- Python version: 3.10
- Framework and version: gRPC 1.43.0
- APM Server version: 6.20.0

**Additional context**

Add any other context about the problem here.

- `requirements.txt`:
  <details>
    <summary>Click to expand</summary>

    ```
    gcloud==0.18.3
    google-api-python-client==2.39.0
    grpcio-tools==1.43.0
    grpcio-health-checking==1.43.0
    setuptools==59.5.0
    elastic-apm
    sentry-sdk[grpcio]==1.31.0
    ```
  </details>



Discussion:

Resolving patch:
diff --git a/elasticapm/contrib/grpc/async_server_interceptor.py b/elasticapm/contrib/grpc/async_server_interceptor.py
--- a/elasticapm/contrib/grpc/async_server_interceptor.py
+++ b/elasticapm/contrib/grpc/async_server_interceptor.py
@@ -33,20 +33,18 @@
 import grpc
 
 import elasticapm
-from elasticapm.contrib.grpc.server_interceptor import _ServicerContextWrapper, _wrap_rpc_behavior, get_trace_parent
+from elasticapm.contrib.grpc.server_interceptor import _ServicerContextWrapper, get_trace_parent
 
 
 class _AsyncServerInterceptor(grpc.aio.ServerInterceptor):
     async def intercept_service(self, continuation, handler_call_details):
-        def transaction_wrapper(behavior, request_streaming, response_streaming):
-            async def _interceptor(request_or_iterator, context):
-                if request_streaming or response_streaming:  # only unary-unary is supported
-                    return behavior(request_or_iterator, context)
+        def wrap_unary_unary(behavior):
+            async def _interceptor(request, context):
                 tp = get_trace_parent(handler_call_details)
                 client = elasticapm.get_client()
                 transaction = client.begin_transaction("request", trace_parent=tp)
                 try:
-                    result = behavior(request_or_iterator, _ServicerContextWrapper(context, transaction))
+                    result = behavior(request, _ServicerContextWrapper(context, transaction))
 
                     # This is so we can support both sync and async rpc functions
                     if inspect.isawaitable(result):
@@ -65,4 +63,12 @@ async def _interceptor(request_or_iterator, context):
 
             return _interceptor
 
-        return _wrap_rpc_behavior(await continuation(handler_call_details), transaction_wrapper)
+        handler = await continuation(handler_call_details)
+        if handler.request_streaming or handler.response_streaming:
+            return handler
+
+        return grpc.unary_unary_rpc_method_handler(
+            wrap_unary_unary(handler.unary_unary),
+            request_deserializer=handler.request_deserializer,
+            response_serializer=handler.response_serializer,
+        )


Test patch: