001package org.hl7.fhir.r4.utils.client;
002
003
004
005
006
007/*
008  Copyright (c) 2011+, HL7, Inc.
009  All rights reserved.
010
011  Redistribution and use in source and binary forms, with or without modification, 
012  are permitted provided that the following conditions are met:
013
014 * Redistributions of source code must retain the above copyright notice, this 
015     list of conditions and the following disclaimer.
016 * Redistributions in binary form must reproduce the above copyright notice, 
017     this list of conditions and the following disclaimer in the documentation 
018     and/or other materials provided with the distribution.
019 * Neither the name of HL7 nor the names of its contributors may be used to 
020     endorse or promote products derived from this software without specific 
021     prior written permission.
022
023  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
024  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
025  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
026  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
027  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
028  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
029  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
030  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
031  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
032  POSSIBILITY OF SUCH DAMAGE.
033
034 */
035
036
037import java.io.ByteArrayOutputStream;
038import java.io.IOException;
039import java.io.InputStream;
040import java.io.OutputStreamWriter;
041import java.io.UnsupportedEncodingException;
042import java.net.HttpURLConnection;
043import java.net.MalformedURLException;
044import java.net.URI;
045import java.net.URLConnection;
046import java.nio.charset.StandardCharsets;
047import java.text.ParseException;
048import java.text.SimpleDateFormat;
049import java.util.ArrayList;
050import java.util.Calendar;
051import java.util.Date;
052import java.util.List;
053import java.util.Locale;
054import java.util.Map;
055
056import org.apache.commons.codec.binary.Base64;
057import org.apache.commons.io.IOUtils;
058import org.apache.commons.lang3.StringUtils;
059import org.apache.http.Header;
060import org.apache.http.HttpEntityEnclosingRequest;
061import org.apache.http.HttpHost;
062import org.apache.http.HttpRequest;
063import org.apache.http.HttpResponse;
064import org.apache.http.client.HttpClient;
065import org.apache.http.client.methods.HttpDelete;
066import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
067import org.apache.http.client.methods.HttpGet;
068import org.apache.http.client.methods.HttpOptions;
069import org.apache.http.client.methods.HttpPost;
070import org.apache.http.client.methods.HttpPut;
071import org.apache.http.client.methods.HttpUriRequest;
072import org.apache.http.conn.params.ConnRoutePNames;
073import org.apache.http.entity.ByteArrayEntity;
074import org.apache.http.impl.client.DefaultHttpClient;
075import org.apache.http.params.HttpConnectionParams;
076import org.apache.http.params.HttpParams;
077import org.hl7.fhir.r4.formats.IParser;
078import org.hl7.fhir.r4.formats.IParser.OutputStyle;
079import org.hl7.fhir.r4.formats.JsonParser;
080import org.hl7.fhir.r4.formats.XmlParser;
081import org.hl7.fhir.r4.model.Bundle;
082import org.hl7.fhir.r4.model.OperationOutcome;
083import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity;
084import org.hl7.fhir.r4.model.OperationOutcome.OperationOutcomeIssueComponent;
085import org.hl7.fhir.r4.model.Resource;
086import org.hl7.fhir.r4.model.ResourceType;
087import org.hl7.fhir.r4.utils.ResourceUtilities;
088import org.hl7.fhir.utilities.ToolingClientLogger;
089import org.hl7.fhir.utilities.Utilities;
090
091/**
092 * Helper class handling lower level HTTP transport concerns.
093 * TODO Document methods.
094 * @author Claude Nanjo
095 */
096public class ClientUtils {
097
098  public static final String DEFAULT_CHARSET = "UTF-8";
099  public static final String HEADER_LOCATION = "location";
100  private static boolean debugging = false;
101  public static final int TIMEOUT_SOCKET = 5000;
102  public static final int TIMEOUT_CONNECT = 1000;
103
104  private HttpHost proxy;
105  private int timeout = TIMEOUT_SOCKET;
106  private String username;
107  private String password;
108  private ToolingClientLogger logger;
109  private int retryCount;
110  private HttpClient httpclient;
111
112  public HttpHost getProxy() {
113    return proxy;
114  }
115
116  public void setProxy(HttpHost proxy) {
117    this.proxy = proxy;
118  }
119
120  public int getTimeout() {
121    return timeout;
122  }
123
124  public void setTimeout(int timeout) {
125    this.timeout = timeout;
126  }
127
128  public String getUsername() {
129    return username;
130  }
131
132  public void setUsername(String username) {
133    this.username = username;
134  }
135
136  public String getPassword() {
137    return password;
138  }
139
140  public void setPassword(String password) {
141    this.password = password;
142  }
143
144  public <T extends Resource> ResourceRequest<T> issueOptionsRequest(URI optionsUri, String resourceFormat, String message, int timeout) {
145    HttpOptions options = new HttpOptions(optionsUri);
146    return issueResourceRequest(resourceFormat, options, message, timeout);
147  }
148
149  public <T extends Resource> ResourceRequest<T> issueGetResourceRequest(URI resourceUri, String resourceFormat, String message, int timeout) {
150    HttpGet httpget = new HttpGet(resourceUri);
151    return issueResourceRequest(resourceFormat, httpget, message, timeout);
152  }
153
154  public <T extends Resource> ResourceRequest<T> issuePutRequest(URI resourceUri, byte[] payload, String resourceFormat, List<Header> headers, String message, int timeout) {
155    HttpPut httpPut = new HttpPut(resourceUri);
156    return issueResourceRequest(resourceFormat, httpPut, payload, headers, message, timeout);
157  }
158
159  public <T extends Resource> ResourceRequest<T> issuePutRequest(URI resourceUri, byte[] payload, String resourceFormat, String message, int timeout) {
160    HttpPut httpPut = new HttpPut(resourceUri);
161    return issueResourceRequest(resourceFormat, httpPut, payload, null, message, timeout);
162  }
163
164  public <T extends Resource> ResourceRequest<T> issuePostRequest(URI resourceUri, byte[] payload, String resourceFormat, List<Header> headers, String message, int timeout) {
165    HttpPost httpPost = new HttpPost(resourceUri);
166    return issueResourceRequest(resourceFormat, httpPost, payload, headers, message, timeout);
167  }
168
169
170  public <T extends Resource> ResourceRequest<T> issuePostRequest(URI resourceUri, byte[] payload, String resourceFormat, String message, int timeout) {
171    return issuePostRequest(resourceUri, payload, resourceFormat, null, message, timeout);
172  }
173
174  public Bundle issueGetFeedRequest(URI resourceUri, String resourceFormat) {
175    HttpGet httpget = new HttpGet(resourceUri);
176    configureFhirRequest(httpget, resourceFormat);
177    HttpResponse response = sendRequest(httpget);
178    return unmarshalReference(response, resourceFormat);
179  }
180
181  private void setAuth(HttpRequest httpget) {
182    if (password != null) {
183      try {
184        byte[] b = Base64.encodeBase64((username+":"+password).getBytes("ASCII"));
185        String b64 = new String(b, StandardCharsets.US_ASCII);
186        httpget.setHeader("Authorization", "Basic " + b64);
187      } catch (UnsupportedEncodingException e) {
188      }
189    }
190  }
191
192  public Bundle postBatchRequest(URI resourceUri, byte[] payload, String resourceFormat, String message, int timeout) {
193    HttpPost httpPost = new HttpPost(resourceUri);
194    configureFhirRequest(httpPost, resourceFormat);
195    HttpResponse response = sendPayload(httpPost, payload, proxy, message, timeout);
196    return unmarshalFeed(response, resourceFormat);
197  }
198
199  public boolean issueDeleteRequest(URI resourceUri) {
200    HttpDelete deleteRequest = new HttpDelete(resourceUri);
201    HttpResponse response = sendRequest(deleteRequest);
202    int responseStatusCode = response.getStatusLine().getStatusCode();
203    boolean deletionSuccessful = false;
204    if(responseStatusCode == 204) {
205      deletionSuccessful = true;
206    }
207    return deletionSuccessful;
208  }
209
210  /***********************************************************
211   * Request/Response Helper methods
212   ***********************************************************/
213
214  protected <T extends Resource> ResourceRequest<T> issueResourceRequest(String resourceFormat, HttpUriRequest request, String message, int timeout) {
215    return issueResourceRequest(resourceFormat, request, null, message, timeout);
216  }
217
218  /**
219   * @param resourceFormat
220   * @param options
221   * @return
222   */
223  protected <T extends Resource> ResourceRequest<T> issueResourceRequest(String resourceFormat, HttpUriRequest request, byte[] payload, String message, int timeout) {
224    return issueResourceRequest(resourceFormat, request, payload, null, message, timeout);
225  }
226
227  /**
228   * @param resourceFormat
229   * @param options
230   * @return
231   */
232  protected <T extends Resource> ResourceRequest<T> issueResourceRequest(String resourceFormat, HttpUriRequest request, byte[] payload, List<Header> headers, String message, int timeout) {
233    configureFhirRequest(request, resourceFormat, headers);
234    HttpResponse response = null;
235    if(request instanceof HttpEntityEnclosingRequest && payload != null) {
236      response = sendPayload((HttpEntityEnclosingRequestBase)request, payload, proxy, message, timeout);
237    } else if (request instanceof HttpEntityEnclosingRequest && payload == null){
238      throw new EFhirClientException("PUT and POST requests require a non-null payload");
239    } else {
240      response = sendRequest(request);
241    }
242    T resource = unmarshalReference(response, resourceFormat);
243    return new ResourceRequest<T>(resource, response.getStatusLine().getStatusCode(), getLocationHeader(response));
244  }
245
246
247  /**
248   * Method adds required request headers.
249   * TODO handle JSON request as well.
250   * 
251   * @param request
252   */
253  protected void configureFhirRequest(HttpRequest request, String format) {
254    configureFhirRequest(request, format, null);
255  }
256
257  /**
258   * Method adds required request headers.
259   * TODO handle JSON request as well.
260   * 
261   * @param request
262   */
263  protected void configureFhirRequest(HttpRequest request, String format, List<Header> headers) {
264    request.addHeader("User-Agent", "Java FHIR Client for FHIR");
265
266    if (format != null) {               
267      request.addHeader("Accept",format);
268      request.addHeader("Content-Type", format + ";charset=" + DEFAULT_CHARSET);
269    }
270    request.addHeader("Accept-Charset", DEFAULT_CHARSET);
271    if(headers != null) {
272      for(Header header : headers) {
273        request.addHeader(header);
274      }
275    }
276    setAuth(request);
277  }
278
279  /**
280   * Method posts request payload
281   * 
282   * @param request
283   * @param payload
284   * @return
285   */
286  @SuppressWarnings({ "resource", "deprecation" })
287  protected HttpResponse sendPayload(HttpEntityEnclosingRequestBase request, byte[] payload, HttpHost proxy, String message, int timeout) {
288    HttpResponse response = null;
289    boolean ok = false;
290    long t = System.currentTimeMillis();
291    int tryCount = 0;
292    while (!ok) {
293      try {
294        tryCount++;
295        if (httpclient == null) {
296          makeClient(proxy);
297        }
298      HttpParams params = httpclient.getParams();
299      HttpConnectionParams.setSoTimeout(params, timeout < 1 ? this.timeout : timeout * 1000);
300        request.setEntity(new ByteArrayEntity(payload));
301        log(request);
302        response = httpclient.execute(request);
303        ok = true;
304      } catch(IOException ioe) {
305        System.out.println(ioe.getMessage()+" ("+(System.currentTimeMillis()-t)+"ms / "+Utilities.describeSize(payload.length)+" for "+message+")");
306        if (tryCount <= retryCount || (tryCount < 3 && ioe instanceof org.apache.http.conn.ConnectTimeoutException)) {
307          ok = false;
308          try {
309            Thread.sleep(100);
310          } catch (InterruptedException e) {
311          }
312        } else {
313          if (tryCount > 1) {
314            System.out.println("Giving up: "+ioe.getMessage()+" (R4 / "+(System.currentTimeMillis()-t)+"ms / "+Utilities.describeSize(payload.length)+" for "+message+")");
315          }
316          throw new EFhirClientException("Error sending HTTP Post/Put Payload: "+ioe.getMessage(), ioe);
317        }
318      }
319    }
320    return response;
321  }
322
323  @SuppressWarnings("deprecation")
324  public void makeClient(HttpHost proxy) {
325    httpclient = new DefaultHttpClient();
326    HttpParams params = httpclient.getParams();
327    HttpConnectionParams.setConnectionTimeout(params, TIMEOUT_CONNECT);
328    HttpConnectionParams.setSoTimeout(params, timeout);
329    HttpConnectionParams.setSoKeepalive(params, true);
330    if(proxy != null) {
331      httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
332    }
333  }
334
335  /**
336   * 
337   * @param request
338   * @param payload
339   * @return
340   */
341  protected HttpResponse sendRequest(HttpUriRequest request) {
342    HttpResponse response = null;
343    try {
344      if (httpclient == null) {
345        makeClient(proxy);
346      }
347      response = httpclient.execute(request);
348    } catch(IOException ioe) {
349      if (ClientUtils.debugging ) {
350        ioe.printStackTrace();
351      }
352      throw new EFhirClientException("Error sending Http Request: "+ioe.getMessage(), ioe);
353    }
354    return response;
355  }
356
357
358  /**
359   * Unmarshals a resource from the response stream.
360   * 
361   * @param response
362   * @return
363   */
364  @SuppressWarnings("unchecked")
365  protected <T extends Resource> T unmarshalReference(HttpResponse response, String format) {
366    T resource = null;
367    OperationOutcome error = null;
368    byte[] cnt = log(response);
369    if (cnt != null) {
370      try {
371        resource = (T)getParser(format).parse(cnt);
372        if (resource instanceof OperationOutcome && hasError((OperationOutcome)resource)) {
373          error = (OperationOutcome) resource;
374        }
375      } catch(IOException ioe) {
376        throw new EFhirClientException("Error reading Http Response: "+ioe.getMessage(), ioe);
377      } catch(Exception e) {
378        throw new EFhirClientException("Error parsing response message: "+e.getMessage(), e);
379      }
380    }
381    if(error != null) {
382      throw new EFhirClientException("Error from server: "+ResourceUtilities.getErrorDescription(error), error);
383    }
384    return resource;
385  }
386
387  /**
388   * Unmarshals Bundle from response stream.
389   * 
390   * @param response
391   * @return
392   */
393  protected Bundle unmarshalFeed(HttpResponse response, String format) {
394    Bundle feed = null;
395    byte[] cnt = log(response);
396    String contentType = response.getHeaders("Content-Type")[0].getValue();
397    OperationOutcome error = null;
398    try {
399      if (cnt != null) {
400        if(contentType.contains(ResourceFormat.RESOURCE_XML.getHeader()) || contentType.contains("text/xml+fhir")) {
401          Resource rf = getParser(format).parse(cnt);
402          if (rf instanceof Bundle)
403            feed = (Bundle) rf;
404          else if (rf instanceof OperationOutcome && hasError((OperationOutcome) rf)) {
405            error = (OperationOutcome) rf;
406          } else {
407            throw new EFhirClientException("Error reading server response: a resource was returned instead");
408          }
409        }
410      }
411    } catch(IOException ioe) {
412      throw new EFhirClientException("Error reading Http Response", ioe);
413    } catch(Exception e) {
414      throw new EFhirClientException("Error parsing response message", e);
415    }
416    if(error != null) {
417      throw new EFhirClientException("Error from server: "+ResourceUtilities.getErrorDescription(error), error);
418    }
419    return feed;
420  }
421
422  private boolean hasError(OperationOutcome oo) {
423    for (OperationOutcomeIssueComponent t : oo.getIssue())
424      if (t.getSeverity() == IssueSeverity.ERROR || t.getSeverity() == IssueSeverity.FATAL)
425        return true;
426    return false;
427  }
428
429  protected String getLocationHeader(HttpResponse response) {
430    String location = null;
431    if(response.getHeaders("location").length > 0) {//TODO Distinguish between both cases if necessary
432      location = response.getHeaders("location")[0].getValue();
433    } else if(response.getHeaders("content-location").length > 0) {
434      location = response.getHeaders("content-location")[0].getValue();
435    }
436    return location;
437  }
438
439
440  /*****************************************************************
441   * Client connection methods
442   * ***************************************************************/
443
444  public HttpURLConnection buildConnection(URI baseServiceUri, String tail) {
445    try {
446      HttpURLConnection client = (HttpURLConnection) baseServiceUri.resolve(tail).toURL().openConnection();
447      return client;
448    } catch(MalformedURLException mue) {
449      throw new EFhirClientException("Invalid Service URL", mue);
450    } catch(IOException ioe) {
451      throw new EFhirClientException("Unable to establish connection to server: " + baseServiceUri.toString() + tail, ioe);
452    }
453  }
454
455  public HttpURLConnection buildConnection(URI baseServiceUri, ResourceType resourceType, String id) {
456    return buildConnection(baseServiceUri, ResourceAddress.buildRelativePathFromResourceType(resourceType, id));
457  }
458
459  /******************************************************************
460   * Other general helper methods
461   * ****************************************************************/
462
463
464  public  <T extends Resource>  byte[] getResourceAsByteArray(T resource, boolean pretty, boolean isJson) {
465    ByteArrayOutputStream baos = null;
466    byte[] byteArray = null;
467    try {
468      baos = new ByteArrayOutputStream();
469      IParser parser = null;
470      if(isJson) {
471        parser = new JsonParser();
472      } else {
473        parser = new XmlParser();
474      }
475      parser.setOutputStyle(pretty ? OutputStyle.PRETTY : OutputStyle.NORMAL);
476      parser.compose(baos, resource);
477      baos.close();
478      byteArray =  baos.toByteArray();
479      baos.close();
480    } catch (Exception e) {
481      try{
482        baos.close();
483      }catch(Exception ex) {
484        throw new EFhirClientException("Error closing output stream", ex);
485      }
486      throw new EFhirClientException("Error converting output stream to byte array", e);
487    }
488    return byteArray;
489  }
490
491  public  byte[] getFeedAsByteArray(Bundle feed, boolean pretty, boolean isJson) {
492    ByteArrayOutputStream baos = null;
493    byte[] byteArray = null;
494    try {
495      baos = new ByteArrayOutputStream();
496      IParser parser = null;
497      if(isJson) {
498        parser = new JsonParser();
499      } else {
500        parser = new XmlParser();
501      }
502      parser.setOutputStyle(pretty ? OutputStyle.PRETTY : OutputStyle.NORMAL);
503      parser.compose(baos, feed);
504      baos.close();
505      byteArray =  baos.toByteArray();
506      baos.close();
507    } catch (Exception e) {
508      try{
509        baos.close();
510      }catch(Exception ex) {
511        throw new EFhirClientException("Error closing output stream", ex);
512      }
513      throw new EFhirClientException("Error converting output stream to byte array", e);
514    }
515    return byteArray;
516  }
517
518  public Calendar getLastModifiedResponseHeaderAsCalendarObject(URLConnection serverConnection) {
519    String dateTime = null;
520    try {
521      dateTime = serverConnection.getHeaderField("Last-Modified");
522      SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", new Locale("en", "US"));
523      Date lastModifiedTimestamp = format.parse(dateTime);
524      Calendar calendar=Calendar.getInstance();
525      calendar.setTime(lastModifiedTimestamp);
526      return calendar;
527    } catch(ParseException pe) {
528      throw new EFhirClientException("Error parsing Last-Modified response header " + dateTime, pe);
529    }
530  }
531
532  protected IParser getParser(String format) {
533    if(StringUtils.isBlank(format)) {
534      format = ResourceFormat.RESOURCE_XML.getHeader();
535    }
536    if(format.equalsIgnoreCase("json") || format.equalsIgnoreCase(ResourceFormat.RESOURCE_JSON.getHeader()) || format.equalsIgnoreCase(ResourceFormat.RESOURCE_JSON.getHeader())) {
537      return new JsonParser();
538    } else if(format.equalsIgnoreCase("xml") || format.equalsIgnoreCase(ResourceFormat.RESOURCE_XML.getHeader()) || format.equalsIgnoreCase(ResourceFormat.RESOURCE_XML.getHeader())) {
539      return new XmlParser();
540    } else {
541      throw new EFhirClientException("Invalid format: " + format);
542    }
543  }
544
545  public Bundle issuePostFeedRequest(URI resourceUri, Map<String, String> parameters, String resourceName, Resource resource, String resourceFormat) throws IOException {
546    HttpPost httppost = new HttpPost(resourceUri);
547    String boundary = "----WebKitFormBoundarykbMUo6H8QaUnYtRy";
548    httppost.addHeader("Content-Type", "multipart/form-data; boundary="+boundary);
549    httppost.addHeader("Accept", resourceFormat);
550    configureFhirRequest(httppost, null);
551    HttpResponse response = sendPayload(httppost, encodeFormSubmission(parameters, resourceName, resource, boundary));
552    return unmarshalFeed(response, resourceFormat);
553  }
554
555  private byte[] encodeFormSubmission(Map<String, String> parameters, String resourceName, Resource resource, String boundary) throws IOException {
556    ByteArrayOutputStream b = new ByteArrayOutputStream();
557    OutputStreamWriter w = new OutputStreamWriter(b, "UTF-8");  
558    for (String name : parameters.keySet()) {
559      w.write("--");
560      w.write(boundary);
561      w.write("\r\nContent-Disposition: form-data; name=\""+name+"\"\r\n\r\n");
562      w.write(parameters.get(name)+"\r\n");
563    }
564    w.write("--");
565    w.write(boundary);
566    w.write("\r\nContent-Disposition: form-data; name=\""+resourceName+"\"\r\n\r\n");
567    w.close(); 
568    JsonParser json = new JsonParser();
569    json.setOutputStyle(OutputStyle.NORMAL);
570    json.compose(b, resource);
571    b.close();
572    w = new OutputStreamWriter(b, "UTF-8");  
573    w.write("\r\n--");
574    w.write(boundary);
575    w.write("--");
576    w.close();
577    return b.toByteArray();
578  }
579
580  /**
581   * Method posts request payload
582   * 
583   * @param request
584   * @param payload
585   * @return
586   */
587  protected HttpResponse sendPayload(HttpEntityEnclosingRequestBase request, byte[] payload) {
588    HttpResponse response = null;
589    try {
590      log(request);
591      if (httpclient == null) {
592        makeClient(proxy);
593      }
594      request.setEntity(new ByteArrayEntity(payload));
595      response = httpclient.execute(request);
596      log(response);
597    } catch(IOException ioe) {
598      throw new EFhirClientException("Error sending HTTP Post/Put Payload: "+ioe.getMessage(), ioe);
599    }
600    return response;
601  }
602
603  private void log(HttpUriRequest request) {
604    if (logger != null) {
605      List<String> headers = new ArrayList<>();
606      for (Header h : request.getAllHeaders()) {
607        headers.add(h.toString());
608      }
609      logger.logRequest(request.getMethod(), request.getURI().toString(), headers, null);
610    }    
611  }
612  private void log(HttpEntityEnclosingRequestBase request)  {
613    if (logger != null) {
614      List<String> headers = new ArrayList<>();
615      for (Header h : request.getAllHeaders()) {
616        headers.add(h.toString());
617      }
618      byte[] cnt = null;
619      InputStream s;
620      try {
621        s = request.getEntity().getContent();
622        cnt = IOUtils.toByteArray(s);
623        s.close();
624      } catch (Exception e) {
625      }
626      logger.logRequest(request.getMethod(), request.getURI().toString(), headers, cnt);
627    }    
628  }  
629
630  private byte[] log(HttpResponse response) {
631    byte[] cnt = null;
632    try {
633      InputStream s = response.getEntity().getContent();
634      cnt = IOUtils.toByteArray(s);
635      s.close();
636    } catch (Exception e) {
637    }
638    if (logger != null) {
639      List<String> headers = new ArrayList<>();
640      for (Header h : response.getAllHeaders()) {
641        headers.add(h.toString());
642      }
643      logger.logResponse(response.getStatusLine().toString(), headers, cnt);
644    }
645    return cnt;
646  }
647
648  public ToolingClientLogger getLogger() {
649    return logger;
650  }
651
652  public void setLogger(ToolingClientLogger logger) {
653    this.logger = logger;
654  }
655
656
657  /**
658   * Used for debugging
659   * 
660   * @param instream
661   * @return
662   */
663  protected String writeInputStreamAsString(InputStream instream) {
664    String value = null;
665    try {
666      value = IOUtils.toString(instream, "UTF-8");
667      System.out.println(value);
668
669    } catch(IOException ioe) {
670      //Do nothing
671    }
672    return value;
673  }
674
675  public int getRetryCount() {
676    return retryCount;
677  }
678
679  public void setRetryCount(int retryCount) {
680    this.retryCount = retryCount;
681  }
682
683
684}