📄 protocol.java
字号:
*/ opens++; privateOut = new PrivateOutputStream(); outputStreamOpened = true; return new DataOutputStream(privateOut); } /** * PrivateInputStream to handle chunking for HTTP/1.1. */ protected class PrivateInputStream extends InputStream { int bytesleft; // Number of bytes left in current chunk boolean chunked; // true if Transfer-Encoding: chunked boolean eof; // true if eof seen PrivateInputStream() throws IOException { bytesleft = 0; chunked = false; eof = false; // Determine if this is a chunked datatransfer and setup String te = (String)headerFields.get("transfer-encoding"); if (te != null && te.equals("chunked")) { chunked = true; bytesleft = readChunkSize(); eof = (bytesleft == 0); } if (te == null) { // CR 6211256 // If there is no Transfer-Encoding header, a Content-Length // header may tell us how much to read. We will treat this // as a big "logical chunk" and set the number of bytes left // to the header's value. If we cannot parse the // value, throw an IOException as we would if we couldn't // read a chunk size; according to the spec, the User Agent // should notify the user that the value is bad. This CR // was filed against the Https procotol handler specifically, // but is included here because we should pay attention // to the content-length header, and to keep the two // PrivateInputStream classes in sync. String cl = (String)headerFields.get("content-length"); if (cl != null) { try { // Parse the content-length. If it fails to parse or // is < 0 it is invalid. bytesleft = Integer.parseInt(cl); } catch (NumberFormatException nfe) { // Deliberately set bytesleft to a bogus value bytesleft = -1; } finally { if (bytesleft < 0) { throw new IOException("Bad Content-Length value"); } eof = (bytesleft == 0); } } } } /** * Returns the number of bytes that can be read (or skipped over) * from this input stream without blocking by the next caller of * a method for this input stream. * * This method simply returns the number of bytes left from a * chunked response from an HTTP 1.1 server, or the remainder * of the Content-Length value if the response is not chunked. */ public int available() throws IOException { // System.out.println("available " + bytesleft + " " + connected); if (connected) { if (bytesleft > 0) { return bytesleft; } else { return streamInput.available(); } } else { throw new IOException("connection is not open"); } } /** * Reads the next byte of data from the input stream. The value byte is * returned as an <code>int</code> in the range <code>0</code> to * <code>255</code>. If no byte is available because the end of the * stream has been reached, the value <code>-1</code> is returned. This * method blocks until input data is available, the end of the stream is * detected, or an exception is thrown. * * <p> A subclass must provide an implementation of this method. * * @return the next byte of data, or <code>-1</code> if the end of * the stream is reached. * @exception IOException if an I/O error occurs. */ public int read() throws IOException { // Be consistent about returning EOF once encountered. if (eof) { return -1; } /* * If all the current chunk has been read and this * is a chunked transfer then read the next chunk length. */ if (bytesleft <= 0 && chunked) { readCRLF(); // Skip trailing \r\n bytesleft = readChunkSize(); if (bytesleft == 0) { eof = true; return -1; } } int ch = streamInput.read(); bytesleft--; // CR 6211256 // If we read an EOF, or if we are not chunked but // we've read all we expect to see (i.e., the Content-Length // header was set), then note that we've hit EOF. eof = (ch == -1) || (!chunked && bytesleft == 0); return ch; } /* * Reads up to <code>len</code> bytes of data from the input stream into * an array of bytes. An attempt is made to read as many as * <code>len</code> bytes, but a smaller number may be read, possibly * zero. The number of bytes actually read is returned as an integer. * * This method allows direct consumer-supplier connection * to avoid default byte-by-byte reading behaviour. */ public int read(byte[] b, int off, int len) throws IOException { /* * Need to check parameters here, because len may be changed * and streamInput.read() will not notice invalid argument. */ if (b == null) { throw new NullPointerException(); } else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } // Be consistent about returning EOF once encountered. if (eof) return -1; if ((chunked) && (bytesleft <= 0)) { readCRLF(); // Skip trailing \r\n bytesleft = readChunkSize(); if (bytesleft == 0) { eof = true; return -1; } } /* * Don't read more than was specified as available . * len will remain > 0, because * if bytesleft is 0, than eof was also true. */ if (len > bytesleft) { len = bytesleft; } int bytesRead = streamInput.read(b, off, len); if (bytesRead < 0) { eof = true; } else { bytesleft -= bytesRead; eof = (!chunked) && (bytesleft <= 0); } return bytesRead; } /* * Read the chunk size from the input. * It is a hex length followed by optional headers (ignored). * and terminated with <cr><lf>. */ private int readChunkSize() throws IOException { int size = -1; try { String chunk = readLine(streamInput); if (chunk == null) { throw new IOException("No Chunk Size"); } int i; for (i = 0; i < chunk.length(); i++) { char ch = chunk.charAt(i); if (Character.digit(ch, 16) == -1) break; } // look at extensions?.... size = Integer.parseInt(chunk.substring(0, i), 16); } catch (NumberFormatException e) { throw new IOException("Bogus chunk size"); } return size; } /* * Read <cr><lf> from the InputStream. * @exception IOException is thrown if either <CR> or <LF> * is missing. */ private void readCRLF() throws IOException { int ch; ch = streamInput.read(); if (ch != '\r') throw new IOException("missing CRLF"); ch = streamInput.read(); if (ch != '\n') throw new IOException("missing CRLF"); } public void close() throws IOException { // System.out.println("close input stream "+opens+" " + connected); if (opens == 0) return; if (--opens == 0 && connected) disconnect(); } } /** * Private OutputStream to allow the buffering of output * so the "Content-Length" header can be supplied. */ protected class PrivateOutputStream extends OutputStream { private ByteArrayOutputStream output; public PrivateOutputStream() { output = new ByteArrayOutputStream(); } public void write(int b) throws IOException { output.write(b); // CR 6216611: set the content-length. Note: we shouldn't set // content-length to the size of the current bytes that we are // writing. The length should be number of all valid bytes in the // output buffer. reqProperties.put("Content-Length", "" + output.size()); } /* * Override this method from OutputStream class, this is either called * directly or by writeUTF(), to set the header field "content-length" * to "len". CR 6216611 */ public void write(byte b[], int off, int len) throws IOException { output.write(b, off, len); // Update Content-Length. Note: we should't set // content-length to the size of the current bytes that we are // writing. The length should be number of all valid bytes in the // output buffer. reqProperties.put("Content-Length", "" + output.size()); } public void write(byte[] b) throws IOException { // Create the headers String reqLine = method + " " + (getFile() == null ? "/" : getFile()) + (getRef() == null ? "" : "#" + getRef()) + (getQuery() == null ? "" : "?" + getQuery()) + " " + httpVersion + "\r\n"; write((reqLine).getBytes(), 0, reqLine.length()); // HTTP 1/1 requests require the Host header to // distinguish virtual host locations. reqProperties.put("Host", host + ":" + port); Enumeration reqKeys = reqProperties.keys(); while (reqKeys.hasMoreElements()) { String key = (String)reqKeys.nextElement(); String reqPropLine = key + ": " + reqProperties.get(key) + "\r\n"; write((reqPropLine).getBytes(), 0, reqPropLine.length()); } write("\r\n".getBytes(), 0, "\r\n".length()); write(b, 0, b.length); // Update Content-Length. Note: we should't set // content-length to the size of the current bytes that we are // writing. The length should be number of all valid bytes in the // output buffer. reqProperties.put("Content-Length", "" + output.size()); } public void flush() throws IOException { if (output.size() > 0) { connect(); } } public byte[] toByteArray() { return output.toByteArray(); } public int size() { return output.size(); } public void close() throws IOException { // System.out.println("close output stream"+opens + " "+connected); // CR 6216611: If the connection is already closed, just return if (opens == 0) return; flush(); if (--opens == 0 && connected) disconnect(); outputStreamOpened = false; } private void reset() { output.reset(); } }// PrivateOutputStream protected void ensureOpen() throws IOException { if (opens == 0) throw new IOException("Connection closed"); } public String getURL() { // RFC: Add back protocol stripped by Content Connection. return protocol + ":" + url; } public String getProtocol() { return protocol; } public String getHost() { return (host.length() == 0 ? null : host); } public String getFile() { return (file.length() == 0 ? null : file); } public String getRef() { return (ref.length() == 0 ? null : ref); } public String getQuery() { return (query.length() == 0 ? null : query); } public int getPort() { return port; } public String getRequestMethod() { return method; } public void setRequestMethod(String method) throws IOException { ensureOpen(); if (connected) throw new IOException("connection already open"); if (!method.equals(HEAD) && !method.equals(GET) && !method.equals(POST)) { throw new IOException("unsupported method: " + method); } // ignore the request if the outputstream is already open if (outputStreamOpened) return; this.method = new String(method); } public String getRequestProperty(String key) { return (String)reqProperties.get(key); } private static boolean validateProperty(String s) { boolean res = true; if (s.endsWith("\r") || s.endsWith("\n")) { res = false; } else { // Check spaces after each \r\n for (int i = s.indexOf('\r'); i != -1; i = s.indexOf('\r', i+1)) { if (s.charAt(i+1) != '\n' || (s.charAt(i+2) != ' ' && s.charAt(i+2) != '\t')) { res = false; break; } } } return res; } public void setRequestProperty(String key, String value) throws IOException { ensureOpen(); if (connected) throw new IOException("connection already open"); if (outputStreamOpened) return; if (!validateProperty(value)) throw new IllegalArgumentException("Illegal request property"); /* * If application setRequestProperties("Connection", "close") * then we need to know this & take appropriate default close action */ if ((key.equalsIgnoreCase("connection")) && (value.equalsIgnoreCase("close"))) { ConnectionCloseFlag = true; } reqProperties.put(key, value); } public int getResponseCode() throws IOException { ensureOpen(); connect(); return responseCode; } public String getResponseMessage() throws IOException { ensureOpen(); connect(); return responseMsg; } public long getLength() { try { connect(); } catch (IOException x) { return -1; } try { return getHeaderFieldInt("content-length", -1); } catch (IOException e) { return -1; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -