From a43daffa8673681b4adbf5dcbde3ce0d268131b6 Mon Sep 17 00:00:00 2001
From: Endi Sukma Dewata <edewata@redhat.com>
Date: Mon, 18 Mar 2013 12:30:17 -0400
Subject: [PATCH] Added CLI option to capture HTTP messages.

A new option has been added to the CLI to capture HTTP requests
and responses and store them in the specified folder.

Ticket #523
---
 .../com/netscape/certsrv/client/PKIConnection.java | 108 ++++++++++++++++++++-
 .../src/com/netscape/cmstools/cli/MainCLI.java     |  19 +++-
 2 files changed, 122 insertions(+), 5 deletions(-)

diff --git a/base/common/src/com/netscape/certsrv/client/PKIConnection.java b/base/common/src/com/netscape/certsrv/client/PKIConnection.java
index c339f3d67d95b1597b7fc97ceb101db86023b99b..f204ff66ca208bc6bcb33a3c16e46bcfaeba61ce 100644
--- a/base/common/src/com/netscape/certsrv/client/PKIConnection.java
+++ b/base/common/src/com/netscape/certsrv/client/PKIConnection.java
@@ -2,6 +2,9 @@ package com.netscape.certsrv.client;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.net.InetAddress;
@@ -18,6 +21,7 @@ import javax.ws.rs.core.MediaType;
 
 import org.apache.commons.httpclient.ConnectTimeoutException;
 import org.apache.http.Header;
+import org.apache.http.HttpEntity;
 import org.apache.http.HttpEntityEnclosingRequest;
 import org.apache.http.HttpException;
 import org.apache.http.HttpRequest;
@@ -34,11 +38,13 @@ import org.apache.http.client.params.HttpClientParams;
 import org.apache.http.conn.scheme.LayeredSchemeSocketFactory;
 import org.apache.http.conn.scheme.Scheme;
 import org.apache.http.conn.scheme.SchemeSocketFactory;
+import org.apache.http.entity.BufferedHttpEntity;
 import org.apache.http.impl.client.ClientParamsStack;
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.impl.client.DefaultRedirectStrategy;
 import org.apache.http.impl.client.EntityEnclosingRequestWrapper;
 import org.apache.http.impl.client.RequestWrapper;
+import org.apache.http.message.BasicHttpResponse;
 import org.apache.http.params.HttpParams;
 import org.apache.http.protocol.HttpContext;
 import org.jboss.resteasy.client.ClientExecutor;
@@ -58,8 +64,6 @@ import org.mozilla.jss.ssl.SSLSocket;
 
 public class PKIConnection {
 
-    boolean verbose;
-
     ClientConfig config;
 
     DefaultHttpClient httpClient = new DefaultHttpClient();
@@ -68,6 +72,12 @@ public class PKIConnection {
     ClientErrorHandler errorHandler;
     ClientExecutor executor;
 
+    int requestCounter;
+    int responseCounter;
+
+    File output;
+    boolean verbose;
+
     public PKIConnection(ClientConfig config) {
         this.config = config;
 
@@ -88,6 +98,9 @@ public class PKIConnection {
         httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
             @Override
             public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
+
+                requestCounter++;
+
                 if (verbose) {
                     System.out.println("HTTP request: "+request.getRequestLine());
                     for (Header header : request.getAllHeaders()) {
@@ -95,6 +108,11 @@ public class PKIConnection {
                     }
                 }
 
+                if (output != null) {
+                    File file = new File(output, "http-request-"+requestCounter);
+                    storeRequest(file, request);
+                }
+
                 // Set the request parameter to follow redirections.
                 HttpParams params = request.getParams();
                 if (params instanceof ClientParamsStack) {
@@ -108,12 +126,20 @@ public class PKIConnection {
         httpClient.addResponseInterceptor(new HttpResponseInterceptor() {
             @Override
             public void process(HttpResponse response, HttpContext context) throws HttpException, IOException {
+
+                responseCounter++;
+
                 if (verbose) {
                     System.out.println("HTTP response: "+response.getStatusLine());
                     for (Header header : response.getAllHeaders()) {
                         System.out.println("  "+header.getName()+": "+header.getValue());
                     }
                 }
+
+                if (output != null) {
+                    File file = new File(output, "http-response-"+responseCounter);
+                    storeResponse(file, response);
+                }
             }
         });
 
@@ -155,6 +181,76 @@ public class PKIConnection {
         errorHandler = new ClientErrorHandler(providerFactory.getClientErrorInterceptors());
     }
 
+    public void storeRequest(File file, HttpRequest request) throws IOException {
+
+        try (PrintStream out = new PrintStream(file)) {
+
+            out.println(request.getRequestLine());
+
+            for (Header header : request.getAllHeaders()) {
+                out.println(header.getName()+": "+header.getValue());
+            }
+
+            out.println();
+
+            if (request instanceof EntityEnclosingRequestWrapper) {
+                EntityEnclosingRequestWrapper wrapper = (EntityEnclosingRequestWrapper) request;
+
+                HttpEntity entity = wrapper.getEntity();
+                if (entity == null) return;
+
+                if (!entity.isRepeatable()) {
+                    BufferedHttpEntity bufferedEntity = new BufferedHttpEntity(entity);
+                    wrapper.setEntity(bufferedEntity);
+                    entity = bufferedEntity;
+                }
+
+                storeEntity(out, entity);
+            }
+        }
+    }
+
+    public void storeResponse(File file, HttpResponse response) throws IOException {
+
+        try (PrintStream out = new PrintStream(file)) {
+
+            out.println(response.getStatusLine());
+
+            for (Header header : response.getAllHeaders()) {
+                out.println(header.getName()+": "+header.getValue());
+            }
+
+            out.println();
+
+            if (response instanceof BasicHttpResponse) {
+                BasicHttpResponse basicResponse = (BasicHttpResponse) response;
+
+                HttpEntity entity = basicResponse.getEntity();
+                if (entity == null) return;
+
+                if (!entity.isRepeatable()) {
+                    BufferedHttpEntity bufferedEntity = new BufferedHttpEntity(entity);
+                    basicResponse.setEntity(bufferedEntity);
+                    entity = bufferedEntity;
+                }
+
+                storeEntity(out, entity);
+            }
+        }
+    }
+
+    public void storeEntity(OutputStream out, HttpEntity entity) throws IOException {
+
+        byte[] buffer = new byte[1024];
+        int c;
+
+        try (InputStream in = entity.getContent()) {
+            while ((c = in.read(buffer)) > 0) {
+                out.write(buffer, 0, c);
+            }
+        }
+    }
+
     private class ServerCertApprovalCB implements SSLCertificateApprovalCallback {
         // NOTE:  The following helper method defined as
         //        'public String displayReason(int reason)'
@@ -372,6 +468,14 @@ public class PKIConnection {
         return request.post(String.class);
     }
 
+    public File getOutput() {
+        return output;
+    }
+
+    public void setOutput(File output) {
+        this.output = output;
+    }
+
     public boolean isVerbose() {
         return verbose;
     }
diff --git a/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java b/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java
index 191a6326da7c53475514ff650f41fe61f0a257bd..d4b57b7ab8e52674c94be0663f4475a85117ebae 100644
--- a/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java
@@ -50,6 +50,8 @@ public class MainCLI extends CLI {
     public PKIConnection connection;
     public AccountClient accountClient;
 
+    String output;
+
     public MainCLI() throws Exception {
         super("pki", "PKI command-line interface");
 
@@ -132,6 +134,10 @@ public class MainCLI extends CLI {
         option.setArgName("password");
         options.addOption(option);
 
+        option = new Option(null, "output", true, "Folder to store HTTP messages");
+        option.setArgName("folder");
+        options.addOption(option);
+
         options.addOption("v", false, "Verbose");
         options.addOption(null, "help", false, "Help");
         options.addOption(null, "version", false, "Version");
@@ -139,6 +145,9 @@ public class MainCLI extends CLI {
 
     public void parseOptions(CommandLine cmd) throws URISyntaxException {
 
+        verbose = cmd.hasOption("v");
+        output = cmd.getOptionValue("output");
+
         String uri = cmd.getOptionValue("U");
 
         String protocol = cmd.getOptionValue("P", "http");
@@ -174,6 +183,12 @@ public class MainCLI extends CLI {
         connection = new PKIConnection(config);
         connection.setVerbose(verbose);
 
+        if (output != null) {
+            File file = new File(output);
+            file.mkdirs();
+            connection.setOutput(file);
+        }
+
         accountClient = new AccountClient(connection);
     }
 
@@ -204,7 +219,7 @@ public class MainCLI extends CLI {
                 System.exit(1);
             }
 
-            verbose = cmd.hasOption("v");
+            parseOptions(cmd);
 
             if (verbose) {
                 System.out.print("Command:");
@@ -215,8 +230,6 @@ public class MainCLI extends CLI {
                 System.out.println();
             }
 
-            parseOptions(cmd);
-
             String command = cmdArgs[0];
             String moduleName;
             String moduleCommand;
-- 
1.8.1.4

