Log jax-ws http request and response - java

I need to log the full http request and response in a JAX-WS WebService call. For the request I need the request headers and the body and for the response, response headers and body.
After some researching, I've found that I can get this information with the property:
-Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true
and show the information that I need but it dumps it to the console and I need to store it in the database with an internal request id.
I've tried to implement a handler:
public class LoggingHandler implements SOAPHandler<SOAPMessageContext> {
#Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound) {
System.out.println("SOAP outbound!!!!!");
Map<String, List<String>> responseHeaders = (Map<String, List<String>>) context
.get(SOAPMessageContext.HTTP_RESPONSE_HEADERS);
try {
String headers = getHeaders(responseHeaders);
System.out.println(headers);
String body = getBody(context.getMessage());
System.out.println(body);
} catch (Exception ex) {
// TODO: What do I have to do in this case?
}
} else {
System.out.println("SOAP inbound!!!!!");
Map<String, List<String>> requestHeaders = (Map<String, List<String>>) context
.get(SOAPMessageContext.HTTP_REQUEST_HEADERS);
try {
String headers = getHeaders(requestHeaders);
System.out.println(headers);
String body = getBody(context.getMessage());
System.out.println(body);
} catch (Exception ex) {
// TODO: What do I have to do in this case?
}
}
return true;
}
private String getBody(SOAPMessage message) throws SOAPException, IOException {
OutputStream stream = new ByteArrayOutputStream();
message.writeTo(stream);
return stream.toString();
}
public String getFullHttpRequest(HttpServletRequest request) throws IOException {
InputStream in = request.getInputStream();
String encoding = request.getCharacterEncoding();
encoding = encoding == null ? "UTF-8" : encoding;
String body = IOUtils.toString(in, encoding);
return body;
}
private String getHeaders(Map<String, List<String>> headers) throws IOException {
StringBuffer result = new StringBuffer();
if (headers != null) {
for (Entry<String, List<String>> header : headers.entrySet()) {
if (header.getValue().isEmpty()) {
// I don't think this is legal, but let's just dump it,
// as the point of the dump is to uncover problems.
result.append(header.getValue());
} else {
for (String value : header.getValue()) {
result.append(header.getKey() + ": " + value);
}
}
result.append("\n");
}
}
return result.toString();
}
}
but in this case, I can get the http request headers and body but in the response, I only get the body, http response headers are always empty.
Any idea on how to archieve this? The objective is to be able to store the full http request and response in a database.
Thanks!!

You could also try
-Dcom.sun.xml.ws.transport.http.HttpAdapter.dump=true
I'm assuming you're providing your web service from within a Java EE application server of some sort (and not from a standalone client). You cannot have access to Java EE infrastructure like HttpServletRequest and HttpServletResponse outside of the context of a web/Java EE container.
You could try to get your hands on the actual servlet response object (within a web context) with
HttpServletResponse response = (HttpServletResponse) messageContext.get(SOAPMessageContext.SERVLET_RESPONSE); //messageContext is the SOAPMessageContext
List<String> responseHeaderNames = (List<String>)response.getHeaderNames();
for(String headerName : responseHeaderNames){
//Do whatever you want with it.
}
I seriously doubt that you'll be able to get your hands on the full response headers within a handler though. Your question really intrigued me and I've spent quite some time researching that part. In all the code samples I've seen, Not even the example on the metro site attempt to implement this functionality and I think the reason is simple. As at the point where a handler is invoked, the container may not have enough definitive information to stamp an http header on the outbound message. You might be able to add stuff but that's doubtful as well.

Related

How to intercept and edit response body in Zuul post filter?

I am using Zuul post filter to intercept the response. My requirement is to add one new field to response json. I'm able to intercept the response and edit it. But, unable to set the updated response to RequestContext.How it is possible to read a response body ,edit and update it back to RequestContext while using Zuul as a proxy in post filter?
Please find the below code i am using.
private void updateResponseBody(RequestContext ctx) throws IOException, JSONException {
final InputStream responseDataStream = ctx.getResponseDataStream();
String responseData = CharStreams.toString(new InputStreamReader(responseDataStream, "UTF-8"));
JSONObject jsonObj = new JSONObject(responseData);
JSONArray groupsArray = jsonObj.getJSONArray("list");
for (int i = 0; i < groupsArray.length(); i++) {
JSONObject groupId = groupsArray.getJSONObject(i);
groupId.accumulate("new_json_field_name", "new_json_field_value");
}
String updatedResponse = jsonObj.toString();
// ctx.setResponseBody(body); // also not working
ctx.setResponseDataStream(org.apache.commons.io.IOUtils.toInputStream(updatedResponse, "UTF-8"));
}
Error I am getting is :
Error while sending response to client: java.io.IOException: An existing connection was forcibly closed by the remote host.
Can anyone please help me on this.
I had the same error and got crazy modifying the code described in How to get response body in Zuul post filter? trying different possibilities. Finally I found the solution in this post by writing the answer in the OutputStream from servletResponse.getOutputStream() instead of ctx.setResponseDataStream():
HttpServletResponse servletResponse = ctx.getResponse();
...
String updatedResponse = jsonObj.toString();
try {
OutputStream outStream = servletResponse.getOutputStream();
outStream.write(updatedResponse.getBytes(), 0, updatedResponse.length());
outStream.flush();
outStream.close();
} catch (IOException e) {
log.warn("Error reading body", e);
}
I had a similar task and tried to do it by writing to the OutputStream. This worked, but had a strange side effect that it made the HttpHeaders in the response to be deleted or corrupted. This made the call produce CORS errors in production even though it ran fine locally through Postman.
I wrote the following method that I call from the run() method of my Post Zuul Filter to add a single node/value to the return Json.
private void addJsonNode(RequestContext requestContext,String name, String id) {
HttpServletResponse servletResponse = requestContext.getResponse();
try {
final InputStream responseDataStream = requestContext.getResponseDataStream();
String responseData = CharStreams.toString(new InputStreamReader(responseDataStream, "UTF-8"));
JSONObject jsonObject = new JSONObject(responseData);
jsonObject.put(name, id);
String updatedResponse = jsonObject.toString(4);
requestContext.setResponseBody(updatedResponse);
} catch (IOException e) {
log.warn("Error reading body", e);
} catch (JSONException e) {
log.warn("Error reading body", e);
}
}

Extending Nanohttp implementation to handle custom HTTP methods

I have been trying to use this cordova plugin, which uses NanoHttpd to handle requests.
By default, Nanohttpd handles some of the HTTP methods, like GET, POST, CONNECT, PROPFIND, PATCH, etc.
I have been trying to figure out how to implement a custom handler so that nanohttpd can handled more HTTP methods like: NOTIFY and SUBSCRIBE
#Override
public Response serve(IHTTPSession session) {
Log.d(this.getClass().getName(), "New request is incoming!");
String requestUUID = UUID.randomUUID().toString();
PluginResult pluginResult = null;
try {
pluginResult = new PluginResult(
PluginResult.Status.OK, this.createJSONRequest(requestUUID, session));
} catch (JSONException e) {
e.printStackTrace();
}
pluginResult.setKeepCallback(true);
this.webserver.onRequestCallbackContext.sendPluginResult(pluginResult);
while (!this.webserver.responses.containsKey(requestUUID)) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
JSONObject responseObject = (JSONObject) this.webserver.responses.get(requestUUID);
Log.d(this.getClass().getName(), "responseObject: " + responseObject.toString());
Response response = null;
try {
response = newFixedLengthResponse(
Response.Status.lookup(responseObject.getInt("status")),
getContentType(responseObject),
responseObject.getString("body")
);
Iterator<?> keys = responseObject.getJSONObject("headers").keys();
while (keys.hasNext()) {
String key = (String) keys.next();
response.addHeader(
key,
responseObject.getJSONObject("headers").getString(key)
);
}
} catch (JSONException e) {
e.printStackTrace();
}
return response;
}
I added a simple notify Response to handle any incoming request, referring from here - https://stackoverflow.com/a/27645191/2096740
public Response notify(IHTTPSession session) {
StringBuilder text = new StringBuilder("<html><body>");
text.append("<h1>Url: ");
text.append(session.getUri());
text.append("</h1><br>");
Map<String, String> queryParams = session.getParms();
if (queryParams.size() > 0) {
for (Map.Entry<String, String> entry : queryParams.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
text.append("<p>Param '");
text.append(key);
text.append("' = ");
text.append(value);
text.append("</p>");
}
} else {
text.append("<p>no params in url</p><br>");
}
return newFixedLengthResponse(text.toString());
}
But this returnsBAD REQUEST: Syntax error. HTTP verb NOTIFY unhandled.
Documentation is not clear and there is not much info circulating on extending Nanohttpd behavior on SO or via web results.
What is the correct way to do this? How can I extend it ?
The check for Method is actually locked in an enum. It is hardcoded and there is no other method to expand.
The getMethod instance itself is a enum type of Method.
Since, I couldn't find any other solution, I therefore conclude it is not possible to do this stuff in Nanohttpd. All its versions in Maven dont support this.
The reason they have
Some built-in support for HEAD, POST and DELETE requests. You can
easily implement/customize any HTTP method, though.
mentioned in their feature list is because the original version had method as a String. It has changed since.
Feature list not been updated to reflect this change.

remove security tag from MessageContext request

Sorry for multiple edits.Should have reviewed before posting
I am using the getRequest method provided by MessageContext (org.springframework.ws.context.MessageContext) to retrieve the backend soap request in an interceptor that extends ClientInterceptor. This is beind done within handleRequest method. I am fetching this to log the request into a file. When I do this , the security section within the header which has the user id and password is also getting logged. I would like to remove this before logging. Are there any available mechanisms to remove this element or should I manipulate the String in order to take the element out?
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
getPayloadFromSoapMessage((SoapMessage) messageContext.getRequest());
}
protected String getPayloadFromSoapMessage(SoapMessage message) {
String payload = "Error parsing";
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
message.writeTo(bos);
payload = bos.toString();
} catch (IOException e) {
LOG.error("Error parsing the SoapMessage", e);
}
return payload;
}

How to read HTTP custom error string

I'm trying to read custom error message I sent along an HTTP response for a web API project using Java.
Currently, I have this piece of Java code to read Header Response,
import java.net.HttpURLConnection;
import java.net.URL;
public class URLReader {
public static void main(String[] args) throws Exception {
URL oracle = new URL(URL);
HttpURLConnection connection =(HttpURLConnection)oracle.openConnection();
connection.setRequestMethod("GET");
connection.connect();
System.out.println(connection.getHeaderField(0));
}
}
An output of HTTP response header looks like this (Fiddler):
How can I get My Error Message text using Java?
#Abzal Kalimbetov is wrong about getErrorStream(), which will return an InputStream when an exception is raised, indicating response code >= 400 is received from the server.
Recap:
InputStream inputStream = null;
try {
// normal operation
inputStream = connection.getInputStream();
}
catch(IOException exception)
{
inputStream = connection.getErrorStream();
//#TODO you can now extract your custom error message from inputStream.
}
If the request status is greater than or equal 400, you use getErrorStream() method
if(connection.getResponseCode()>=400){
String myErrorMessage = connection.getErrorStream();
}
You can set a header in your server application which you then can read out very simple like this: ( Servlet usage is needed for this. )
private void test(ServletRequest request, ServletResponse response) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.addHeader( "myErrorMessage", "this is the message" );
String message = httpServletRequest.getHeader( "myErrorMessage" );
}
if you don't have these resources available it is possible to get the message like this:
//get all headers
Map<String, List<String>> map = connection.getHeaderFields();
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
System.out.println("Key : " + entry.getKey() +
" ,Value : " + entry.getValue());
}
//get header by 'key'
String server = connection.getHeaderField("Server");

Restricting specific file extensions getting uploaded (server side)

I am working on below scenario since last two days,
I have developed a java filter that check whether request is multipart type,
If it is, I want to restrict .php file getting uploaded.
In servlet filter I successfully retrieved type of file, if it's valid one, i have forwarded that request to proceed.
Now my business logic that was working exactly fine without filter is now failed to upload.
My project is using Spring framework.
At business logic, I'm using MultipartRequest(class of spring) as casting in.
Invoking request.getFileNames() which is returning nothing after involving filter.
In filter, I have wrapped request after validating file extensions as follows:
All form fields are set in parameter map that will be passed with request.
And File type field is set as attribute in request object.
Would you guys please help?
Thanks,
Namrata Shah
In doFilter(),
I have checked whether received request is instance of HttpServletRequest. If yes, then it'll be parsed if it's a multipart req using below code:
parseRequest():
{
List<FileItem> multipartItems = null;
try
{ multipartItems = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
}
catch (FileUploadException e)
{
throw new ServletException("Cannot parse multipart request: " + e.getMessage());
}
Map<String, String[]> parameterMap = new HashMap<String, String[]>();
for (FileItem multipartItem : multipartItems)
{
if (multipartItem.isFormField())
processFormField(multipartItem, parameterMap);
else
processFileField(multipartItem, request);
}
return wrapRequest(request, parameterMap);
}
Code for processFileField():
request.setAttribute(fileField.getFieldName(), fileField);
then, I wrap request as follows:
private static HttpServletRequest wrapRequest(
HttpServletRequest request, final Map<String, String[]> parameterMap)
{
return new HttpServletRequestWrapper(request)
{
#Override
public Map<String, String[]> getParameterMap()
{
return parameterMap;
}
public String[] getParameterValues(String name)
{
return parameterMap.get(name);
}
public String getParameter(String name)
{
String[] params = getParameterValues(name);
return params != null && params.length > 0 ? params[0] : null;
}
public Enumeration<String> getParameterNames()
{
return Collections.enumeration(parameterMap.keySet());
}
};
}
Code for processFormField():
String name = formField.getFieldName(),value = formField.getString();
String[] values = parameterMap.get(name);
if (values == null)
{
parameterMap.put(name, new String[]{value});
}
else
{
int length = values.length;
String[] newValues = new String[length + 1];
System.arraycopy(values, 0, newValues, 0, length);
newValues[length] = value;
parameterMap.put(name, newValues);
}

Categories

Resources