| /*=============================================================================# |
| # 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); |
| } |
| } |
| } |
| |
| } |