aboutsummaryrefslogtreecommitdiff
path: root/src/share/classes/sun/rmi/transport/proxy/HttpInputStream.java
blob: 229a2cebd130e7bf28a4351197fb38060fefa9b0 (plain)
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
/*
 * Copyright 1996-2001 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
package sun.rmi.transport.proxy;

import java.io.*;

import sun.rmi.runtime.Log;

/**
 * The HttpInputStream class assists the HttpSendSocket and HttpReceiveSocket
 * classes by filtering out the header for the message as well as any
 * data after its proper content length.
 */
class HttpInputStream extends FilterInputStream {

    /** bytes remaining to be read from proper content of message */
    protected int bytesLeft;

    /** bytes remaining to be read at time of last mark */
    protected int bytesLeftAtMark;

    /**
     * Create new filter on a given input stream.
     * @param in the InputStream to filter from
     */
    public HttpInputStream(InputStream in) throws IOException
    {
        super(in);

        if (in.markSupported())
            in.mark(0); // prevent resetting back to old marks

        // pull out header, looking for content length

        DataInputStream dis = new DataInputStream(in);
        String key = "Content-length:".toLowerCase();
        boolean contentLengthFound = false;
        String line;
        do {
            line = dis.readLine();

            if (RMIMasterSocketFactory.proxyLog.isLoggable(Log.VERBOSE)) {
                RMIMasterSocketFactory.proxyLog.log(Log.VERBOSE,
                    "received header line: \"" + line + "\"");
            }

            if (line == null)
                throw new EOFException();

            if (line.toLowerCase().startsWith(key)) {
                if (contentLengthFound)
                    ; // what would we want to do in this case??
                bytesLeft =
                    Integer.parseInt(line.substring(key.length()).trim());
                contentLengthFound = true;
            }

            // The idea here is to go past the first blank line.
            // Some DataInputStream.readLine() documentation specifies that
            // it does include the line-terminating character(s) in the
            // returned string, but it actually doesn't, so we'll cover
            // all cases here...
        } while ((line.length() != 0) &&
                 (line.charAt(0) != '\r') && (line.charAt(0) != '\n'));

        if (!contentLengthFound || bytesLeft < 0) {
            // This really shouldn't happen, but if it does, shoud we fail??
            // For now, just give up and let a whole lot of bytes through...
            bytesLeft = Integer.MAX_VALUE;
        }
        bytesLeftAtMark = bytesLeft;

        if (RMIMasterSocketFactory.proxyLog.isLoggable(Log.VERBOSE)) {
            RMIMasterSocketFactory.proxyLog.log(Log.VERBOSE,
                "content length: " + bytesLeft);
        }
    }

    /**
     * Returns the number of bytes that can be read with blocking.
     * Make sure that this does not exceed the number of bytes remaining
     * in the proper content of the message.
     */
    public int available() throws IOException
    {
        int bytesAvailable = in.available();
        if (bytesAvailable > bytesLeft)
            bytesAvailable = bytesLeft;

        return bytesAvailable;
    }

    /**
     * Read a byte of data from the stream.  Make sure that one is available
     * from the proper content of the message, else -1 is returned to
     * indicate to the user that the end of the stream has been reached.
     */
    public int read() throws IOException
    {
        if (bytesLeft > 0) {
            int data = in.read();
            if (data != -1)
                -- bytesLeft;

            if (RMIMasterSocketFactory.proxyLog.isLoggable(Log.VERBOSE)) {
                RMIMasterSocketFactory.proxyLog.log(Log.VERBOSE,
                   "received byte: '" +
                    ((data & 0x7F) < ' ' ? " " : String.valueOf((char) data)) +
                    "' " + data);
            }

            return data;
        }
        else {
            RMIMasterSocketFactory.proxyLog.log(Log.VERBOSE,
                                                "read past content length");

            return -1;
        }
    }

    public int read(byte b[], int off, int len) throws IOException
    {
        if (bytesLeft == 0 && len > 0) {
            RMIMasterSocketFactory.proxyLog.log(Log.VERBOSE,
                                                "read past content length");

            return -1;
        }
        if (len > bytesLeft)
            len = bytesLeft;
        int bytesRead = in.read(b, off, len);
        bytesLeft -= bytesRead;

        if (RMIMasterSocketFactory.proxyLog.isLoggable(Log.VERBOSE)) {
            RMIMasterSocketFactory.proxyLog.log(Log.VERBOSE,
                "read " + bytesRead + " bytes, " + bytesLeft + " remaining");
        }

        return bytesRead;
    }

    /**
     * Mark the current position in the stream (for future calls to reset).
     * Remember where we are within the proper content of the message, so
     * that a reset method call can recreate our state properly.
     * @param readlimit how many bytes can be read before mark becomes invalid
     */
    public void mark(int readlimit)
    {
        in.mark(readlimit);
        if (in.markSupported())
            bytesLeftAtMark = bytesLeft;
    }

    /**
     * Repositions the stream to the last marked position.  Make sure to
     * adjust our position within the proper content accordingly.
     */
    public void reset() throws IOException
    {
        in.reset();
        bytesLeft = bytesLeftAtMark;
    }

    /**
     * Skips bytes of the stream.  Make sure to adjust our
     * position within the proper content accordingly.
     * @param n number of bytes to be skipped
     */
    public long skip(long n) throws IOException
    {
        if (n > bytesLeft)
            n = bytesLeft;
        long bytesSkipped = in.skip(n);
        bytesLeft -= bytesSkipped;
        return bytesSkipped;
    }
}