blob: 20ef198432a1e4f189ce3eb3ac85a3f9951e9358 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.slider.core.restclient;
import com.google.common.base.Preconditions;
import org.apache.commons.io.IOUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authentication.client.AuthenticationException;
import org.apache.hadoop.yarn.webapp.ForbiddenException;
import org.apache.hadoop.yarn.webapp.NotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* Operations on the JDK UrlConnection class.
*
*/
public class UrlConnectionOperations extends Configured {
private static final Logger log =
LoggerFactory.getLogger(UrlConnectionOperations.class);
private SliderURLConnectionFactory connectionFactory;
private boolean useSpnego = false;
/**
* Create an instance off the configuration. The SPNEGO policy
* is derived from the current UGI settings.
* @param conf config
*/
public UrlConnectionOperations(Configuration conf) {
super(conf);
connectionFactory = SliderURLConnectionFactory.newInstance(conf);
if (UserGroupInformation.isSecurityEnabled()) {
log.debug("SPNEGO is enabled");
setUseSpnego(true);
}
}
public boolean isUseSpnego() {
return useSpnego;
}
public void setUseSpnego(boolean useSpnego) {
this.useSpnego = useSpnego;
}
/**
* Opens a url with cache disabled, redirect handled in
* (JDK) implementation.
*
* @param url to open
* @return URLConnection
* @throws IOException
* @throws AuthenticationException authentication failure
*/
public HttpURLConnection openConnection(URL url) throws
IOException,
AuthenticationException {
Preconditions.checkArgument(url.getPort() != 0, "no port");
return (HttpURLConnection) connectionFactory.openConnection(url, useSpnego);
}
public HttpOperationResponse execGet(URL url) throws
IOException,
AuthenticationException {
return execHttpOperation(HttpVerb.GET, url, null, "");
}
public HttpOperationResponse execHttpOperation(HttpVerb verb,
URL url,
byte[] payload,
String contentType)
throws IOException, AuthenticationException {
HttpURLConnection conn = null;
HttpOperationResponse outcome = new HttpOperationResponse();
int resultCode;
byte[] body = null;
log.debug("{} {} spnego={}", verb, url, useSpnego);
boolean doOutput = verb.hasUploadBody();
if (doOutput) {
Preconditions.checkArgument(payload !=null,
"Null payload on a verb which expects one");
}
try {
conn = openConnection(url);
conn.setRequestMethod(verb.getVerb());
conn.setDoOutput(doOutput);
if (doOutput) {
conn.setRequestProperty("Content-Type", contentType);
}
// now do the connection
conn.connect();
if (doOutput) {
OutputStream output = conn.getOutputStream();
IOUtils.write(payload, output);
output.close();
}
resultCode = conn.getResponseCode();
outcome.lastModified = conn.getLastModified();
outcome.contentType = conn.getContentType();
outcome.headers = conn.getHeaderFields();
InputStream stream = conn.getErrorStream();
if (stream == null) {
stream = conn.getInputStream();
}
if (stream != null) {
// read into a buffer.
body = IOUtils.toByteArray(stream);
} else {
// no body:
log.debug("No body in response");
}
} catch (SSLException e) {
throw e;
} catch (IOException e) {
throw NetUtils.wrapException(url.toString(),
url.getPort(), "localhost", 0, e);
} catch (AuthenticationException e) {
throw new AuthenticationException("From " + url + ": " + e, e);
} finally {
if (conn != null) {
conn.disconnect();
}
}
uprateFaults(HttpVerb.GET, url.toString(), resultCode, "", body);
outcome.responseCode = resultCode;
outcome.data = body;
return outcome;
}
/**
* Uprate error codes 400 and up into faults;
* 404 is converted to a {@link NotFoundException},
* 401 to {@link ForbiddenException}
*
* @param verb HTTP Verb used
* @param url URL as string
* @param resultCode response from the request
* @param bodyAsString
*@param body optional body of the request @throws IOException if the result was considered a failure
*/
public static void uprateFaults(HttpVerb verb, String url,
int resultCode, String bodyAsString, byte[] body)
throws IOException {
if (resultCode < 400) {
//success
return;
}
String msg = verb.toString() +" "+ url;
if (resultCode == 404) {
throw new NotFoundException(msg);
}
if (resultCode == 401) {
throw new ForbiddenException(msg);
}
// all other error codes
// get a string respnse
if (bodyAsString == null) {
if (body != null && body.length > 0) {
bodyAsString = new String(body);
} else {
bodyAsString = "";
}
}
String message = msg +
" failed with exit code " + resultCode
+ ", body length " + bodyAsString.length()
+ ":\n" + bodyAsString;
log.error(message);
throw new IOException(message);
}
}