Getting wrong SSE response from the server

46 views Asked by At

Developing the project with Django. DRF installed. Some processin the backend generating a log file. I am looking at the end of the file for target strings. When target string was caught, it has to be shown on HTML user page. Everything happens in dynamics.

urls.py

    path('api/log-stream/', LogStreamAPIView.as_view(), name='log_stream'),

LogStreamAPIView

class LogStreamAPIView(APIView):
    def get(self, request):
        def stream_log():
            f = subprocess.Popen(['tail', '-F', rcvenot_log], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            p = select.poll()
            p.register(f.stdout)

            while TEST_IN_PROGRESS:
                if p.poll(0.1):
                    new_line_log = f.stdout.readline().decode('utf-8').strip()
                    for target_line in RCV_SCENARIO:
                        if target_line in new_line_log:
                            yield f"event: {new_line_log}\n\n"

        return StreamingHttpResponse(stream_log(), content_type='text/event-stream')

Script on the user HTML page

        <div style="width: 200px; height: 200px; overflow-y: scroll;">
            <ul id="logList"></ul>
        </div>

        <script>
            document.addEventListener("DOMContentLoaded", function() {
                var logList = document.getElementById('logList');

                function updateLog(newLine) {
                    var listItem = document.createElement('li');
                    listItem.textContent = newLine;
                    logList.appendChild(listItem);
                }

                var source = new EventSource('/api/log-stream/');
                source.onmessage = function(event) {
                    updateLog(event.data);
                };
            });
        </script>

I see the strigns via API mysite.ru/api/log-stream/. Some strings event: 2024.03.22 16:16:13:016[INFO ][Thread-0] ************************* - 2

But I don't see them on HTML page.

In browser console I see the response code 406: GET 127.0.0.1:8000/api/log-stream 406 (Not Acceptable) In Response see {"detail":"The \"Accept\" request header could not be satisfied."}

I have tried throw data in different formats f"event: {new_line_log}\n\n" or f"data: {new_line_log}\n\n". It doesn't work.

Tried to add status in return StreamingHttpResponse(stream_log(), content_type='text/event-stream’s , status=200) and status=202. Same

Here is some screen shots of browser console.

screen shots of browser console

screen shots of browser console

Read the docs of SSE, the server should respond with Content-Type: text/event-stream, and in the browser console I see Content-Type: application/json. I suspect if the error might be a here...

Please help to figer out the problem

1

There are 1 answers

2
Abdul Aziz Barkat On

The reason you're getting an error is because Django Rest Framework is trying to perform content negotiation. By default it looks at the DEFAULT_RENDERER_CLASSES setting to figure out the possible content types it can return and if nothing matches it will return a 406 error.

Given this particular view of yours doesn't really seem to be using any DRF specific functionality one of your options is to simply convert that into a normal view that doesn't use DRF.

The other solution is to set up a custom content negotiation class that simply ignores all of the content negotiation. Here's an example from DRF's documentation:

from rest_framework.negotiation import BaseContentNegotiation

class IgnoreClientContentNegotiation(BaseContentNegotiation):
    def select_parser(self, request, parsers):
        """
        Select the first parser in the `.parser_classes` list.
        """
        return parsers[0]

    def select_renderer(self, request, renderers, format_suffix):
        """
        Select the first renderer in the `.renderer_classes` list.
        """
        return (renderers[0], renderers[0].media_type)

Then you'd modify your view to use this for content negotiation:

class LogStreamAPIView(APIView):
    content_negotiation_class = IgnoreClientContentNegotiation
    # Rest of your view
    ...