001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.rest.client; 019 020import java.io.BufferedInputStream; 021import java.io.ByteArrayInputStream; 022import java.io.ByteArrayOutputStream; 023import java.io.File; 024import java.io.IOException; 025import java.io.InputStream; 026import java.net.URI; 027import java.net.URISyntaxException; 028import java.net.URL; 029import java.nio.file.Files; 030import java.security.KeyManagementException; 031import java.security.KeyStore; 032import java.security.KeyStoreException; 033import java.security.NoSuchAlgorithmException; 034import java.security.cert.CertificateException; 035import java.util.Collections; 036import java.util.Map; 037import java.util.Optional; 038import java.util.concurrent.ConcurrentHashMap; 039import javax.net.ssl.SSLContext; 040import org.apache.hadoop.conf.Configuration; 041import org.apache.hadoop.hbase.HBaseConfiguration; 042import org.apache.hadoop.hbase.rest.Constants; 043import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 044import org.apache.hadoop.security.authentication.client.AuthenticatedURL; 045import org.apache.hadoop.security.authentication.client.AuthenticationException; 046import org.apache.hadoop.security.authentication.client.KerberosAuthenticator; 047import org.apache.http.Header; 048import org.apache.http.HttpResponse; 049import org.apache.http.HttpStatus; 050import org.apache.http.client.HttpClient; 051import org.apache.http.client.config.RequestConfig; 052import org.apache.http.client.methods.HttpDelete; 053import org.apache.http.client.methods.HttpGet; 054import org.apache.http.client.methods.HttpHead; 055import org.apache.http.client.methods.HttpPost; 056import org.apache.http.client.methods.HttpPut; 057import org.apache.http.client.methods.HttpUriRequest; 058import org.apache.http.entity.InputStreamEntity; 059import org.apache.http.impl.client.HttpClientBuilder; 060import org.apache.http.impl.client.HttpClients; 061import org.apache.http.message.BasicHeader; 062import org.apache.http.ssl.SSLContexts; 063import org.apache.http.util.EntityUtils; 064import org.apache.yetus.audience.InterfaceAudience; 065import org.slf4j.Logger; 066import org.slf4j.LoggerFactory; 067 068/** 069 * A wrapper around HttpClient which provides some useful function and semantics for interacting 070 * with the REST gateway. 071 */ 072@InterfaceAudience.Public 073public class Client { 074 public static final Header[] EMPTY_HEADER_ARRAY = new Header[0]; 075 076 private static final Logger LOG = LoggerFactory.getLogger(Client.class); 077 078 private HttpClient httpClient; 079 private Cluster cluster; 080 private Configuration conf; 081 private boolean sslEnabled; 082 private HttpResponse resp; 083 private HttpGet httpGet = null; 084 085 private Map<String, String> extraHeaders; 086 087 private static final String AUTH_COOKIE = "hadoop.auth"; 088 private static final String AUTH_COOKIE_EQ = AUTH_COOKIE + "="; 089 private static final String COOKIE = "Cookie"; 090 091 /** 092 * Default Constructor 093 */ 094 public Client() { 095 this(null); 096 } 097 098 private void initialize(Cluster cluster, Configuration conf, boolean sslEnabled, 099 Optional<KeyStore> trustStore) { 100 this.cluster = cluster; 101 this.conf = conf; 102 this.sslEnabled = sslEnabled; 103 extraHeaders = new ConcurrentHashMap<>(); 104 String clspath = System.getProperty("java.class.path"); 105 LOG.debug("classpath " + clspath); 106 HttpClientBuilder httpClientBuilder = HttpClients.custom(); 107 108 int connTimeout = this.conf.getInt(Constants.REST_CLIENT_CONN_TIMEOUT, 109 Constants.DEFAULT_REST_CLIENT_CONN_TIMEOUT); 110 int socketTimeout = this.conf.getInt(Constants.REST_CLIENT_SOCKET_TIMEOUT, 111 Constants.DEFAULT_REST_CLIENT_SOCKET_TIMEOUT); 112 RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(connTimeout) 113 .setSocketTimeout(socketTimeout).setNormalizeUri(false) // URIs should not be normalized, see 114 // HBASE-26903 115 .build(); 116 httpClientBuilder.setDefaultRequestConfig(requestConfig); 117 118 // Since HBASE-25267 we don't use the deprecated DefaultHttpClient anymore. 119 // The new http client would decompress the gzip content automatically. 120 // In order to keep the original behaviour of this public class, we disable 121 // automatic content compression. 122 httpClientBuilder.disableContentCompression(); 123 124 if (sslEnabled && trustStore.isPresent()) { 125 try { 126 SSLContext sslcontext = 127 SSLContexts.custom().loadTrustMaterial(trustStore.get(), null).build(); 128 httpClientBuilder.setSSLContext(sslcontext); 129 } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) { 130 throw new ClientTrustStoreInitializationException("Error while processing truststore", e); 131 } 132 } 133 134 this.httpClient = httpClientBuilder.build(); 135 } 136 137 /** 138 * Constructor 139 * @param cluster the cluster definition 140 */ 141 public Client(Cluster cluster) { 142 this(cluster, false); 143 } 144 145 /** 146 * Constructor 147 * @param cluster the cluster definition 148 * @param sslEnabled enable SSL or not 149 */ 150 public Client(Cluster cluster, boolean sslEnabled) { 151 initialize(cluster, HBaseConfiguration.create(), sslEnabled, Optional.empty()); 152 } 153 154 /** 155 * Constructor 156 * @param cluster the cluster definition 157 * @param conf Configuration 158 * @param sslEnabled enable SSL or not 159 */ 160 public Client(Cluster cluster, Configuration conf, boolean sslEnabled) { 161 initialize(cluster, conf, sslEnabled, Optional.empty()); 162 } 163 164 /** 165 * Constructor, allowing to define custom trust store (only for SSL connections) 166 * @param cluster the cluster definition 167 * @param trustStorePath custom trust store to use for SSL connections 168 * @param trustStorePassword password to use for custom trust store 169 * @param trustStoreType type of custom trust store 170 * @throws ClientTrustStoreInitializationException if the trust store file can not be loaded 171 */ 172 public Client(Cluster cluster, String trustStorePath, Optional<String> trustStorePassword, 173 Optional<String> trustStoreType) { 174 this(cluster, HBaseConfiguration.create(), trustStorePath, trustStorePassword, trustStoreType); 175 } 176 177 /** 178 * Constructor, allowing to define custom trust store (only for SSL connections) 179 * @param cluster the cluster definition 180 * @param conf Configuration 181 * @param trustStorePath custom trust store to use for SSL connections 182 * @param trustStorePassword password to use for custom trust store 183 * @param trustStoreType type of custom trust store 184 * @throws ClientTrustStoreInitializationException if the trust store file can not be loaded 185 */ 186 public Client(Cluster cluster, Configuration conf, String trustStorePath, 187 Optional<String> trustStorePassword, Optional<String> trustStoreType) { 188 189 char[] password = trustStorePassword.map(String::toCharArray).orElse(null); 190 String type = trustStoreType.orElse(KeyStore.getDefaultType()); 191 192 KeyStore trustStore; 193 try { 194 trustStore = KeyStore.getInstance(type); 195 } catch (KeyStoreException e) { 196 throw new ClientTrustStoreInitializationException("Invalid trust store type: " + type, e); 197 } 198 try (InputStream inputStream = 199 new BufferedInputStream(Files.newInputStream(new File(trustStorePath).toPath()))) { 200 trustStore.load(inputStream, password); 201 } catch (CertificateException | NoSuchAlgorithmException | IOException e) { 202 throw new ClientTrustStoreInitializationException("Trust store load error: " + trustStorePath, 203 e); 204 } 205 206 initialize(cluster, conf, true, Optional.of(trustStore)); 207 } 208 209 /** 210 * Shut down the client. Close any open persistent connections. 211 */ 212 public void shutdown() { 213 } 214 215 /** Returns the wrapped HttpClient */ 216 public HttpClient getHttpClient() { 217 return httpClient; 218 } 219 220 /** 221 * Add extra headers. These extra headers will be applied to all http methods before they are 222 * removed. If any header is not used any more, client needs to remove it explicitly. 223 */ 224 public void addExtraHeader(final String name, final String value) { 225 extraHeaders.put(name, value); 226 } 227 228 /** 229 * Get an extra header value. 230 */ 231 public String getExtraHeader(final String name) { 232 return extraHeaders.get(name); 233 } 234 235 /** 236 * Get all extra headers (read-only). 237 */ 238 public Map<String, String> getExtraHeaders() { 239 return Collections.unmodifiableMap(extraHeaders); 240 } 241 242 /** 243 * Remove an extra header. 244 */ 245 public void removeExtraHeader(final String name) { 246 extraHeaders.remove(name); 247 } 248 249 /** 250 * Execute a transaction method given only the path. Will select at random one of the members of 251 * the supplied cluster definition and iterate through the list until a transaction can be 252 * successfully completed. The definition of success here is a complete HTTP transaction, 253 * irrespective of result code. 254 * @param cluster the cluster definition 255 * @param method the transaction method 256 * @param headers HTTP header values to send 257 * @param path the properly urlencoded path 258 * @return the HTTP response code n 259 */ 260 public HttpResponse executePathOnly(Cluster cluster, HttpUriRequest method, Header[] headers, 261 String path) throws IOException { 262 IOException lastException; 263 if (cluster.nodes.size() < 1) { 264 throw new IOException("Cluster is empty"); 265 } 266 int start = (int) Math.round((cluster.nodes.size() - 1) * Math.random()); 267 int i = start; 268 do { 269 cluster.lastHost = cluster.nodes.get(i); 270 try { 271 StringBuilder sb = new StringBuilder(); 272 if (sslEnabled) { 273 sb.append("https://"); 274 } else { 275 sb.append("http://"); 276 } 277 sb.append(cluster.lastHost); 278 sb.append(path); 279 URI uri = new URI(sb.toString()); 280 if (method instanceof HttpPut) { 281 HttpPut put = new HttpPut(uri); 282 put.setEntity(((HttpPut) method).getEntity()); 283 put.setHeaders(method.getAllHeaders()); 284 method = put; 285 } else if (method instanceof HttpGet) { 286 method = new HttpGet(uri); 287 } else if (method instanceof HttpHead) { 288 method = new HttpHead(uri); 289 } else if (method instanceof HttpDelete) { 290 method = new HttpDelete(uri); 291 } else if (method instanceof HttpPost) { 292 HttpPost post = new HttpPost(uri); 293 post.setEntity(((HttpPost) method).getEntity()); 294 post.setHeaders(method.getAllHeaders()); 295 method = post; 296 } 297 return executeURI(method, headers, uri.toString()); 298 } catch (IOException e) { 299 lastException = e; 300 } catch (URISyntaxException use) { 301 lastException = new IOException(use); 302 } 303 } while (++i != start && i < cluster.nodes.size()); 304 throw lastException; 305 } 306 307 /** 308 * Execute a transaction method given a complete URI. 309 * @param method the transaction method 310 * @param headers HTTP header values to send 311 * @param uri a properly urlencoded URI 312 * @return the HTTP response code n 313 */ 314 public HttpResponse executeURI(HttpUriRequest method, Header[] headers, String uri) 315 throws IOException { 316 // method.setURI(new URI(uri, true)); 317 for (Map.Entry<String, String> e : extraHeaders.entrySet()) { 318 method.addHeader(e.getKey(), e.getValue()); 319 } 320 if (headers != null) { 321 for (Header header : headers) { 322 method.addHeader(header); 323 } 324 } 325 long startTime = EnvironmentEdgeManager.currentTime(); 326 if (resp != null) EntityUtils.consumeQuietly(resp.getEntity()); 327 resp = httpClient.execute(method); 328 if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { 329 // Authentication error 330 LOG.debug("Performing negotiation with the server."); 331 negotiate(method, uri); 332 resp = httpClient.execute(method); 333 } 334 335 long endTime = EnvironmentEdgeManager.currentTime(); 336 if (LOG.isTraceEnabled()) { 337 LOG.trace(method.getMethod() + " " + uri + " " + resp.getStatusLine().getStatusCode() + " " 338 + resp.getStatusLine().getReasonPhrase() + " in " + (endTime - startTime) + " ms"); 339 } 340 return resp; 341 } 342 343 /** 344 * Execute a transaction method. Will call either <tt>executePathOnly</tt> or <tt>executeURI</tt> 345 * depending on whether a path only is supplied in 'path', or if a complete URI is passed instead, 346 * respectively. 347 * @param cluster the cluster definition 348 * @param method the HTTP method 349 * @param headers HTTP header values to send 350 * @param path the properly urlencoded path or URI 351 * @return the HTTP response code n 352 */ 353 public HttpResponse execute(Cluster cluster, HttpUriRequest method, Header[] headers, String path) 354 throws IOException { 355 if (path.startsWith("/")) { 356 return executePathOnly(cluster, method, headers, path); 357 } 358 return executeURI(method, headers, path); 359 } 360 361 /** 362 * Initiate client side Kerberos negotiation with the server. 363 * @param method method to inject the authentication token into. 364 * @param uri the String to parse as a URL. 365 * @throws IOException if unknown protocol is found. 366 */ 367 private void negotiate(HttpUriRequest method, String uri) throws IOException { 368 try { 369 AuthenticatedURL.Token token = new AuthenticatedURL.Token(); 370 KerberosAuthenticator authenticator = new KerberosAuthenticator(); 371 authenticator.authenticate(new URL(uri), token); 372 // Inject the obtained negotiated token in the method cookie 373 injectToken(method, token); 374 } catch (AuthenticationException e) { 375 LOG.error("Failed to negotiate with the server.", e); 376 throw new IOException(e); 377 } 378 } 379 380 /** 381 * Helper method that injects an authentication token to send with the method. 382 * @param method method to inject the authentication token into. 383 * @param token authentication token to inject. 384 */ 385 private void injectToken(HttpUriRequest method, AuthenticatedURL.Token token) { 386 String t = token.toString(); 387 if (t != null) { 388 if (!t.startsWith("\"")) { 389 t = "\"" + t + "\""; 390 } 391 method.addHeader(COOKIE, AUTH_COOKIE_EQ + t); 392 } 393 } 394 395 /** Returns the cluster definition */ 396 public Cluster getCluster() { 397 return cluster; 398 } 399 400 /** 401 * @param cluster the cluster definition 402 */ 403 public void setCluster(Cluster cluster) { 404 this.cluster = cluster; 405 } 406 407 /** 408 * Send a HEAD request 409 * @param path the path or URI 410 * @return a Response object with response detail n 411 */ 412 public Response head(String path) throws IOException { 413 return head(cluster, path, null); 414 } 415 416 /** 417 * Send a HEAD request 418 * @param cluster the cluster definition 419 * @param path the path or URI 420 * @param headers the HTTP headers to include in the request 421 * @return a Response object with response detail n 422 */ 423 public Response head(Cluster cluster, String path, Header[] headers) throws IOException { 424 HttpHead method = new HttpHead(path); 425 try { 426 HttpResponse resp = execute(cluster, method, null, path); 427 return new Response(resp.getStatusLine().getStatusCode(), resp.getAllHeaders(), null); 428 } finally { 429 method.releaseConnection(); 430 } 431 } 432 433 /** 434 * Send a GET request 435 * @param path the path or URI 436 * @return a Response object with response detail n 437 */ 438 public Response get(String path) throws IOException { 439 return get(cluster, path); 440 } 441 442 /** 443 * Send a GET request 444 * @param cluster the cluster definition 445 * @param path the path or URI 446 * @return a Response object with response detail n 447 */ 448 public Response get(Cluster cluster, String path) throws IOException { 449 return get(cluster, path, EMPTY_HEADER_ARRAY); 450 } 451 452 /** 453 * Send a GET request 454 * @param path the path or URI 455 * @param accept Accept header value 456 * @return a Response object with response detail n 457 */ 458 public Response get(String path, String accept) throws IOException { 459 return get(cluster, path, accept); 460 } 461 462 /** 463 * Send a GET request 464 * @param cluster the cluster definition 465 * @param path the path or URI 466 * @param accept Accept header value 467 * @return a Response object with response detail n 468 */ 469 public Response get(Cluster cluster, String path, String accept) throws IOException { 470 Header[] headers = new Header[1]; 471 headers[0] = new BasicHeader("Accept", accept); 472 return get(cluster, path, headers); 473 } 474 475 /** 476 * Send a GET request 477 * @param path the path or URI 478 * @param headers the HTTP headers to include in the request, <tt>Accept</tt> must be supplied 479 * @return a Response object with response detail n 480 */ 481 public Response get(String path, Header[] headers) throws IOException { 482 return get(cluster, path, headers); 483 } 484 485 /** 486 * Returns the response body of the HTTPResponse, if any, as an array of bytes. If response body 487 * is not available or cannot be read, returns <tt>null</tt> Note: This will cause the entire 488 * response body to be buffered in memory. A malicious server may easily exhaust all the VM 489 * memory. It is strongly recommended, to use getResponseAsStream if the content length of the 490 * response is unknown or reasonably large. 491 * @param resp HttpResponse 492 * @return The response body, null if body is empty 493 * @throws IOException If an I/O (transport) problem occurs while obtaining the response body. 494 */ 495 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP_LOAD_OF_KNOWN_NULL_VALUE", 496 justification = "null is possible return value") 497 public static byte[] getResponseBody(HttpResponse resp) throws IOException { 498 if (resp.getEntity() == null) return null; 499 try (InputStream instream = resp.getEntity().getContent()) { 500 if (instream != null) { 501 long contentLength = resp.getEntity().getContentLength(); 502 if (contentLength > Integer.MAX_VALUE) { 503 // guard integer cast from overflow 504 throw new IOException("Content too large to be buffered: " + contentLength + " bytes"); 505 } 506 ByteArrayOutputStream outstream = 507 new ByteArrayOutputStream(contentLength > 0 ? (int) contentLength : 4 * 1024); 508 byte[] buffer = new byte[4096]; 509 int len; 510 while ((len = instream.read(buffer)) > 0) { 511 outstream.write(buffer, 0, len); 512 } 513 outstream.close(); 514 return outstream.toByteArray(); 515 } 516 return null; 517 } 518 } 519 520 /** 521 * Send a GET request 522 * @param c the cluster definition 523 * @param path the path or URI 524 * @param headers the HTTP headers to include in the request 525 * @return a Response object with response detail n 526 */ 527 public Response get(Cluster c, String path, Header[] headers) throws IOException { 528 if (httpGet != null) { 529 httpGet.releaseConnection(); 530 } 531 httpGet = new HttpGet(path); 532 HttpResponse resp = execute(c, httpGet, headers, path); 533 return new Response(resp.getStatusLine().getStatusCode(), resp.getAllHeaders(), resp, 534 resp.getEntity() == null ? null : resp.getEntity().getContent()); 535 } 536 537 /** 538 * Send a PUT request 539 * @param path the path or URI 540 * @param contentType the content MIME type 541 * @param content the content bytes 542 * @return a Response object with response detail n 543 */ 544 public Response put(String path, String contentType, byte[] content) throws IOException { 545 return put(cluster, path, contentType, content); 546 } 547 548 /** 549 * Send a PUT request 550 * @param path the path or URI 551 * @param contentType the content MIME type 552 * @param content the content bytes 553 * @param extraHdr extra Header to send 554 * @return a Response object with response detail n 555 */ 556 public Response put(String path, String contentType, byte[] content, Header extraHdr) 557 throws IOException { 558 return put(cluster, path, contentType, content, extraHdr); 559 } 560 561 /** 562 * Send a PUT request 563 * @param cluster the cluster definition 564 * @param path the path or URI 565 * @param contentType the content MIME type 566 * @param content the content bytes 567 * @return a Response object with response detail 568 * @throws IOException for error 569 */ 570 public Response put(Cluster cluster, String path, String contentType, byte[] content) 571 throws IOException { 572 Header[] headers = new Header[1]; 573 headers[0] = new BasicHeader("Content-Type", contentType); 574 return put(cluster, path, headers, content); 575 } 576 577 /** 578 * Send a PUT request 579 * @param cluster the cluster definition 580 * @param path the path or URI 581 * @param contentType the content MIME type 582 * @param content the content bytes 583 * @param extraHdr additional Header to send 584 * @return a Response object with response detail 585 * @throws IOException for error 586 */ 587 public Response put(Cluster cluster, String path, String contentType, byte[] content, 588 Header extraHdr) throws IOException { 589 int cnt = extraHdr == null ? 1 : 2; 590 Header[] headers = new Header[cnt]; 591 headers[0] = new BasicHeader("Content-Type", contentType); 592 if (extraHdr != null) { 593 headers[1] = extraHdr; 594 } 595 return put(cluster, path, headers, content); 596 } 597 598 /** 599 * Send a PUT request 600 * @param path the path or URI 601 * @param headers the HTTP headers to include, <tt>Content-Type</tt> must be supplied 602 * @param content the content bytes 603 * @return a Response object with response detail n 604 */ 605 public Response put(String path, Header[] headers, byte[] content) throws IOException { 606 return put(cluster, path, headers, content); 607 } 608 609 /** 610 * Send a PUT request 611 * @param cluster the cluster definition 612 * @param path the path or URI 613 * @param headers the HTTP headers to include, <tt>Content-Type</tt> must be supplied 614 * @param content the content bytes 615 * @return a Response object with response detail n 616 */ 617 public Response put(Cluster cluster, String path, Header[] headers, byte[] content) 618 throws IOException { 619 HttpPut method = new HttpPut(path); 620 try { 621 method.setEntity(new InputStreamEntity(new ByteArrayInputStream(content), content.length)); 622 HttpResponse resp = execute(cluster, method, headers, path); 623 headers = resp.getAllHeaders(); 624 content = getResponseBody(resp); 625 return new Response(resp.getStatusLine().getStatusCode(), headers, content); 626 } finally { 627 method.releaseConnection(); 628 } 629 } 630 631 /** 632 * Send a POST request 633 * @param path the path or URI 634 * @param contentType the content MIME type 635 * @param content the content bytes 636 * @return a Response object with response detail n 637 */ 638 public Response post(String path, String contentType, byte[] content) throws IOException { 639 return post(cluster, path, contentType, content); 640 } 641 642 /** 643 * Send a POST request 644 * @param path the path or URI 645 * @param contentType the content MIME type 646 * @param content the content bytes 647 * @param extraHdr additional Header to send 648 * @return a Response object with response detail n 649 */ 650 public Response post(String path, String contentType, byte[] content, Header extraHdr) 651 throws IOException { 652 return post(cluster, path, contentType, content, extraHdr); 653 } 654 655 /** 656 * Send a POST request 657 * @param cluster the cluster definition 658 * @param path the path or URI 659 * @param contentType the content MIME type 660 * @param content the content bytes 661 * @return a Response object with response detail 662 * @throws IOException for error 663 */ 664 public Response post(Cluster cluster, String path, String contentType, byte[] content) 665 throws IOException { 666 Header[] headers = new Header[1]; 667 headers[0] = new BasicHeader("Content-Type", contentType); 668 return post(cluster, path, headers, content); 669 } 670 671 /** 672 * Send a POST request 673 * @param cluster the cluster definition 674 * @param path the path or URI 675 * @param contentType the content MIME type 676 * @param content the content bytes 677 * @param extraHdr additional Header to send 678 * @return a Response object with response detail 679 * @throws IOException for error 680 */ 681 public Response post(Cluster cluster, String path, String contentType, byte[] content, 682 Header extraHdr) throws IOException { 683 int cnt = extraHdr == null ? 1 : 2; 684 Header[] headers = new Header[cnt]; 685 headers[0] = new BasicHeader("Content-Type", contentType); 686 if (extraHdr != null) { 687 headers[1] = extraHdr; 688 } 689 return post(cluster, path, headers, content); 690 } 691 692 /** 693 * Send a POST request 694 * @param path the path or URI 695 * @param headers the HTTP headers to include, <tt>Content-Type</tt> must be supplied 696 * @param content the content bytes 697 * @return a Response object with response detail n 698 */ 699 public Response post(String path, Header[] headers, byte[] content) throws IOException { 700 return post(cluster, path, headers, content); 701 } 702 703 /** 704 * Send a POST request 705 * @param cluster the cluster definition 706 * @param path the path or URI 707 * @param headers the HTTP headers to include, <tt>Content-Type</tt> must be supplied 708 * @param content the content bytes 709 * @return a Response object with response detail n 710 */ 711 public Response post(Cluster cluster, String path, Header[] headers, byte[] content) 712 throws IOException { 713 HttpPost method = new HttpPost(path); 714 try { 715 method.setEntity(new InputStreamEntity(new ByteArrayInputStream(content), content.length)); 716 HttpResponse resp = execute(cluster, method, headers, path); 717 headers = resp.getAllHeaders(); 718 content = getResponseBody(resp); 719 return new Response(resp.getStatusLine().getStatusCode(), headers, content); 720 } finally { 721 method.releaseConnection(); 722 } 723 } 724 725 /** 726 * Send a DELETE request 727 * @param path the path or URI 728 * @return a Response object with response detail n 729 */ 730 public Response delete(String path) throws IOException { 731 return delete(cluster, path); 732 } 733 734 /** 735 * Send a DELETE request 736 * @param path the path or URI 737 * @param extraHdr additional Header to send 738 * @return a Response object with response detail n 739 */ 740 public Response delete(String path, Header extraHdr) throws IOException { 741 return delete(cluster, path, extraHdr); 742 } 743 744 /** 745 * Send a DELETE request 746 * @param cluster the cluster definition 747 * @param path the path or URI 748 * @return a Response object with response detail 749 * @throws IOException for error 750 */ 751 public Response delete(Cluster cluster, String path) throws IOException { 752 HttpDelete method = new HttpDelete(path); 753 try { 754 HttpResponse resp = execute(cluster, method, null, path); 755 Header[] headers = resp.getAllHeaders(); 756 byte[] content = getResponseBody(resp); 757 return new Response(resp.getStatusLine().getStatusCode(), headers, content); 758 } finally { 759 method.releaseConnection(); 760 } 761 } 762 763 /** 764 * Send a DELETE request 765 * @param cluster the cluster definition 766 * @param path the path or URI 767 * @return a Response object with response detail 768 * @throws IOException for error 769 */ 770 public Response delete(Cluster cluster, String path, Header extraHdr) throws IOException { 771 HttpDelete method = new HttpDelete(path); 772 try { 773 Header[] headers = { extraHdr }; 774 HttpResponse resp = execute(cluster, method, headers, path); 775 headers = resp.getAllHeaders(); 776 byte[] content = getResponseBody(resp); 777 return new Response(resp.getStatusLine().getStatusCode(), headers, content); 778 } finally { 779 method.releaseConnection(); 780 } 781 } 782 783 public static class ClientTrustStoreInitializationException extends RuntimeException { 784 785 public ClientTrustStoreInitializationException(String message, Throwable cause) { 786 super(message, cause); 787 } 788 } 789}