001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: *
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: /**
020: * @author Mikhail A. Markov, Vasily Zakharov
021: * @version $Revision: 1.1.2.4 $
022: */package org.apache.harmony.rmi.transport.proxy;
023:
024: import java.io.ByteArrayOutputStream;
025: import java.io.DataOutputStream;
026: import java.io.IOException;
027: import java.io.OutputStream;
028: import java.security.AccessController;
029:
030: import org.apache.harmony.rmi.common.GetStringPropAction;
031: import org.apache.harmony.rmi.common.RMIConstants;
032: import org.apache.harmony.rmi.common.RMILog;
033: import org.apache.harmony.rmi.internal.nls.Messages;
034:
035: /**
036: * Output stream for HTTP connections.
037: * It sends data only once, wrapped into HTTP response.
038: *
039: * @author Mikhail A. Markov, Vasily Zakharov
040: * @version $Revision: 1.1.2.4 $
041: */
042: public class HttpOutputStream extends ByteArrayOutputStream implements
043: ProxyConstants {
044:
045: /**
046: * Underlying output stream.
047: */
048: private DataOutputStream out;
049:
050: /**
051: * If this is inbound connection stream.
052: */
053: private boolean inbound;
054:
055: /**
056: * Target host name (for HTTP headers).
057: */
058: private String host;
059:
060: /**
061: * Target port number (for HTTP headers).
062: */
063: private int port;
064:
065: /**
066: * If this is CGI stream.
067: */
068: private boolean cgi;
069:
070: /**
071: * If this stream was closed.
072: */
073: private boolean isClosed = false;
074:
075: /**
076: * Constructs this stream by wrapping the specified output stream.
077: * The resulting stream doesn't use CGI.
078: *
079: * @param out
080: * Output stream to wrap.
081: *
082: * @param inbound
083: * If this is inbound connection stream.
084: *
085: * @param host
086: * Target host name.
087: * Used for HTTP headers (for outbound streams)
088: * and for diagnostics.
089: * Optional (can be <code>null</code>) for inbound streams.
090: *
091: * @param port
092: * Target port number.
093: * Used for HTTP headers (for outbound streams),
094: * and for diagnostics.
095: * Is ignored if <code>host</code> is <code>null</code>).
096: */
097: public HttpOutputStream(OutputStream out, boolean inbound,
098: String host, int port) {
099: this (out, inbound, host, port, false);
100: }
101:
102: /**
103: * Constructs this stream by wrapping the specified output stream.
104: *
105: * @param out
106: * Output stream to wrap.
107: *
108: * @param inbound
109: * If this is inbound connection stream.
110: *
111: * @param host
112: * Target host name.
113: * Used for HTTP headers for outbound streams,
114: * may be used for diagnostics purposes for all streams.
115: * Optional (can be <code>null</code>) for inbound streams.
116: *
117: * @param port
118: * Target port number.
119: * Used for HTTP headers for outbound streams,
120: * may be used for diagnostics purposes for all streams.
121: * Optional for inbound streams.
122: *
123: * @param cgi
124: * If this is CGI stream (ignored for inbound streams).
125: */
126: public HttpOutputStream(OutputStream out, boolean inbound,
127: String host, int port, boolean cgi) {
128: super ();
129: this .out = new DataOutputStream(out);
130: this .inbound = inbound;
131: this .host = host;
132: this .port = port;
133: this .cgi = (inbound ? false : cgi);
134: }
135:
136: /**
137: * Converts the specified string to bytes using {@link String#getBytes()}
138: * and writes to the stream.
139: *
140: * @param s
141: * String to write.
142: *
143: * @throws IOException
144: * If I/O error occurs.
145: *
146: * @see java.io.DataOutput#writeBytes(String)
147: */
148: public final void writeBytes(String s) throws IOException {
149: write(s.getBytes());
150: }
151:
152: /**
153: * Wraps all data contained in this stream into HTTP response and writes it
154: * into the underlying output stream. This method can only be called once.
155: *
156: * @throws IOException
157: * If I/O error occurs.
158: */
159: public synchronized void close() throws IOException {
160: if (isClosed) {
161: // rmi.88=Repeated attempt to close HttpOutputStream
162: throw new IOException(Messages.getString("rmi.88")); //$NON-NLS-1$
163: }
164:
165: // Port the outbound connection is established to.
166: int connectPort = (cgi ? RMIConstants.HTTP_DEFAULT_PORT : port);
167:
168: // Sending HTTP POST request or OK response.
169: //
170: // Note: the following things may need reconsidering in future:
171: // - What headers should really be present here.
172: // - Which HTTP protocol version should be used.
173: // - What User-Agent name and version should be used.
174: // - What proxy control headers should be included.
175: //
176: // Note: reference implementation uses the following headers
177: // (retrieved using the black box testing by writing a dummy
178: // socket server that logs everything that comes to it,
179: // and making an RMI request to it with reference implementation):
180: // POST http://HOST:PORT/ HTTP/1.1
181: // Content-type: application/octet-stream
182: // Cache-Control: no-cache
183: // Pragma: no-cache
184: // User-Agent: Java/1.4.2_04
185: // Host: HOST:PORT
186: // Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
187: // Proxy-Connection: keep-alive
188: // Content-Length: LENGTH
189: out
190: .writeBytes((inbound ? HTTP_RESPONSE_HEADER
191: : (HTTP_REQUEST_SIGNATURE
192: + "http://" + host + ':' + connectPort //$NON-NLS-1$
193: + '/'
194: + (cgi ? ("cgi-bin/java-rmi?forward=" + port) : "") //$NON-NLS-1$ //$NON-NLS-2$
195: + " HTTP/1.1" + EOLN //$NON-NLS-1$
196: + "Cache-Control: no-cache" + EOLN + "Pragma: no-cache" + EOLN //$NON-NLS-1$ //$NON-NLS-2$
197: + "Host: " + host + ':' + connectPort + EOLN //$NON-NLS-1$
198: + "Proxy-Connection: keep-alive" + EOLN //$NON-NLS-1$
199: + "User-Agent: DRL/" + (String) AccessController.doPrivileged( //$NON-NLS-1$
200: new GetStringPropAction("java.version")))) + EOLN); //$NON-NLS-1$
201:
202: out.writeBytes("Content-type: application/octet-stream" + EOLN //$NON-NLS-1$
203: + CONTENT_LENGTH_SIGNATURE + ' ' + count + EOLN + EOLN);
204: out.write(buf, 0, count);
205: out.flush();
206:
207: reset();
208:
209: isClosed = true;
210:
211: if (proxyTransportLog.isLoggable(RMILog.VERBOSE)) {
212: proxyTransportLog
213: .log(
214: RMILog.VERBOSE,
215: "HTTP " + (inbound ? "response" : "request") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
216: + ((host != null) ? (" to [" + host + ':' + port + ']') //$NON-NLS-1$
217: : "") + " sent."); //$NON-NLS-1$ //$NON-NLS-2$
218: }
219: }
220: }
|