web-dev-qa-db-fra.com

Tomcat renvoie parfois une réponse sans en-tête HTTP.

J'étudie un problème où Tomcat (7.0.90 7.0.92) renvoie une réponse sans en-tête HTTP très occasionnellement.

Selon les paquets capturés par Wireshark, une fois que Tomcat a reçu une demande, il ne renvoie qu'un corps de réponse. Il ne retourne ni une ligne d'état ni des en-têtes de réponse HTTP.

Cela provoque une instance Nginx en aval générant l'erreur «en amont n'a envoyé aucun en-tête HTTP/1.0 valide lors de la lecture de l'en-tête de réponse depuis l'amont», renvoyant une erreur 502 au client et fermant la connexion http correspondante entre Nginx et Tomcat.

Que peut être une cause de ce comportement? Y a-t-il une possibilité qui amène Tomcat à se comporter de cette façon? Ou peut-il y avoir quelque chose qui supprime les en-têtes HTTP sous certaines conditions? Ou bien Wireshark n'a pas réussi à capturer les images contenant les en-têtes HTTP? Tout conseil visant à cerner le problème est également grandement apprécié.

Voici une capture d'écran de "Follow HTTP Stream" de Wireshark qui montre la réponse problématique:

 enter image description here

MODIFIER:

Voici une capture d’écran de "TCP Stream" de la partie concernée (réponse uniquement). Il semble que les morceaux dans la deuxième réponse de la dernière semblent bien:

 enter image description here

EDIT2:

J'ai transmis cette question à la liste de diffusion des utilisateurs de Tomcat et ai obtenu des suggestions d'investigation de la part des développeurs:

http://Tomcat.10.x6.nabble.com/Tomcat-occasionally-returns-a-response-without-HTTP-headers-td5080623.html

Mais je n’ai pas encore trouvé de solution adéquate. Je cherche toujours des idées pour résoudre ce problème.

8
Kohei Nozaki

Il s'est avéré que la bibliothèque "sjsxp" utilisée par JAX-WS RI v2.1.3 fait que Tomcat se comporte de cette manière. J'ai essayé une version différente de JAX-WS RI (v2.1.7) qui n'utilise plus la bibliothèque "sjsxp" et le problème a été résolu.

Un problème très similaire a été posté sur la liste de diffusion Metro: http://metro.1045641.n5.nabble.com/JAX-WS-RI-2-1-5-returning-malformed-response-tp1063518.html

0
Kohei Nozaki

Les problèmes que vous rencontrez proviennent de la mise en pipeline de plusieurs demandes sur une seule connexion avec l'amont, comme l'explique la réponse d'hier de Eugène Adell.

Que ce soit un bogue dans nginx, Tomcat, votre application ou l'interaction de toute combinaison de ce qui précède, constituerait probablement une discussion pour un autre forum, mais pour l'instant, considérons quelle serait la meilleure solution:

Pouvez-vous publier votre configuration nginx? Plus précisément, si vous utilisez keepalive et une valeur non définie par défaut de proxy_http_version dans nginx? - cnst Il y a 1 heure 

@cnst J'utilise proxy_http_version 1.1 et keepalive 100 - Kohei Nozaki Il y a 1 heure

Conformément à une réponse précédente de à une question sans lien ici sur SO , tout en partageant les paramètres de configuration comme ci-dessus, vous souhaiterez peut-être réexaminer les raisons de votre utilisation de la fonctionnalité keepalive entre les paramètres de chargement frontaux. l'équilibreur (par exemple, nginx) et le serveur d'applications principal (par exemple, Tomcat).

Conformément à une explication keepalive sur ServerFault dans le contexte de nginx , la fonctionnalité keepalive dans le contexte upstream de nginx n’était même pas prise en charge jusqu’à très récemment, au cours des années de développement de nginx. Pourquoi? C'est parce qu'il y a très peu de scénarios valides d'utilisation de keepalive lorsqu'il est généralement plus rapide d'établir une nouvelle connexion que d'attendre qu'une connexion existante soit disponible:

  • Lorsque la latence entre le client et le serveur est de l'ordre de 50 ms +, keepalive permet de réutiliser les informations d'identification TCP et SSL, ce qui entraîne une accélération très importante, car aucun aller-retour supplémentaire n'est requis pour établir la connexion. pour traiter les requêtes HTTP.

    C'est pourquoi vous ne devez jamais désactiver keepalive entre le client et nginx (contrôlé via http://nginx.org/r/keepalive_timeout in http, server et location).

  • Mais lorsque la latence entre le serveur proxy frontal et le serveur d'applications principal est de l'ordre de 1 ms (0,001 s), l'utilisation de keepalive est une recette pour poursuivre Heisenbugs sans en retirer aucun avantage, car la latence supplémentaire de 1 ms pour établir une connexion pourrait ainsi être inférieur à la latence de 100 ms d’attente de la disponibilité d’une connexion existante. (Il s’agit d’une simplification excessive de la gestion des connexions, mais elle vous montre à quel point les éventuels avantages de la variable keepalive entre l’équilibreur de charge frontal et le serveur d’application seraient extrêmement minimes, à condition que les deux résident dans la même région.)

    C’est la raison pour laquelle utiliser http://nginx.org/r/keepalive dans le contexte upstream est rarement une bonne idée, sauf si vous en avez vraiment besoin et si vous avez spécifiquement vérifié qu’il produisait les résultats souhaités, compte tenu de la points comme ci-dessus.

    (Et, pour que les choses soient claires, ces points sont indépendants de la nature du logiciel que vous utilisez. Par conséquent, même si vous ne rencontriez pas les problèmes que vous rencontrez avec votre combinaison de nginx et de Tomcat, je vous recommanderais quand même pas. utilisez keepalive entre l'équilibreur de charge et le serveur d'applications même si vous décidez de vous écarter de nginx et de Tomcat, ou des deux.


Ma suggestion?

  • Le problème ne serait pas reproductible avec les valeurs par défaut de http://nginx.org/r/proxy_http_version et http://nginx.org/r/keepalive .

  • Si votre back-end se trouve à moins de 5 ms du front-end, vous ne tirerez certainement aucun avantage de la modification de ces directives. Par conséquent, à moins que vous ne poursuiviez Heisenbugs, vous pourriez aussi bien conserver ces paramètres spécifiques. défauts par défaut.

4
cnst

Nous voyons que vous réutilisez une connexion établie pour envoyer la demande POST et que, comme vous l'avez dit, la réponse arrive sans les status-line et les en-têtes.

une fois que Tomcat a reçu une demande, il ne renvoie qu'un corps de réponse.

Pas exactement. Cela commence par 5d, qui est probablement un taille de bloc, ce qui signifie que la dernière réponse «complète» (avec ligne d'état) et en-têtes ) obtenu de cette connexion contenait un en-tête "Transfer-Encoding: chunked} _". Pour une raison quelconque, votre serveur pense toujours que la réponse précédente n'est pas terminée au moment où il commence à envoyer cette nouvelle réponse à votre dernière demande.

Un morceau manquant semble confirmé car la capture d'écran ne montre pas un dernier morceau} _ (valeur = 0) terminant la demande précédente. Notez que la dernière réponse se termine par un dernier morceau (le dernier octet affiché est 0).

Qu'est-ce qui cause ça? La réponse précédente n'est pas considérée techniquement comme une réponse complète. Cela peut être un bug sur Tomcat, votre bibliothèque de services Web, votre propre code. Peut-être même que vous envoyez votre demande trop tôt, avant que la précédente ne soit complètement traitée.

Reste-t-il des octets si vous comparez les tailles de bloc de ce qui est réellement envoyé au client? Tous les tampons sont-ils vidés? Méfiez-vous aussi des fins de ligne (CRLF vs LF).

Une dernière cause à laquelle je pense, si votre réponse contient une sorte d’entrée d’utilisateur tirée de la demande, vous pouvez être confronté à HTTP Splitting .

Solutions possibles.

Il est intéressant d'essayer de désactiver le codage fragmenté au niveau de votre bibliothèque, par exemple avec Axis2, vérifiez le Transport HTTP .

Lors de la réutilisation d'une connexion, vérifiez votre code client pour vous assurer que vous n'envoyez pas de demande avant de lire toute la réponse précédente (pour éviter les chevauchements).

Lectures supplémentaires

RFC 2616 3.6.1 Codage de transfert en bloc

2
Eugène Adell