All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.acplt.oncrpc.XdrHttpDecodingStream Maven / Gradle / Ivy

There is a newer version: 1.1.6
Show newest version
/*
 * $Header: /home/harald/repos/remotetea.sf.net/remotetea/src/org/acplt/oncrpc/XdrHttpDecodingStream.java,v 1.3 2005/11/11 21:23:44 haraldalbrecht Exp $
 *
 * Copyright (c) 1999, 2000
 * Lehrstuhl fuer Prozessleittechnik (PLT), RWTH Aachen
 * D-52064 Aachen, Germany.
 * All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this program (see the file LICENSE.txt for more
 * details); if not, write to the Free Software Foundation, Inc.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package org.acplt.oncrpc;

import java.io.*;
import java.net.*;
import org.acplt.oncrpc.web.HttpClientConnection;
import org.acplt.oncrpc.web.HttpTunnelConstants;
import org.acplt.oncrpc.web.Base64;

/**
 * The XdrHttpDecodingStream class provides the necessary
 * functionality to {@link XdrDecodingStream} to receive XDR data through
 * HTTP tunnels.
 *
 * 

Please note that there is currently no standard about how to tunnel * XDR data over HTTP connections. There are a (quite a) few solutions out * there, but they are more or less incompatible due to the lack of a RFC. * *

This class is responsible solely for receiving ONC/RPC replies. * The reply data is base64 encoded and embedded within an ordinary plain * ASCII page, as is shown in this example. * *

 *     DEADBEEFDEADBEEFDEADBEEF...<CR><LF>
 *     B0D0EADSDEADBEEFB0D0EADS...<CR><LF>
 *     ...<CR><LF>
 *     DEADBE==<CR><LF>
 * 
* *

Parsing is minimalistic to make the whole sucker as fast as possible (not * looking at Java's performance at all). * * @version $Revision: 1.3 $ $Date: 2005/11/11 21:23:44 $ $State: Exp $ $Locker: $ * @author Harald Albrecht */ public class XdrHttpDecodingStream extends XdrDecodingStream { /** * Constructs a new XdrHttpDecodingStream. * * @param httpClient HTTP client connection from which to read the * encoded and embedded ONC/RPC reply message. */ public XdrHttpDecodingStream(HttpClientConnection httpClient) { this.httpClient = httpClient; // // // Calculate the buffer size depending on the number of plain // ASCII lines we try to read and process as one chunk. Then allocate // the necessary buffers. // int lines = HttpTunnelConstants.LINES_PER_BLOCK; int bufferSize = lines * HttpTunnelConstants.BYTES_PER_LINE; int asciiBufferSize = lines * HttpTunnelConstants.ENCODED_BYTES_PER_LINE_CRLF; buffer = new byte[bufferSize]; asciiBuffer = new byte[asciiBufferSize]; this.asciiBufferSize = asciiBufferSize; // // Reset the incomming buffer, just to be sure. // bufferIndex = 0; bufferHighmark = -4; } /** * Returns the Internet address of the sender of the current XDR data. * This method should only be called after {@link #beginDecoding}, * otherwise it might return stale information. * * @return InetAddress of the sender of the current XDR data. */ public InetAddress getSenderAddress() { return null; // FIXME } /** * Returns the port number of the sender of the current XDR data. * This method should only be called after {@link #beginDecoding}, * otherwise it might return stale information. * * @return Port number of the sender of the current XDR data. */ public int getSenderPort() { return 0; // FIXME } /** * Initiates decoding of the next XDR record. For HTTP-based XDR we * just read the content delivered with the answer to the POST command. * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ public void beginDecoding() throws OncRpcException, IOException { // // Note that we don't assume getting a content length in advance, // but instead read 'till we drop. This is assumption is necessary // because we can not rely on keep-alive connections but instead // need to be able to deal with ordinay the close-after-receipt // interaction pattern of older HTTP versions. // httpClient.beginDecoding(); // // Just set up the (binary) data buffer to contain no data so that // the next decoding of some XDR data will result in the buffer being // filled from the HTTP connection. Delegation is sooo sweeeet! // bufferIndex = 0; bufferHighmark = -4; } /** * End decoding of the current XDR record. The general contract of * endDecoding is that calling it is an indication that * the current record is no more interesting to the caller and any * allocated data for this record can be freed. * *

To help the HTTP connection keeping alive, we swallow all data * until we reach the end. If this is not possible, either because the * server indicated that it can not keep the connection open, the content * length was unknown in advance, or we got an I/O exception, we close * the connection. * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ public void endDecoding() throws OncRpcException, IOException { // // Let the HTTP client object handle all the crude aspects of keeping // alive connections, swallowing unwanted chunked data, etc. // try { httpClient.endDecoding(); } catch ( IOException e ) { } // // Make sure that no-one can accidently retrieve any stale data from // the decoding buffer. // bufferIndex = 0; bufferHighmark = -4; } /** * Closes this decoding XDR stream and releases any system resources * associated with this stream. A closed XDR stream cannot perform decoding * operations and cannot be reopened. * *

This implementation frees the allocated buffer but does not close * the associated datagram socket. It only throws away the reference to * this socket. * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ public void close() throws OncRpcException, IOException { // // Do not close the httpClient, as it does not belong to us, but we // simply got a reference to use this particular client. // httpClient = null; buffer = null; } /** * Receives more encoded data over the HTTP connection and decodes it into * octets, making them available through the buffer field. * * @throws OncRpcException Decoding of the received 64base encoded stream failed. * @throws IOException IO error reading the underlying byte stream. */ private void fill() throws OncRpcException, IOException { // // In the first step we receive the base64 encoded plain ASCII data // through the HTTP connection one block at a time. // int charsRead; try { int remaining = httpClient.getRemainingContentLength(); if ( remaining < 0 ) { charsRead = httpClient.readContentBytes(asciiBuffer, 0, asciiBufferSize); } else { charsRead = httpClient.readContentBytes(asciiBuffer, 0, remaining); } } catch ( ProtocolException e ) { throw(new OncRpcException(OncRpcException.RPC_BUFFERUNDERFLOW)); } // // In the next step we decode the base64 data and store the octets into // the real buffer. // int decoded = 0; int encoded = 0; int toDecode; while ( charsRead > 0 ) { // FIXME: more sanity checks... // We currently simply assume that the last two characters are CRLF // and thus skip them. toDecode = charsRead >= HttpTunnelConstants.ENCODED_BYTES_PER_LINE ? HttpTunnelConstants.ENCODED_BYTES_PER_LINE : (charsRead - 2); decoded += Base64.decode(asciiBuffer, encoded, toDecode, buffer, decoded); // // FIXME: check CRLF // encoded += toDecode + 2; charsRead -= toDecode + 2; } // // Set the buffer "pointers" accordingly. // bufferIndex = 0; bufferHighmark = decoded - 4; } /** * Decodes (aka "deserializes") a "XDR int" value received from a * XDR stream. A XDR int is 32 bits wide -- the same width Java's "int" * data type has. * * @return The decoded int value. * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ public int xdrDecodeInt() throws OncRpcException, IOException { if ( bufferIndex > bufferHighmark ) { fill(); } // // There's enough space in the buffer to hold at least one // XDR int. So let's retrieve it now. // Note: buffer[...] gives a byte, which is signed. So if we // add it to the value (which is int), it has to be widened // to 32 bit, so its sign is propagated. To avoid this sign // madness, we have to "and" it with 0xFF, so all unwanted // bits are cut off after sign extension. Sigh. // int value = buffer[bufferIndex++] & 0xFF; value = (value << 8) + (buffer[bufferIndex++] & 0xFF); value = (value << 8) + (buffer[bufferIndex++] & 0xFF); value = (value << 8) + (buffer[bufferIndex++] & 0xFF); return value; } /** * Decodes (aka "deserializes") an opaque value, which is nothing more * than a series of octets (or 8 bits wide bytes). Because the length * of the opaque value is given, we don't need to retrieve it from the * XDR stream. This is different from * {@link #xdrDecodeOpaque(byte[], int, int)} where * first the length of the opaque value is retrieved from the XDR stream. * * @param length Length of opaque data to decode. * * @return Opaque data as a byte vector. * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ public byte [] xdrDecodeOpaque(int length) throws OncRpcException, IOException { int padding = (4 - (length & 3)) & 3; int offset = 0; // current offset into bytes vector int toCopy; // // Now allocate enough memory to hold the data to be retrieved and // get part after part from the buffer. // byte [] bytes = new byte[length]; if ( bufferIndex > bufferHighmark ) { fill(); } while ( length > 0 ) { toCopy = bufferHighmark - bufferIndex + 4; if ( toCopy >= length ) { // // The buffer holds more data than we need. So copy the bytes // and leave the stage. // System.arraycopy(buffer, bufferIndex, bytes, offset, length); bufferIndex += length; // No need to adjust "offset", because this is the last round. break; } else { // // We need to copy more data than currently available from our // buffer, so we copy all we can get our hands on, then fill // the buffer again and repeat this until we got all we want. // System.arraycopy(buffer, bufferIndex, bytes, offset, toCopy); bufferIndex += toCopy; offset += toCopy; length -= toCopy; fill(); } } bufferIndex += padding; return bytes; } /** * Decodes (aka "deserializes") a XDR opaque value, which is represented * by a vector of byte values, and starts at offset with a * length of length. Only the opaque value is decoded, so the * caller has to know how long the opaque value will be. The decoded data * is always padded to be a multiple of four (because that's what the * sender does). * * @param opaque Byte vector which will receive the decoded opaque value. * @param offset Start offset in the byte vector. * @param length the number of bytes to decode. * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ public void xdrDecodeOpaque(byte [] opaque, int offset, int length) throws OncRpcException, IOException { int padding = (4 - (length & 3)) & 3; int toCopy; // // Now get part after part and fill the byte vector. // if ( bufferIndex > bufferHighmark ) { fill(); } while ( length > 0 ) { toCopy = bufferHighmark - bufferIndex + 4; if ( toCopy >= length ) { // // The buffer holds more data than we need. So copy the bytes // and leave the stage. // System.arraycopy(buffer, bufferIndex, opaque, offset, length); bufferIndex += length; // No need to adjust "offset", because this is the last round. break; } else { // // We need to copy more data than currently available from our // buffer, so we copy all we can get our hands on, then fill // the buffer again and repeat this until we got all we want. // System.arraycopy(buffer, bufferIndex, opaque, offset, toCopy); bufferIndex += toCopy; offset += toCopy; length -= toCopy; fill(); } } bufferIndex += padding; } /** * Client HTTP tunnel to retrieve embedded XDR records from. */ private HttpClientConnection httpClient; /** * The buffer which will be filled from the datagram socket and then * be used to supply the information when decoding data. */ private byte [] buffer; /** * The buffer receiving base64 encoded plain ASCII data from a HTTP web * server. This buffer is only used for immediate decoding of the binary * data, which is then stored in the usual buffer field. */ private byte [] asciiBuffer; /** * Size of buffer for receiving the base64 encoded plain ASCII data. The * encoded data is then immediately decoded after recept into "ordinary" * binary data, which is stored in the usual buffer field. */ private int asciiBufferSize; /** * The read pointer is an index into the buffer. */ private int bufferIndex; /** * Index of the last four byte word in the buffer, which has been read * in from the datagram socket. */ private int bufferHighmark; } // End of XdrHttpDecodingStream





© 2015 - 2024 Weber Informatics LLC | Privacy Policy