From 2f0a5f652b88aed37105990403d00e4a960f89d7 Mon Sep 17 00:00:00 2001 From: olivier Date: Fri, 24 Oct 2025 17:00:12 -0700 Subject: [PATCH] =?UTF-8?q?Classes=20utilitaire=20qui=20fourni=20des=20m?= =?UTF-8?q?=C3=A9thodes=20pour=20envoyer=20et=20recevoir=20des=20messages?= =?UTF-8?q?=20depuis=20des=20serveurs=20distants=20en=20utilisant=20le=20p?= =?UTF-8?q?rotocole=20HTTP.=20Cette=20classe=20ne=20contient=20pas=20les?= =?UTF-8?q?=20methodes=20"static"=20et=20elle=20doit=20=C3=AAtre=20instanc?= =?UTF-8?q?i=C3=A9e=20avant=20utilisation,=20ce=20qui=20permet=20la=20cr?= =?UTF-8?q?=C3=A9ation=20d'une=20classe=20simul=C3=A9e=20(mock),=20l'?= =?UTF-8?q?=C3=A9tendant,=20sp=C3=A9cifiquement=20pour=20des=20besoins=20d?= =?UTF-8?q?e=20tests=20unitaires=20et=20d'int=C3=A9gration=20sans=20d?= =?UTF-8?q?=C3=A9pendance=20sur=20une=20r=C3=A9seau=20r=C3=A9elle.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/common/HttpUtils.java | 202 ++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 src/main/java/common/HttpUtils.java diff --git a/src/main/java/common/HttpUtils.java b/src/main/java/common/HttpUtils.java new file mode 100644 index 0000000..646a940 --- /dev/null +++ b/src/main/java/common/HttpUtils.java @@ -0,0 +1,202 @@ +package common; + +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Utility methods to support HTTP network communication. Declared as a + * non-static class so that a mock implementation can be instantiated for testing. + */ +public class HttpUtils +{ + Logger LOGGER = Logger.getLogger( HttpUtils.class.getName() ); + + /** + * Sends an HTTP request. If postData is not null the request will be a POsT + * request otherwise it will be a GET request. + * + * @param urlString The URL to send the request to. + * @param postData Optional POST data. if not null, http request will be POST, otherwise it will be + * a GET + * @param contentType Optional content type for POST data. + * @param responseHeaders A map to store response headers. + * @param outputFilePath If not null, and the file exists, save the response to this file. + * @param resume Only applicable when outputFilePath is present. If false the + * target file should be truncated, true to try resume download and append + * data (works only in combination with a valid outputStream) + * @return The response from the server as a string. + */ + public String HTTPSendRequest( String urlString, String postData, String contentType, + Map responseHeaders, String outputFilePath, boolean resume ) + { + try + { + URL url = new URL( urlString ); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setUseCaches( false ); + connection.setDefaultUseCaches( false ); + if (postData != null && !postData.isEmpty()) + { + connection.setRequestMethod( "POST" ); + connection.setDoOutput( true ); + if (contentType != null && !contentType.isEmpty()) + { + connection.setRequestProperty( "Content-Type", contentType ); + } + // Copy POST data + try (OutputStream os = connection.getOutputStream()) + { + byte[] input = postData.getBytes( StandardCharsets.UTF_8 ); + os.write( input, 0, input.length ); + } + } + else + { + connection.setRequestMethod( "GET" ); + } + + if (resume && outputFilePath != null && Files.exists( Paths.get( outputFilePath ) )) + { + long existingSize = Files.size( Paths.get( outputFilePath ) ); + connection.setRequestProperty( "Range", "bytes=" + existingSize + "-" ); + LOGGER.info( "Resuming download from byte: " + existingSize ); + } + + // Get response + int responseCode = connection.getResponseCode(); + LOGGER.log( Level.FINER, "HTTP Response Code: " + responseCode ); + + // Populate response headers + if (responseHeaders != null) + { + connection.getHeaderFields().forEach( ( key, values ) -> + { + if (key != null && !values.isEmpty()) + { + responseHeaders.put( key, values.get( 0 ) ); // Take first value + } + } ); + } + + if (responseCode >= 200 && responseCode < 300) // || responseCode == 206 /* Partial Content for resume */) + { + InputStream is = connection.getInputStream(); + if (outputFilePath != null && !outputFilePath.isEmpty()) + { + try (FileOutputStream fos = new FileOutputStream( outputFilePath, resume )) + { // Append if resuming + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = is.read( buffer )) != -1) + { + fos.write( buffer, 0, bytesRead ); + } + } + return ""; // Return empty string if writing to file + } + else + { + try (BufferedReader in = new BufferedReader( new InputStreamReader( is, StandardCharsets.UTF_8 ) )) + { + StringBuilder response = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) + { + response.append( line ); + } + return response.toString(); + } + } + } + else + { + try (BufferedReader in = new BufferedReader( + new InputStreamReader( connection.getErrorStream(), StandardCharsets.UTF_8 ) )) + { + StringBuilder errorResponse = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) + { + errorResponse.append( line ); + } + // Attempt to parse error XML if available + if (errorResponse.length() > 0 && errorResponse.toString().startsWith( " responseHeaders ) + { + return HTTPSendRequest( urlString, null, contentType, + responseHeaders, null, false ); + } + + /** + * Sends an HTTP request. If postData is not null the request will be a POST + * request otherwise it will be a GET request. The response is assumed to be + * XML, and will be parsed into an XML Document. + * + * @param url The URL to send the request to. + * @param postData Optional POST data. if not null, http request will be POST, otherwise it will be a get + * @param contentType Optional content type for POST data. + * @param responseHeaders A map to store response headers. + * @param resume remains to be seen. + * + * @return The response from the server as an XML Document. + */ + public Document sendHTTPRequestForXML( String url, String postData, String contentType, + Map responseHeaders, boolean resume ) + throws IOException, SAXException, ParserConfigurationException + { + String response = HTTPSendRequest( url, postData, contentType, responseHeaders, null, resume ); + byte[] responseData = response.getBytes( StandardCharsets.UTF_8 ); + + DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance(); + fac.setNamespaceAware( true ); + DocumentBuilder db = fac.newDocumentBuilder(); + Document responseDoc = db.parse( new ByteArrayInputStream( responseData ) ); + responseDoc.getDocumentElement().normalize(); + return responseDoc; + } +}