in src/main/java/com/microsoft/azure/datalake/store/HttpTransport.java [156:349]
private static void makeSingleCall(ADLStoreClient client,
Operation op,
String path,
QueryParams queryParams,
byte[] requestBody,
int offsetWithinContentsArray,
int length,
RequestOptions opts,
OperationResponse resp) {
if (client == null) throw new IllegalArgumentException("client is null");
if (client.getAccountName() == null || client.getAccountName().equals("")) {
resp.successful = false;
resp.message = "Account name or client is null or blank";
return;
}
String token = null;
long tokenStartTime = System.nanoTime();
try {
token = client.getAccessToken();
if (token == null || token.equals("")) {
resp.successful = false;
resp.message = "Access token is null or blank";
resp.tokenAcquisitionLatency = System.nanoTime() - tokenStartTime;
return;
}
} catch (IOException ex) {
resp.successful = false;
resp.message = "Error fetching access token";
resp.ex = ex;
resp.tokenAcquisitionLatency = System.nanoTime() - tokenStartTime;
return;
}
resp.tokenAcquisitionLatency = System.nanoTime() - tokenStartTime;
if (op == null) {
resp.successful = false;
resp.message = "operation is null";
return;
}
if (path == null || path.trim().equals("")) {
resp.successful = false;
resp.message = "path is null";
return;
}
resp.opCode = op.name;
if (requestBody != null && requestBody.length > 0) {
if (offsetWithinContentsArray < 0 ||
length < 0 ||
offsetWithinContentsArray + length < 0 || // integer overflow
offsetWithinContentsArray >= requestBody.length ||
offsetWithinContentsArray + length > requestBody.length) {
throw new IndexOutOfBoundsException("offset+length overflows byte buffer for path " + path);
}
} else {
if (offsetWithinContentsArray != 0 || length != 0) {
throw new IndexOutOfBoundsException("Non-zero offset or length with null body for path " + path);
}
}
// Build URL
StringBuilder urlString = new StringBuilder();
urlString.append(client.getHttpPrefix()); // http or https
urlString.append("://");
urlString.append(client.getAccountName());
urlString.append(op.namespace);
String prefix = client.getFilePathPrefix();
if (prefix != null) urlString.append(prefix);
if (path.charAt(0) != '/') urlString.append('/');
try {
urlString.append((new URI(null, null, path, null)).toASCIIString()); // use URI to encode path
} catch (URISyntaxException ex) {
resp.successful = false;
resp.message = "Invalid path " + path;
return;
}
urlString.append('?');
urlString.append(queryParams.serialize());
URL url;
try {
url = new URL(urlString.toString());
} catch (MalformedURLException ex) {
resp.ex = ex;
resp.successful = false;
return;
}
HttpURLConnection conn = null;
String userAgent = client.getUserAgent();
try {
conn = (HttpURLConnection) url.openConnection();
if(conn instanceof HttpsURLConnection && client.shouldAlterCipherSuits()) {
HttpsURLConnection secureConn = (HttpsURLConnection) conn;
SSLSocketFactoryEx sslSocketFactoryEx = null;
try {
sslSocketFactoryEx =
SSLSocketFactoryEx.getDefaultFactory(client.getSSLChannelMode());
} catch (IOException e) {
// Suppress exception. Failure to init SSLSocketFactoryEx would have only performance impact.
log.info("Failed to init SSLSocketFactoryEx, Fallback to default SSLSocketFactory");
}
if(sslSocketFactoryEx != null) {
secureConn.setSSLSocketFactory(sslSocketFactoryEx);
userAgent += "/" + sslSocketFactoryEx.getUserAgent();
}
}
// Setup Http Request (method and headers)
conn.setRequestProperty("Authorization", token);
conn.setRequestProperty("User-Agent", userAgent);
conn.setRequestProperty("x-ms-client-request-id", opts.requestid);
String latencyHeader = LatencyTracker.get();
if (latencyHeader != null) conn.setRequestProperty("x-ms-adl-client-latency", latencyHeader);
if (client.getTiHeaderValue() != null)
conn.setRequestProperty("x-ms-tracking-info", client.getTiHeaderValue());
conn.setConnectTimeout(opts.timeout);
conn.setReadTimeout(opts.timeout);
conn.setUseCaches(false);
conn.setRequestMethod(op.method);
conn.setDoInput(true);
if(op.enforceMimeTypeJson) {
conn.setRequestProperty("Content-Type", "application/json");
}
// populate request body if applicable
if (!op.method.equals("GET")) {
conn.setDoOutput(true);
conn.setRequestMethod(op.method);
OutputStream outStr = conn.getOutputStream();
if (op.requiresBody && requestBody != null) {
outStr.write(requestBody, offsetWithinContentsArray, length);
outStr.close();
} else {
// server *requires* a Content-Length header, and doesnt take absence of header as 0 (bad behavior)
// The only way to force java to send "Content-Length:0" is to do this.
// Setting Content-Length header to 0 using setRequestProprty doesnt work (bad behavior)
byte[] buf = new byte[]{}; // zero-length byte-array
outStr.write(buf);
outStr.close();
}
}
// get Response Stream if applicable
resp.httpResponseCode = conn.getResponseCode();
resp.httpResponseMessage = conn.getResponseMessage();
resp.requestId = conn.getHeaderField("x-ms-request-id");
resp.responseContentLength = conn.getHeaderFieldLong("Content-Length", 0);
String chunked = conn.getHeaderField("Transfer-Encoding");
if (chunked != null && chunked.equals("chunked")) resp.responseChunked = true;
// if request failed, then the body of an HTTP 4xx or 5xx response contains error info as JSon
if (resp.httpResponseCode >= 400) {
if (resp.httpResponseCode == 401 && tokenlog.isDebugEnabled()) { // log auth token errors separately
String logline = "HTTPRequest,HTTP401,cReqId:" +
opts.requestid + ",sReqId:" +
resp.requestId + ",path:" +
path + ",token:" +
token; // ok to log, since token doesn't seem to be working anyway
tokenlog.debug(logline);
}
if (resp.responseContentLength > 0 && conn.getErrorStream() != null) {
getCodesFromJSon(conn.getErrorStream(), resp);
return;
}
} else {
consumeInputStream(conn.getErrorStream()); // read(ignore) and close if the stream exists
if (op.returnsBody) { // response stream will be handled by caller
resp.responseStream = conn.getInputStream();
} else { // read and discard response stream so it is consumed and connection can be reused
consumeInputStream(conn.getInputStream());
}
}
} catch (IOException ex) {
resp.ex = ex;
resp.successful = false;
if (conn != null) {
try {
consumeInputStream(conn.getInputStream()); // read(ignore) and close if the stream exists
consumeInputStream(conn.getErrorStream()); // read(ignore) and close if the stream exists
} catch (IOException ex2) {
// ignore, since we already have the root IOException - this part is just when cleaning up error stream
}
}
}
}