001: /*
002: * Copyright 2005-2007 Noelios Consulting.
003: *
004: * The contents of this file are subject to the terms of the Common Development
005: * and Distribution License (the "License"). You may not use this file except in
006: * compliance with the License.
007: *
008: * You can obtain a copy of the license at
009: * http://www.opensource.org/licenses/cddl1.txt See the License for the specific
010: * language governing permissions and limitations under the License.
011: *
012: * When distributing Covered Code, include this CDDL HEADER in each file and
013: * include the License file at http://www.opensource.org/licenses/cddl1.txt If
014: * applicable, add the following below this CDDL HEADER, with the fields
015: * enclosed by brackets "[]" replaced with your own identifying information:
016: * Portions Copyright [yyyy] [name of copyright owner]
017: */
018:
019: package com.noelios.restlet.http;
020:
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.io.OutputStream;
024: import java.net.Socket;
025: import java.net.UnknownHostException;
026: import java.nio.channels.ReadableByteChannel;
027: import java.nio.channels.WritableByteChannel;
028: import java.util.logging.Level;
029:
030: import org.restlet.data.Parameter;
031: import org.restlet.data.Request;
032: import org.restlet.data.Status;
033:
034: /**
035: * HTTP client call based on streams.
036: *
037: * @author Jerome Louvel (contact@noelios.com)
038: */
039: public class StreamClientCall extends HttpClientCall {
040: /** The request to send. */
041: private Request request;
042:
043: /** The request output stream. */
044: private OutputStream requestStream;
045:
046: /** The response input stream. */
047: private InputStream responseStream;
048:
049: /**
050: * Constructor.
051: *
052: * @param helper
053: * The client connector helper.
054: * @param request
055: * The request to send.
056: */
057: public StreamClientCall(StreamClientHelper helper, Request request) {
058: super (helper, request.getMethod().toString(), request
059: .getResourceRef().getIdentifier());
060:
061: // Set the HTTP version
062: setVersion("HTTP/1.1");
063: }
064:
065: /**
066: * Creates the socket that will be used to send the request and get the
067: * response.
068: *
069: * @param hostDomain
070: * The target host domain name.
071: * @param hostPort
072: * The target host port.
073: * @return The created socket.
074: * @throws UnknownHostException
075: * @throws IOException
076: */
077: public Socket createSocket(String hostDomain, int hostPort)
078: throws UnknownHostException, IOException {
079: return new Socket(hostDomain, hostPort);
080: }
081:
082: /**
083: * Returns the request to send.
084: *
085: * @return The request to send.
086: */
087: public Request getRequest() {
088: return this .request;
089: }
090:
091: @Override
092: public WritableByteChannel getRequestChannel() {
093: return null;
094: }
095:
096: @Override
097: public OutputStream getRequestStream() {
098: return this .requestStream;
099: }
100:
101: @Override
102: public ReadableByteChannel getResponseChannel() {
103: return null;
104: }
105:
106: @Override
107: public InputStream getResponseStream() {
108: return this .responseStream;
109: }
110:
111: /**
112: * Parses the HTTP response.
113: *
114: * @throws IOException
115: */
116: protected void parseResponse() throws IOException {
117: StringBuilder sb = new StringBuilder();
118:
119: // Parse the HTTP version
120: int next = getResponseStream().read();
121: while ((next != -1) && !HttpUtils.isSpace(next)) {
122: sb.append((char) next);
123: next = getResponseStream().read();
124: }
125:
126: if (next == -1) {
127: throw new IOException(
128: "Unable to parse the response HTTP version. End of stream reached too early.");
129: } else {
130: setVersion(sb.toString());
131: sb.delete(0, sb.length());
132:
133: // Parse the status code
134: next = getResponseStream().read();
135: while ((next != -1) && !HttpUtils.isSpace(next)) {
136: sb.append((char) next);
137: next = getResponseStream().read();
138: }
139:
140: if (next == -1) {
141: throw new IOException(
142: "Unable to parse the response status. End of stream reached too early.");
143: } else {
144: setStatusCode(Integer.parseInt(sb.toString()));
145: sb.delete(0, sb.length());
146:
147: // Parse the reason phrase
148: next = getResponseStream().read();
149: while ((next != -1)
150: && !HttpUtils.isCarriageReturn(next)) {
151: sb.append((char) next);
152: next = getResponseStream().read();
153: }
154:
155: if (next == -1) {
156: throw new IOException(
157: "Unable to parse the reason phrase. End of stream reached too early.");
158: } else {
159: next = getResponseStream().read();
160:
161: if (HttpUtils.isLineFeed(next)) {
162: setReasonPhrase(sb.toString());
163: sb.delete(0, sb.length());
164:
165: // Parse the headers
166: Parameter header = HttpUtils.readHeader(
167: getResponseStream(), sb);
168: while (header != null) {
169: getResponseHeaders().add(header);
170: header = HttpUtils.readHeader(
171: getResponseStream(), sb);
172: }
173: } else {
174: throw new IOException(
175: "Unable to parse the reason phrase. The carriage return must be followed by a line feed.");
176: }
177: }
178: }
179: }
180: }
181:
182: @Override
183: public Status sendRequest(Request request) {
184: Status result = null;
185:
186: try {
187: // Extract the host info
188: String hostDomain = request.getResourceRef()
189: .getHostDomain();
190: int hostPort = request.getResourceRef().getHostPort();
191: if (hostPort == -1) {
192: hostPort = request.getResourceRef().getSchemeProtocol()
193: .getDefaultPort();
194: }
195:
196: // Create the client socket
197: Socket socket = createSocket(hostDomain, hostPort);
198: this .requestStream = socket.getOutputStream();
199: this .responseStream = socket.getInputStream();
200:
201: // Write the request line
202: getRequestStream().write(getMethod().getBytes());
203: getRequestStream().write(' ');
204: getRequestStream().write(getRequestUri().getBytes());
205: getRequestStream().write(' ');
206: getRequestStream().write(getVersion().getBytes());
207: getRequestStream().write(13); // CR
208: getRequestStream().write(10); // LF
209:
210: // We don't support persistent connections yet
211: getRequestHeaders().set(HttpConstants.HEADER_CONNECTION,
212: "close", true);
213:
214: // We don't support persistent connections yet
215: String host = hostDomain;
216: if (request.getResourceRef().getHostPort() != -1) {
217: host += ":" + request.getResourceRef().getHostPort();
218: }
219: getRequestHeaders().set(HttpConstants.HEADER_HOST, host,
220: true);
221:
222: // Write the request headers
223: for (Parameter header : getRequestHeaders()) {
224: HttpUtils.writeHeader(header, getRequestStream());
225: }
226:
227: // Write the end of the headers section
228: getRequestStream().write(13); // CR
229: getRequestStream().write(10); // LF
230:
231: // Write the request body
232: result = super .sendRequest(request);
233:
234: // Parse the response
235: parseResponse();
236: } catch (IOException ioe) {
237: getHelper()
238: .getLogger()
239: .log(
240: Level.WARNING,
241: "An error occured during the communication with the remote HTTP server.",
242: ioe);
243: result = new Status(
244: Status.CONNECTOR_ERROR_COMMUNICATION,
245: "Unable to complete the HTTP call due to a communication error with the remote server. "
246: + ioe.getMessage());
247: }
248:
249: return result;
250: }
251: }
|