Problématique courante
Lors de la conception de scénarios d'automatisation d'interfaces avec Apache JMeter, l'utilisation d'un gestionnaire d'en-têtes HTTP commun simplifie la configuration. Cependant, des conflits surgissent lorsque des requêtes distinctes exigent des valeurs Content-Type incompatibles, comme multipart/form-data pour les téléchargements de fichiers et application/json pour les échanges structurés.
Scénario d'exemple
Imaginons un workflow de test comprenant cinq appels HTTP séquentiels :
- Envoi d'un fichier (requête A)
- Récupération d'états (requête B)
- Association d'image à un service (requête C)
- Mise à jour du statut (requête D)
- Contrôle final de l'état (requête E)
Dans cette séquence, la requête A nécessite l'en-tête Content-Type: multipart/form-data, tandis que C et D utilisent Content-Type: application/json. Si un gestionnaire d'en-têtes global est configuré avec application/json, la requête A échouera à cause de l'incompatibilité.
Stratégie initiale : gestionnaires d'en-têtes localisés
La première approche consiste à ne pas partager le gestionnaire d'en-têtes. On place un gestionnaire dédié à la requête A avec multipart/form-data, et on ajoute des gsetionnaires spécifiques pour C et D avec application/json. Les requêtes B et E n'ont pas besoin d'en-têtes personnalisés.
Cette méthode fonctionne mais engendre une duplication de configuraton. Elle devient fastidieuse à maintenir sur des suites de tests volumineuses, car chaque modification d'en-tête doit être répliquée manuellement.
Alternative : isolation par groupes de threads
Une seconde option consiste à répartir les requêtes dans des groupes de threads distincts. Un groupe gère la requête A avec son en-tête spécifique, tandis qu'un autre groupe contient les requêtes B à E avec un gestionnaire commun pour application/json.
Bien que cela isole les conflits, cela divise le flux de test. Cette fragmentation complique la coordination entre les groupes et peut poser des défis pour la synchronisation des données.
Solution optimale : manipulation dynamique via BeanShell
La méthode la plus flexible repose sur des processeurs préliminaires BeanShell pour modifier les en-têtes à l'exécution. Pour la requête A, on supprime l'en-tête Content-Type du gestionnaire global avant l'envoi. Pour les requêtes C et D, on le réintègre dynamiquement.
Exemple de code pour retirer l'en-tête de la requête A :
import org.apache.jmeter.protocol.http.control.HeaderManager;
// Récupérer le gestionnaire d'en-têtes actif
HeaderManager gestionnaireEntetes = sampler.getHeaderManager();
// Journaliser l'état initial
log.info("En-têtes avant retrait : " + gestionnaireEntetes.getHeaders().getStringValue());
// Supprimer l'entrée Content-Type
sampler.getHeaderManager().removeHeaderNamed("Content-Type");
// Vérifier la modification
log.info("En-têtes après retrait : " + gestionnaireEntetes.getHeaders().getStringValue());
Pour la requête C, on ajoute un processeur BeanShell qui insère l'en-tête JSON :
import org.apache.jmeter.protocol.http.control.HeaderManager;
import org.apache.jmeter.protocol.http.control.Header;
// Accéder au gestionnaire courant
HeaderManager gestionnaireEntetes = sampler.getHeaderManager();
// Documenter avant l'ajout
log.info("En-têtes avant insertion : " + gestionnaireEntetes.getHeaders().getStringValue());
// Créer un objet Header pour le Content-Type JSON
Header enteteJson = new Header("Content-Type", "application/json");
// L'ajouter au gestionnaire
gestionnaireEntetes.add(enteteJson);
// Confirmer l'opération
log.info("En-têtes après insertion : " + gestionnaireEntetes.getHeaders().getStringValue());
Après cette insertion, les requêtes D et E utiliseront automatiquement l'en-tête JSON sans traitement supplémentaire. Le gestionnaire d'en-têtes global doit rester actif pour que les manipulations BeanShell fonctionnent correctement. Si ce gestionnaire est désactivé, le script échouera car les en-têtes modifiés ne seront pas reconnus.