Skip to main content
summaryrefslogtreecommitdiffstats
blob: 74b67212bf58013b52f387335bc200c255371f80 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/*******************************************************************************
 * Copyright (c) 2006, 2010 Steffen Pingel and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Steffen Pingel - initial API and implementation
 *******************************************************************************/

package org.eclipse.mylyn.internal.trac.core.util;

import java.io.BufferedOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.HttpVersion;
import org.apache.commons.httpclient.auth.AuthScheme;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.XmlRpcRequest;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientException;
import org.apache.xmlrpc.client.XmlRpcHttpClientConfig;
import org.apache.xmlrpc.client.XmlRpcHttpTransport;
import org.apache.xmlrpc.client.XmlRpcTransport;
import org.apache.xmlrpc.client.XmlRpcTransportFactory;
import org.apache.xmlrpc.common.XmlRpcStreamRequestConfig;
import org.apache.xmlrpc.util.HttpUtil;
import org.apache.xmlrpc.util.XmlRpcIOException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.mylyn.commons.net.AbstractWebLocation;
import org.eclipse.mylyn.commons.net.WebUtil;
import org.xml.sax.SAXException;

/**
 * A custom transport factory used to establish XML-RPC connections. Uses the Mylyn proxy settings.
 * 
 * @author Steffen Pingel
 */
public class TracHttpClientTransportFactory implements XmlRpcTransportFactory {

	/**
	 * A transport that uses the Apache HttpClient library.
	 */
	public static class TracHttpClientTransport extends XmlRpcHttpTransport {

		private final HttpClient httpClient;

		private final AbstractWebLocation location;

		private PostMethod method;

		private int contentLength = -1;

		private XmlRpcHttpClientConfig config;

		private IProgressMonitor monitor;

		private HostConfiguration hostConfiguration;

		private final HttpMethodInterceptor interceptor;

		public TracHttpClientTransport(XmlRpcClient client, HttpClient httpClient, AbstractWebLocation location,
				HttpMethodInterceptor interceptor) {
			super(client, ""); //$NON-NLS-1$
			this.httpClient = httpClient;
			this.location = location;
			this.interceptor = interceptor;
		}

		@Override
		protected void close() throws XmlRpcClientException {
			method.releaseConnection();
		}

		public int getContentLength() {
			return contentLength;
		}

		@Override
		protected InputStream getInputStream() throws XmlRpcException {
			int responseCode = method.getStatusCode();
			if (responseCode != HttpURLConnection.HTTP_OK) {
				TracHttpException e = new TracHttpException(responseCode);
				if (responseCode == HttpStatus.SC_UNAUTHORIZED) {
					e.setAuthScheme(method.getHostAuthState().getAuthScheme());
				}
				throw e;
			}

			try {
				return method.getResponseBodyAsStream();
			} catch (HttpException e) {
				throw new XmlRpcClientException("Error in HTTP transport: " + e.getMessage(), e); //$NON-NLS-1$
			} catch (IOException e) {
				throw new XmlRpcClientException("I/O error in server communication: " + e.getMessage(), e); //$NON-NLS-1$
			}
		}

		@Override
		protected String getUserAgent() {
			return WebUtil.getUserAgent(""); //$NON-NLS-1$
		}

		@Override
		protected void initHttpHeaders(XmlRpcRequest request) throws XmlRpcClientException {
			config = (XmlRpcHttpClientConfig) request.getConfig();

			if (request instanceof TracXmlRpcClientRequest) {
				TracXmlRpcClientRequest tracRequest = (TracXmlRpcClientRequest) request;
				monitor = tracRequest.getProgressMonitor();
			} else {
				monitor = null;
			}

			String url = config.getServerURL().toString();
			hostConfiguration = WebUtil.createHostConfiguration(httpClient, location, monitor);
			method = new PostMethod(WebUtil.getRequestPath(url));

			super.initHttpHeaders(request);

			if (config.getConnectionTimeout() != 0) {
				httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(config.getConnectionTimeout());
			}

			if (config.getReplyTimeout() != 0) {
				httpClient.getHttpConnectionManager().getParams().setSoTimeout(config.getConnectionTimeout());
			}

			method.getParams().setVersion(HttpVersion.HTTP_1_1);

			if (interceptor != null) {
				interceptor.processRequest(method);
			}
		}

		@Override
		protected boolean isResponseGzipCompressed(XmlRpcStreamRequestConfig config) {
			Header header = method.getResponseHeader("Content-Encoding"); //$NON-NLS-1$
			return header != null && HttpUtil.isUsingGzipEncoding(header.getValue());
		}

		@Override
		protected void setContentLength(int contentLength) {
			this.contentLength = contentLength;
		}

		@Override
		protected void setCredentials(XmlRpcHttpClientConfig config) throws XmlRpcClientException {
			// handled by TracXmlRpcClient
		}

		@Override
		protected void setRequestHeader(String header, String value) {
			method.setRequestHeader(new Header(header, value));
		}

		@Override
		protected void writeRequest(final ReqWriter writer) throws XmlRpcException {
			method.setRequestEntity(new RequestEntity() {
				public long getContentLength() {
					return TracHttpClientTransport.this.getContentLength();
				}

				public String getContentType() {
					return "text/xml"; //$NON-NLS-1$
				}

				public boolean isRepeatable() {
					return getContentLength() != -1;
				}

				public void writeRequest(OutputStream pOut) throws IOException {
					try {
						/* Make sure, that the socket is not closed by replacing it with our
						 * own BufferedOutputStream.
						 */
						OutputStream ostream;
						if (isUsingByteArrayOutput(config)) {
							// No need to buffer the output.
							ostream = new FilterOutputStream(pOut) {
								@Override
								public void close() throws IOException {
									flush();
								}
							};
						} else {
							ostream = new BufferedOutputStream(pOut) {
								@Override
								public void close() throws IOException {
									flush();
								}
							};
						}
						writer.write(ostream);
					} catch (XmlRpcException e) {
						throw new XmlRpcIOException(e);
					} catch (SAXException e) {
						throw new XmlRpcIOException(e);
					}
				}
			});

			try {
				WebUtil.execute(httpClient, hostConfiguration, method, monitor);
				if (interceptor != null) {
					interceptor.processResponse(method);
				}
			} catch (XmlRpcIOException e) {
				Throwable t = e.getLinkedException();
				if (t instanceof XmlRpcException) {
					throw (XmlRpcException) t;
				} else {
					throw new XmlRpcException("Unexpected exception: " + t.getMessage(), t); //$NON-NLS-1$
				}
			} catch (IOException e) {
				throw new XmlRpcException("I/O error while communicating with HTTP server: " + e.getMessage(), e); //$NON-NLS-1$
			}
		}

	}

	public static class TracHttpException extends XmlRpcException {

		private static final long serialVersionUID = 9032521978140685830L;

		private AuthScheme authScheme;

		public TracHttpException(int responseCode) {
			super(responseCode, "HTTP Error " + responseCode); //$NON-NLS-1$
		}

		public AuthScheme getAuthScheme() {
			return authScheme;
		}

		public void setAuthScheme(AuthScheme authScheme) {
			this.authScheme = authScheme;
		}

	}

	private final XmlRpcClient xmlRpcClient;

	private AbstractWebLocation location;

	private final HttpClient httpClient;

	private HttpMethodInterceptor interceptor;

	public TracHttpClientTransportFactory(XmlRpcClient xmlRpcClient, HttpClient httpClient) {
		this.xmlRpcClient = xmlRpcClient;
		this.httpClient = httpClient;
	}

	public AbstractWebLocation getLocation() {
		return location;
	}

	public XmlRpcTransport getTransport() {
		return new TracHttpClientTransport(xmlRpcClient, httpClient, location, interceptor);
	}

	public void setLocation(AbstractWebLocation location) {
		this.location = location;
	}

	public HttpMethodInterceptor getInterceptor() {
		return interceptor;
	}

	public void setInterceptor(HttpMethodInterceptor interceptor) {
		this.interceptor = interceptor;
	}

}

Back to the top