laminas-api-tools / api-tools-api-problem

Laminas Module providing API-Problem assets and rendering
https://api-tools.getlaminas.org/documentation
BSD 3-Clause "New" or "Revised" License
8 stars 18 forks source link

SendApiProblemResponseListener modifies content #2

Open weierophinney opened 4 years ago

weierophinney commented 4 years ago

I have noticed, that SendApiProblemResponseListener is modifying the response content AFTER sending headers (if display_exceptions is enabled).

I have a Content-Length header based on response content but this header is now responding a wrong length as it doesn't know if the content gets modified afterwords.

On the same time I'm automatically pretty printing the response if the request contains header X-Pretty: 1 but this will also be ignored as the content gets rewritten in this case.

What is the reason to overwrite HttpResponseSender instead of listening on EVENT_FINISH?

This is my broken code:

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $eventManager  = $e->getApplication()->getEventManager();
        $eventManager->attach(MvcEvent::EVENT_FINISH, [$this, 'onFinish'], -1000);
    }

    public function onFinish(MvcEvent $event)
    {
        $response = $event->getResponse();
        $request  = $event->getRequest();

        if ($request instanceof HttpRequest && $response instanceof HttpResponse && !($response instanceof HttpStreamResponse)) {
            $rqHeaders = $request->getHeaders();
            $rsHeaders = $response->getHeaders();
            $rsContent = $response->getContent();

            // pretty print JSON response
            if ($rqHeaders->has('X-Pretty')
                && $rqHeaders->get('X-Pretty')->getFieldValue() === '1'
                && is_string($rsContent) && $rsContent
                && $rsHeaders->has('Content-Type')
                && preg_match('/^application\\/(.*\\-)?json/', $rsHeaders->get('Content-Type')->getFieldValue())
            ) {
                $rsContentDecode = json_decode($rsContent);
                if ($rsContentDecode !== null) {
                    $prettyOptions = JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
                    $rsContent     = json_encode($rsContentDecode, $prettyOptions);
                    $response->setContent($rsContent);

                    if ($rsHeaders->has('Content-Length')) {
                        $rsHeaders->removeHeader($rsHeaders->get('Content-Length'));
                    }
                    $rsHeaders->addHeaderLine('Content-Length', strlen($rsContent));
                }
            }

            // Send Content-Length header if possible
            if (is_string($rsContent) && !$rsHeaders->has('Content-Length')) {
                $rsHeaders->addHeaderLine('Content-Length', strlen($rsContent));
            }
        }
    }
}

Originally posted by @marc-mabe at https://github.com/zfcampus/zf-api-problem/issues/54