001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.axis2.transport.http.server;
020:
021: import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch;
022: import org.apache.axis2.AxisFault;
023: import org.apache.axis2.Constants;
024: import org.apache.axis2.addressing.AddressingHelper;
025: import org.apache.axis2.addressing.EndpointReference;
026: import org.apache.axis2.context.ConfigurationContext;
027: import org.apache.axis2.context.MessageContext;
028: import org.apache.axis2.description.TransportInDescription;
029: import org.apache.axis2.description.TransportOutDescription;
030: import org.apache.axis2.engine.AxisEngine;
031: import org.apache.axis2.transport.RequestResponseTransport;
032: import org.apache.axis2.transport.http.HTTPConstants;
033: import org.apache.axis2.util.MessageContextBuilder;
034: import org.apache.commons.logging.Log;
035: import org.apache.commons.logging.LogFactory;
036: import org.apache.http.*;
037: import org.apache.http.params.HttpParams;
038: import org.apache.http.params.HttpParamsLinker;
039: import org.apache.http.protocol.HttpContext;
040: import org.apache.http.protocol.HttpProcessor;
041: import org.apache.axiom.soap.SOAP12Constants;
042: import org.apache.axiom.soap.SOAP11Constants;
043:
044: import javax.servlet.http.HttpServletResponse;
045: import javax.xml.namespace.QName;
046: import java.io.IOException;
047: import java.io.InputStream;
048: import java.io.OutputStream;
049: import java.net.SocketException;
050: import java.util.HashMap;
051: import java.util.Iterator;
052:
053: /**
054: * This class is an extension of the defaulf HTTP service responsible for
055: * maintaining and polulating the {@link MessageContext} for incoming Axis
056: * requests.
057: */
058: public class AxisHttpService {
059:
060: private static final Log LOG = LogFactory
061: .getLog(AxisHttpService.class);
062:
063: private final HttpProcessor httpProcessor;
064: private final ConnectionReuseStrategy connStrategy;
065: private final HttpResponseFactory responseFactory;
066: private final ConfigurationContext configurationContext;
067: private final Worker worker;
068:
069: private HttpParams params;
070:
071: public AxisHttpService(final HttpProcessor httpProcessor,
072: final ConnectionReuseStrategy connStrategy,
073: final HttpResponseFactory responseFactory,
074: final ConfigurationContext configurationContext,
075: final Worker worker) {
076: super ();
077: if (httpProcessor == null) {
078: throw new IllegalArgumentException(
079: "HTTP processor may not be null");
080: }
081: if (connStrategy == null) {
082: throw new IllegalArgumentException(
083: "Connection strategy may not be null");
084: }
085: if (responseFactory == null) {
086: throw new IllegalArgumentException(
087: "Response factory may not be null");
088: }
089: if (worker == null) {
090: throw new IllegalArgumentException("Worker may not be null");
091: }
092: if (configurationContext == null) {
093: throw new IllegalArgumentException(
094: "Configuration context may not be null");
095: }
096: this .httpProcessor = httpProcessor;
097: this .connStrategy = connStrategy;
098: this .responseFactory = responseFactory;
099: this .configurationContext = configurationContext;
100: this .worker = worker;
101:
102: }
103:
104: public HttpParams getParams() {
105: return this .params;
106: }
107:
108: public void setParams(final HttpParams params) {
109: this .params = params;
110: }
111:
112: public void handleRequest(final AxisHttpConnection conn,
113: final HttpContext context) throws IOException,
114: HttpException {
115:
116: MessageContext msgContext = configurationContext
117: .createMessageContext();
118: msgContext.setIncomingTransportName(Constants.TRANSPORT_HTTP);
119:
120: if (conn != null) {
121: msgContext.setProperty(MessageContext.REMOTE_ADDR, conn
122: .getRemoteAddress().getHostAddress());
123: msgContext.setProperty(MessageContext.TRANSPORT_ADDR, conn
124: .getLocalAddress().getHostAddress());
125:
126: if (LOG.isDebugEnabled()) {
127: LOG.debug("Remote address of the connection : "
128: + conn.getRemoteAddress().getHostAddress());
129: }
130: }
131:
132: HttpResponse response;
133: try {
134: HttpRequest request = conn.receiveRequest();
135: HttpParamsLinker.link(request, this .params);
136: HttpVersion ver = request.getRequestLine().getHttpVersion();
137: if (!ver.lessEquals(HttpVersion.HTTP_1_1)) {
138: // Downgrade protocol version if greater than HTTP/1.1
139: ver = HttpVersion.HTTP_1_1;
140: }
141:
142: response = this .responseFactory.newHttpResponse(ver,
143: HttpStatus.SC_OK, context);
144: HttpParamsLinker.link(response, this .params);
145:
146: if (request instanceof HttpEntityEnclosingRequest) {
147: if (((HttpEntityEnclosingRequest) request)
148: .expectContinue()) {
149: HttpResponse ack = this .responseFactory
150: .newHttpResponse(ver,
151: HttpStatus.SC_CONTINUE, context);
152: HttpParamsLinker.link(ack, this .params);
153: conn.sendResponse(ack);
154: conn.flush();
155: }
156: }
157:
158: // Create Axis request and response objects
159: AxisHttpRequestImpl axisreq = new AxisHttpRequestImpl(conn,
160: request, this .httpProcessor, context);
161: AxisHttpResponseImpl axisres = new AxisHttpResponseImpl(
162: conn, response, this .httpProcessor, context);
163:
164: // Prepare HTTP request
165: axisreq.prepare();
166:
167: // Run the service
168: doService(axisreq, axisres, context, msgContext);
169:
170: // Make sure the request content is fully consumed
171: InputStream instream = conn.getInputStream();
172: if (instream != null) {
173: instream.close();
174: }
175:
176: // Commit response if not committed
177: if (!axisres.isCommitted()) {
178: axisres.commit();
179: }
180:
181: // Make sure the response content is properly terminated
182: OutputStream outstream = conn.getOutputStream();
183: if (outstream != null) {
184: outstream.close();
185: }
186:
187: } catch (HttpException ex) {
188: response = this .responseFactory.newHttpResponse(
189: HttpVersion.HTTP_1_0,
190: HttpStatus.SC_INTERNAL_SERVER_ERROR, context);
191: HttpParamsLinker.link(response, this .params);
192: handleException(ex, response);
193: this .httpProcessor.process(response, context);
194: conn.sendResponse(response);
195: }
196:
197: conn.flush();
198: if (!this .connStrategy.keepAlive(response, context)) {
199: conn.close();
200: } else {
201: conn.reset();
202: }
203: }
204:
205: protected void handleException(final HttpException ex,
206: final HttpResponse response) {
207: if (ex instanceof MethodNotSupportedException) {
208: response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED);
209: } else if (ex instanceof UnsupportedHttpVersionException) {
210: response
211: .setStatusCode(HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED);
212: } else if (ex instanceof ProtocolException) {
213: response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
214: } else {
215: response.setStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
216: }
217: }
218:
219: protected void doService(final AxisHttpRequest request,
220: final AxisHttpResponse response, final HttpContext context,
221: final MessageContext msgContext) throws HttpException,
222: IOException {
223: if (LOG.isDebugEnabled()) {
224: LOG.debug("Request method: " + request.getMethod());
225: LOG.debug("Target URI: " + request.getRequestURI());
226: }
227:
228: try {
229: TransportOutDescription transportOut = this .configurationContext
230: .getAxisConfiguration().getTransportOut(
231: Constants.TRANSPORT_HTTP);
232: TransportInDescription transportIn = this .configurationContext
233: .getAxisConfiguration().getTransportIn(
234: Constants.TRANSPORT_HTTP);
235:
236: String sessionKey = (String) context
237: .getAttribute(HTTPConstants.COOKIE_STRING);
238: msgContext.setTransportIn(transportIn);
239: msgContext.setTransportOut(transportOut);
240: msgContext.setServerSide(true);
241: msgContext.setProperty(HTTPConstants.COOKIE_STRING,
242: sessionKey);
243: msgContext.setProperty(
244: Constants.Configuration.TRANSPORT_IN_URL, request
245: .getRequestURI());
246:
247: // set the transport Headers
248: HashMap headerMap = new HashMap();
249: for (Iterator it = request.headerIterator(); it.hasNext();) {
250: Header header = (Header) it.next();
251: headerMap.put(header.getName(), header.getValue());
252: }
253: msgContext.setProperty(MessageContext.TRANSPORT_HEADERS,
254: headerMap);
255: msgContext.setProperty(
256: Constants.Configuration.CONTENT_TYPE, request
257: .getContentType());
258:
259: msgContext.setProperty(MessageContext.TRANSPORT_OUT,
260: response.getOutputStream());
261: msgContext.setProperty(Constants.OUT_TRANSPORT_INFO,
262: response);
263: msgContext.setTo(new EndpointReference(request
264: .getRequestURI()));
265: msgContext.setProperty(
266: RequestResponseTransport.TRANSPORT_CONTROL,
267: new SimpleHTTPRequestResponseTransport());
268:
269: this .worker.service(request, response, msgContext);
270: } catch (SocketException ex) {
271: // Socket is unreliable.
272: throw ex;
273: } catch (HttpException ex) {
274: // HTTP protocol violation. Transport is unrelaible
275: throw ex;
276: } catch (Throwable e) {
277:
278: msgContext.setProperty(MessageContext.TRANSPORT_OUT,
279: response.getOutputStream());
280: msgContext.setProperty(Constants.OUT_TRANSPORT_INFO,
281: response);
282:
283: MessageContext faultContext = MessageContextBuilder
284: .createFaultMessageContext(msgContext, e);
285: // If the fault is not going along the back channel we should be 202ing
286: if (AddressingHelper.isFaultRedirected(msgContext)) {
287: response.setStatus(HttpStatus.SC_ACCEPTED);
288: } else {
289: String state = (String) msgContext
290: .getProperty(Constants.HTTP_RESPONSE_STATE);
291: if (state != null) {
292: int stateInt = Integer.parseInt(state);
293: response.setStatus(stateInt);
294: if (stateInt == HttpServletResponse.SC_UNAUTHORIZED) { // Unauthorized
295: String realm = (String) msgContext
296: .getProperty(Constants.HTTP_BASIC_AUTH_REALM);
297: response.addHeader("WWW-Authenticate",
298: "basic realm=\"" + realm + "\"");
299: }
300: } else {
301: if (e instanceof AxisFault) {
302: response.sendError(
303: getStatusFromAxisFault((AxisFault) e),
304: e.getMessage());
305: } else {
306: response.sendError(
307: HttpStatus.SC_INTERNAL_SERVER_ERROR,
308: "Internal server error");
309: }
310: }
311: }
312: AxisEngine.sendFault(faultContext);
313: }
314: }
315:
316: public int getStatusFromAxisFault(AxisFault fault) {
317: QName faultCode = fault.getFaultCode();
318: if (SOAP12Constants.QNAME_SENDER_FAULTCODE.equals(faultCode)
319: || SOAP11Constants.QNAME_SENDER_FAULTCODE
320: .equals(faultCode)) {
321: return HttpServletResponse.SC_BAD_REQUEST;
322: }
323:
324: return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
325: }
326:
327: class SimpleHTTPRequestResponseTransport implements
328: RequestResponseTransport {
329:
330: private CountDownLatch responseReadySignal = new CountDownLatch(
331: 1);
332: RequestResponseTransportStatus status = RequestResponseTransportStatus.INITIAL;
333: AxisFault faultToBeThrownOut = null;
334: private boolean responseWritten = false;
335:
336: public void acknowledgeMessage(MessageContext msgContext)
337: throws AxisFault {
338: //TODO: Once the core HTTP API allows us to return an ack before unwinding, then the should be fixed
339: signalResponseReady();
340: }
341:
342: public void awaitResponse() throws InterruptedException,
343: AxisFault {
344: status = RequestResponseTransportStatus.WAITING;
345: responseReadySignal.await();
346:
347: if (faultToBeThrownOut != null) {
348: throw faultToBeThrownOut;
349: }
350: }
351:
352: public void signalResponseReady() {
353: status = RequestResponseTransportStatus.SIGNALLED;
354: responseReadySignal.countDown();
355: }
356:
357: public RequestResponseTransportStatus getStatus() {
358: return status;
359: }
360:
361: public void signalFaultReady(AxisFault fault) {
362: faultToBeThrownOut = fault;
363: signalResponseReady();
364: }
365:
366: public boolean isResponseWritten() {
367: return responseWritten;
368: }
369:
370: public void setResponseWritten(boolean responseWritten) {
371: this.responseWritten = responseWritten;
372: }
373:
374: }
375:
376: }
|