blob: d5aa4cc83be46d94fa0d7fb41b4f1f945f3d7fb1 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2018, 2019 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.internal.rhelp.core.server;
import java.io.InputStream;
import java.net.ConnectException;
import java.net.URI;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.util.BasicAuthentication;
import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.client.util.InputStreamResponseListener;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.jcommons.status.ProgressMonitor;
import org.eclipse.statet.jcommons.status.StatusException;
import org.eclipse.statet.internal.rhelp.core.SerUtil;
@NonNullByDefault
public class JettyREnvHelpAccess extends ServerREnvHelpAccess {
private final HttpClient httpClient;
public JettyREnvHelpAccess(final URI url, final HttpClient httpClient) throws Exception {
super(url);
this.httpClient= httpClient;
final String userInfo= url.getUserInfo();
if (userInfo != null) {
final String[] auth= userInfo.split("\\:", 2); //$NON-NLS-1$
this.httpClient.getAuthenticationStore().addAuthenticationResult(
new BasicAuthentication.BasicResult(createUrl(getBasePath()),
auth[0], (auth.length == 2) ? auth[1] : "" ) ); //$NON-NLS-1$
}
this.httpClient.start();
}
protected void checkDataStreamResponse(final Response response) throws ResponseException {
if (response.getStatus() != HttpServletResponse.SC_OK) {
final String message= String.format("Http Status %1$s - %2$s",
response.getStatus(), response.getReason() );
switch (response.getStatus()) {
case HttpServletResponse.SC_NOT_FOUND:
throw new NotFoundException(message);
}
throw new ResponseException(message);
}
final String contentType= response.getHeaders().get(HttpHeader.CONTENT_TYPE);
if (contentType == null || !contentType.startsWith(ServerApi.DS_MIME_TYPE)) {
throw new ResponseException(String.format("Unexpected content type: '%1$s'.",
contentType ));
}
}
@Override
protected @Nullable InputStream getDataStream(final URI url, final @Nullable String eTag)
throws StatusException, ResponseException {
final Request request= this.httpClient.newRequest(url);
if (eTag != null) {
request.header(HttpHeader.IF_NONE_MATCH, eTag);
}
request.accept(ServerApi.DS_MIME_TYPE + ';' + ServerApi.DS_SER_VERSION + '=' + SerUtil.READABLE_SERVER);
return doGetDataStream(request, (eTag != null));
}
@Override
protected InputStream getDataStream(final URI url, final String[] params, final byte[] requestData)
throws StatusException, ResponseException {
final Request request= this.httpClient.newRequest(url);
for (int i= 0; i < params.length; ) {
request.param(params[i++], params[i++]);
}
request.method(HttpMethod.POST);
request.content(new BytesContentProvider(ServerApi.DS_MIME_TYPE, requestData));
return doGetDataStream(request, false);
}
private @Nullable InputStream doGetDataStream(final Request request, final boolean notModified)
throws StatusException, ResponseException {
request.accept(ServerApi.DS_MIME_TYPE);
final InputStreamResponseListener listener= new InputStreamResponseListener();
request.send(listener);
try {
final Response response= listener.get(20, TimeUnit.SECONDS);
if (notModified && response.getStatus() == HttpServletResponse.SC_NOT_MODIFIED) {
return null;
}
checkDataStreamResponse(response);
return listener.getInputStream();
}
catch (final InterruptedException | CancellationException e) {
request.abort(e);
throw onCancelled();
}
catch (final TimeoutException e) {
request.abort(e);
throw onTimeout(e);
}
catch (final ExecutionException e) {
if (e.getCause() instanceof TimeoutException) {
request.abort(e.getCause());
throw onTimeout(e.getCause());
}
if (e.getCause() instanceof ConnectException) {
request.abort(e.getCause());
throw onConnectError(e.getCause());
}
request.abort(e);
throw onFailed(e);
}
}
@Override
protected byte[] getDataStreamBytes(final URI url, final String[] params,
final int timeout, final @Nullable ProgressMonitor m) throws StatusException,
ResponseException {
final Request request= this.httpClient.newRequest(url);
for (int i= 0; i < params.length; ) {
request.param(params[i++], params[i++]);
}
request.accept(ServerApi.DS_MIME_TYPE);
if (timeout != -1) {
request.timeout(timeout, TimeUnit.SECONDS);
}
final FutureResponseListener listener= new FutureResponseListener(request);
request.send(listener);
while (true) {
try {
final ContentResponse response= (m != null) ?
listener.get(100, TimeUnit.MILLISECONDS) :
listener.get();
checkDataStreamResponse(response);
return response.getContent();
}
catch (final TimeoutException e) {
if (m.isCanceled()) {
listener.cancel(true);
throw onCancelled();
}
continue;
}
catch (final InterruptedException | CancellationException e) {
request.abort(e);
if (m != null) {
m.setCanceled(true);
}
throw onCancelled();
}
catch (final ExecutionException e) {
if (e.getCause() instanceof TimeoutException) {
request.abort(e.getCause());
throw onTimeout(e.getCause());
}
if (e.getCause() instanceof ConnectException) {
request.abort(e.getCause());
throw onConnectError(e.getCause());
}
request.abort(e);
throw onFailed(e);
}
}
}
}