Introduction
L’événement Garden Night possède plusieurs entrées web locales : accueil des participants, plan, page du personnel et billetterie. Avant l’ouverture, les organisateurs ont besoin d’un résultat fidèle pour chaque vérification demandée. Une entrée refusée ou lente ne doit pas disparaître du rapport.
Vous compléterez une fonction de vérification en Go et enregistrerez un véritable rapport JSON. Vous devez connaître les fonctions, structures, slices, modules et la syntaxe élémentaire des canaux Go, et avoir suivi Building a Lost and Found API ou acquis une expérience HTTP équivalente. Les erreurs du client HTTP, les délais limités et la responsabilité de libérer les réponses sont expliqués avant leur utilisation. Toutes les cibles sont des services locaux contrôlés ; il s’agit d’une vérification ponctuelle, pas d’un scanner Internet ni d’un service de supervision. Cette unité indépendante ne nécessite aucun fichier d’une autre VM. Prévoyez environ 55 minutes, lecture et erreurs courantes comprises ; il s’agit d’une estimation de l’auteur.
Lire une petite vérification réussie
Dans cette étape, exécutez un petit exemple fonctionnel et découvrez ce qu’identifie un résultat. Commencez dans Workspace. Le code Go et le service local de l’événement sont déjà prêts. Toutes les commandes ci-dessous s’exécutent dans Workspace.
cd sélectionne le répertoire. Go se trouve dans /usr/local/go/bin ; export ajoute ce répertoire avant le $PATH existant de ce shell. Ce réglage ne vaut que pour cette session de terminal. Répétez ces deux lignes dans un nouveau shell ; aucun fichier global de configuration n’est à modifier.
cd /home/labex/project/site-checker
export PATH=/usr/local/go/bin:$PATH
cat lit les fichiers sans les modifier. Examinez les deux tâches normales et les structures de données Go :
cat normal.json types.go
Chaque cible possède un id identifiant cette tâche, une url à demander et want, le texte attendu dans le corps de la réponse. Le résultat conserve id et url ; kind décrit l’issue de la vérification et status contient le code HTTP reçu, ou zéro si aucun en-tête de réponse n’est arrivé. Une tâche est distincte de son URL : deux personnes peuvent demander des vérifications séparées de la même entrée.
go run . compile et exécute le paquet Go de ce répertoire. La commande préparée accepte -input pour une liste JSON, -workers pour le nombre maximal de vérifications simultanées et -timeout pour le budget de temps HTTP de chaque cible. 200ms signifie 200 millisecondes. Ici, un worker vérifie successivement les deux entrées rapides. Le symbole > du shell envoie la sortie standard dans un fichier, qu’il remplace s’il existe déjà ; les erreurs restent affichées dans le terminal.
go run . -input normal.json -workers 1 -timeout 200ms > normal-report.json
Lisez le résultat réellement enregistré :
cat normal-report.json
Les deux tâches doivent afficher kind: "ok" et status: 200, avec leurs identifiants et URL d’origine. 200 indique que le serveur a accepté ce GET ; cela ne prouve pas à lui seul que le contenu attendu est présent. Le premier brouillon ne comprend que le cas facile. Vous allez rendre les résultats utiles pour les autres entrées.
Si vous voyez go: command not found, répétez le export de cette session. Si un fichier est introuvable, répétez cd et vérifiez son nom. Si Go signale ensuite une erreur de compilation, utilisez le nom de fichier et le numéro de ligne pour repérer et corriger cette petite zone. L’étape suivante explique comment modifier, enregistrer et vérifier vos changements sans remplacer tout le fichier. Gardez intacts main.go, types.go, tous les fichiers JSON d’entrée et le service local préparé.
Distinguer les réponses des requêtes échouées
Dans cette étape, modifiez la petite fonction Check dans Workspace, une décision à la fois. Gardez run.go séquentiel pour vous concentrer sur la signification d’une cible. Lisez d’abord le brouillon :
cat check.go
Le résultat commence avec l’identifiant et l’URL de cette tâche. client.Get renvoie une réponse et une erreur. La branche d’erreur existante traite une connexion échouée ; un appel réussi confie à cette fonction la responsabilité de fermer le corps de réponse. Gardez defer response.Body.Close() juste après cette branche d’erreur : defer s’exécute au retour de la fonction, y compris lors d’un retour anticipé. Gardez aussi l’affectation du statut reçu. Vous étendrez ce brouillon au lieu de le remplacer par un programme terminé fourni.
Modifier et récupérer localement
Avant toute modification, faites une sauvegarde du code source. cp copie un fichier ; l’extension .bak est importante, car un autre fichier .go serait compilé dans ce paquet et pourrait définir Check deux fois.
cp check.go check.go.bak
Ouvrez le fichier existant dans l’éditeur du terminal :
nano check.go
Dans nano, déplacez-vous avec les flèches. Tapez pour insérer du texte ; Retour arrière et Suppr retirent des caractères. Ctrl+K coupe la ligne courante : utilisez-le uniquement lorsque toute cette ligne doit être supprimée. Ctrl signifie maintenir Control pendant l’appui sur l’autre touche. Utilisez Ctrl+W, tapez func Check, puis Entrée pour rechercher ; Ctrl+C annule une recherche. Modifiez à l’emplacement du curseur. Pour enregistrer, appuyez sur Ctrl+O, vérifiez que le nom reste check.go et appuyez sur Entrée. Appuyez sur Ctrl+X pour quitter. Relancez nano check.go lorsque vous devez modifier à nouveau. Si vous avez ouvert le mauvais nom ou fait des modifications involontaires, appuyez sur Ctrl+X, puis sur N à la question d’enregistrement. À une invite de nom de fichier indésirable, Ctrl+C annule l’invite. Exécutez pwd pour lire votre répertoire, utilisez le cd de la première étape et ouvrez le bon fichier avant de réessayer.
Après chaque modification, gofmt -w check.go met le fichier en forme sur place. Une accolade manquante ou une autre erreur de syntaxe indique un fichier et un numéro de ligne : rouvrez ce fichier, recherchez l’instruction voisine et corrigez cette zone. Utilisez cat check.go après enregistrement pour inspecter ce qui est réellement sur disque. Gardez les parties fonctionnelles déjà écrites ; une erreur ordinaire du code source ne nécessite ni de réinitialiser la VM ni de remplacer toute la fonction.
Décider si le serveur a accepté cette entrée
Une réponse 403 est un échange HTTP réussi : Get ne renvoie donc pas automatiquement une erreur Go pour celle-ci. Cet événement attend exactement le statut 200 ; les autres statuts reçus donnent http_error. C’est la règle métier de l’événement, pas l’affirmation que tous les autres statuts 2xx sont universellement des échecs HTTP.
Dans Check, après avoir enregistré response.StatusCode, écrivez une condition le comparant à http.StatusOK, la constante 200 de la bibliothèque standard. Pour un statut différent, définissez la catégorie du résultat à "http_error" et renvoyez immédiatement ce résultat. Gardez pour le moment le résultat final "ok" existant. Ce retour ferme le corps via le defer existant, sans attendre de lire un corps d’erreur inutile.
Enregistrez, quittez nano et mettez en forme :
gofmt -w check.go
Avant l’exécution, prévoyez les résultats des tâches normales et du personnel. Vous pouvez inspecter toutes les tâches sans les modifier :
cat targets.json
go run . -input targets.json -workers 1 -timeout 200ms
À ce stade, la tâche du personnel doit donner http_error avec 403, tandis que les pages reçues avec 200 restent ok même si leur contenu est incorrect. La vérification lente de billetterie est encore appelée connection_error ; vous distinguerez les expirations plus bas. Ces erreurs restantes volontaires montrent ce que vos prochaines modifications doivent changer. Chaque identifiant d’entrée, dont les deux identifiants d’accueil, doit toujours avoir un résultat.
Vérifier le contenu reçu de la page
Pour une réponse 200, io.ReadAll(response.Body) renvoie les octets du corps et une erreur. Ajoutez io et strings à côté de net/http dans un groupe d’imports : import ( suivi d’un nom de paquet entre guillemets par ligne, puis ). Les imports nomment les paquets utilisés par votre fichier ; les imports inutilisés sont des erreurs de compilation : ajoutez-les au moment d’implémenter leurs appels.
Avant de classer un corps reçu avec succès, lisez-le dans body et err. Si la lecture échoue, renvoyez pour l’instant un résultat "connection_error". Gardez le statut déjà reçu dans le résultat. Après une lecture réussie, définissez ou gardez d’abord result.Kind = "ok". Puis, après cette affectation et avant le retour final, testez le marqueur attendu. string(body) convertit les octets en texte et strings.Contains(string(body), target.Want) indique si ce texte contient le marqueur. Lorsque cette réponse est fausse, remplacez la catégorie par "content_mismatch". N’affectez plus "ok" après cette condition, sinon cela écraserait la différence détectée. En Go, ! inverse un booléen. Gardez un retour final pour le corps lu avec succès.
Enregistrez et quittez, puis mettez en forme et répétez les mêmes tâches réelles :
gofmt -w check.go
go run . -input targets.json -workers 1 -timeout 200ms
Le plan incorrect doit maintenant donner content_mismatch avec 200, tandis que les deux tâches d’accueil restent ok avec 200 et que le résultat du personnel reste http_error avec 403. Un statut reçu ne prouve pas que le marqueur correspond. L’expiration de billetterie a encore besoin de sa propre catégorie.
Distinguer une attente limitée d’un autre échec
Le budget du client HTTP inclut la lecture du corps de réponse. Une expiration avant les en-têtes laisse le statut à zéro ; une expiration après les en-têtes 200 doit conserver le statut 200. Ajoutez net aux imports. Un net.Error possède une méthode Timeout(). Ce court modèle Go vérifie qu’une erreur prend en charge cette interface avant d’appeler sa méthode :
if networkError, ok := err.(net.Error); ok && networkError.Timeout() {
// The operation exceeded its time budget.
}
L’assertion de type renvoie une valeur convertie et un booléen. && évalue sa partie droite uniquement si la partie gauche est vraie. Dans les deux branches d’erreur, celle de requête et celle de lecture du corps, gardez "connection_error" par défaut, puis passez à "timeout" lorsque ce test protégé réussit. Gardez le retour de chaque branche et l’identité de la tâche originale. Enregistrez, quittez et mettez en forme :
gofmt -w check.go
Le service existant propose aussi deux cas contrôlés utiles : /garden/bodylate/check envoie les en-têtes 200 avant son délai fini de 600ms pour le corps ; /garden/deniedbody/check envoie les en-têtes 403 avant le même délai inutile du corps. Créez une entrée supplémentaire pour examiner ces cas, en gardant intactes toutes les listes JSON préparées :
nano body-probes.json
Saisissez ce tableau JSON. JSON utilise des clés et chaînes entre guillemets doubles, des virgules entre champs et objets, et des crochets autour de la liste. Il n’y a pas de virgule après le dernier champ ou objet.
[
{"id": "body-200", "url": "http://127.0.0.1:8080/garden/bodylate/check", "want": "GARDEN OPEN"},
{"id": "body-403", "url": "http://127.0.0.1:8080/garden/deniedbody/check", "want": "GARDEN OPEN"}
]
Enregistrez avec Ctrl+O, confirmez body-probes.json et Entrée, puis quittez avec Ctrl+X. Lisez le fichier réel avant de l’utiliser :
cat body-probes.json
Prévoyez les deux résultats, puis demandez-les avec un budget de 200ms :
go run . -input body-probes.json -workers 1 -timeout 200ms
Attendez timeout avec le statut 200 pour body-200, et http_error avec le statut 403 pour body-403. Le refus est connu avant la lecture de son corps : le délai de celui-ci ne doit donc pas le transformer en expiration. Le délai client met fin à l’attente ; le serveur peut terminer son travail fini plus tard. Ne déduisez pas la correction de la durée totale.
Enfin, relancez la liste originale complète des tâches :
go run . -input targets.json -workers 1 -timeout 200ms
Associez les résultats par identifiant, pas par position. Attendez deux résultats ok, un http_error avec 403, un content_mismatch avec 200, un timeout avec zéro et un connection_error avec zéro. Les échecs métier sont des valeurs ordinaires du rapport : la commande se termine donc tout de même avec succès. Une erreur de compilation ou de syntaxe JSON signifie qu’elle n’a pas pu effectuer son travail : utilisez le fichier et l’emplacement indiqués pour corriger votre modification locale, puis relancez cette commande. La vérification du cours teste votre source originale et la fermeture des corps sur des entrées indépendantes ; elle n’exige ni éditeur particulier ni nom précis pour le fichier supplémentaire.
Conserver et lire le rapport complet
Dans cette étape, enregistrez un rapport utile en conservant toutes les tâches. Lisez la boucle séquentielle préparée :
cat run.go
http.Client{Timeout: budget} applique un budget fini à chaque requête. La boucle appelle Check une fois par cible et ajoute tous les résultats, erreurs comprises. Elle ne doit ni revenir dès la première entrée défaillante ni réunir les résultats dans une map indexée par URL. Le slice vide initialisé produit aussi un tableau JSON vide lorsqu’aucune tâche n’est fournie.
Enregistrez maintenant le rapport complet. Seul le fichier de rapport est volontairement remplacé ; les tâches d’entrée restent intactes.
go run . -input targets.json -workers 1 -timeout 200ms > report.json
Lisez le fichier que vous avez réellement produit :
cat report.json
Utilisez les identifiants pour relier les deux tâches d’accueil à la même URL. Chacune doit apparaître exactement une fois. Le résultat 403 informe les organisateurs qu’un serveur a répondu et refusé la requête : ce n’est pas une panne de connexion. Le marqueur incorrect du plan demande d’examiner le contenu malgré le statut 200. Une expiration indique que cette vérification a dépassé son budget, pas que le service ne répondra jamais.
Enfin, essayez la liste vide préparée. La sortie standard s’affiche directement et ne remplace donc pas votre rapport utile :
go run . -input empty.json -workers 1 -timeout 200ms
Attendez [] et une fin normale. Conservez report.json, votre check.go modifié et toutes les entrées d’origine. Les vérifications du cours lisent ce rapport mais exécutent aussi votre code inchangé contre des services locaux indépendants : des lignes copiées ou statiques ne remplacent pas de vraies requêtes.
Résumé
Vous avez créé un résultat fidèle pour chaque entrée locale, distingué les refus HTTP des échecs de transport, reconnu les contenus incorrects, appliqué des budgets HTTP finis et fermé tous les corps reçus. Vous avez enregistré et lu un rapport JSON complet, en conservant des tâches à identifiants séparés pour les URL répétées. L’unité suivante garde ces décisions et change la répartition du travail entre un petit nombre de workers.



