📄 protocol.java
字号:
/* * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. * * This software is the confidential and proprietary information of Sun * Microsystems, Inc. ("Confidential Information"). You shall not * disclose such Confidential Information and shall use it only in * accordance with the terms of the license agreement you entered into * with Sun. * * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING * THIS SOFTWARE OR ITS DERIVATIVES. * Use is subject to license terms. */package com.sun.cldc.io.j2me.http;import java.io.IOException;import java.io.InputStream;import java.io.InterruptedIOException;import java.io.OutputStream;import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.ByteArrayOutputStream;import java.util.Hashtable;import java.util.Enumeration;import javax.microedition.io.StreamConnection;import javax.microedition.io.Connector;import com.sun.cldc.io.connections.HttpConnection;import com.sun.cldc.io.ConnectionBase;import com.sun.cldc.io.DateParser;/** * This class implements the necessary functionality * for an HTTP connection. */public class Protocol extends ConnectionBase implements HttpConnection { private int index; // used by URL parsing functions private String url; private String protocol; private String host; private String file; private String ref; private String query; private int port = 80; private int responseCode; private String responseMsg; private Hashtable reqProperties; private Hashtable headerFields; private String[] headerFieldNames; private String[] headerFieldValues; private String method; private int opens; private int mode; private boolean connected; /* * In/out Streams used to buffer input and output */ private PrivateInputStream in; private PrivateOutputStream out; /* * The data streams provided to the application. * They wrap up the in and out streams. */ private DataInputStream appDataIn; private DataOutputStream appDataOut; /* * The streams from the underlying socket connection. */ private StreamConnection streamConnnection; private DataOutputStream streamOutput; private DataInputStream streamInput; /* * A shared temporary buffer used in a couple of places */ private StringBuffer stringbuffer; private String http_version = "HTTP/1.1"; /** * Create a new instance of this class. * We are initially unconnected. */ public Protocol() { reqProperties = new Hashtable(); headerFields = new Hashtable(); stringbuffer = new StringBuffer(32); opens = 0; connected = false; method = GET; responseCode = -1; protocol = "http"; } public void open(String url, int mode, boolean timeouts) throws IOException { if (opens > 0) { throw new IOException("already connected"); } opens++; if (mode != Connector.READ && mode != Connector.WRITE && mode != Connector.READ_WRITE) { throw new IOException("illegal mode: " + mode); } this.url = url; this.mode = mode; parseURL(); } public void close() throws IOException { if (--opens == 0 && connected) { disconnect(); } } /* * Open the input stream if it has not already been opened. * @exception IOException is thrown if it has already been * opened. */ public InputStream openInputStream() throws IOException { if (in != null) { throw new IOException("already open"); } // If the connection was opened and closed before the // data input stream is accessed, throw an IO exception if (opens == 0) { throw new IOException("connection is closed"); } // Check that the connection was opened for reading if (mode != Connector.READ && mode != Connector.READ_WRITE) { throw new IOException("write-only connection"); } connect(); opens++; in = new PrivateInputStream(); return in; } public DataInputStream openDataInputStream() throws IOException { if (appDataIn != null) { throw new IOException("already open"); } // TBD: throw in exception if the connection has been closed. if (in == null) { openInputStream(); } appDataIn = new DataInputStream(in); return appDataIn; } public OutputStream openOutputStream() throws IOException { if (mode != Connector.WRITE && mode != Connector.READ_WRITE) { throw new IOException("read-only connection"); } // If the connection was opened and closed before the // data output stream is accessed, throw an IO exception if (opens == 0) { throw new IOException("connection is closed"); } if (out != null) { throw new IOException("already open"); } opens++; out = new PrivateOutputStream(); return out; } public DataOutputStream openDataOutputStream() throws IOException { if (mode != Connector.WRITE && mode != Connector.READ_WRITE) { throw new IOException("read-only connection"); } // If the connection was opened and closed before the // data output stream is accessed, throw an IO exception if (opens == 0) { throw new IOException("connection is closed"); } if (out != null) { throw new IOException("already open"); } opens++; out = new PrivateOutputStream(); return new DataOutputStream(out); } /** * PrivateInputStream to handle chunking for HTTP/1.1. */ class PrivateInputStream extends InputStream { int bytesleft; // Number of bytes left in current chunk int bytesread; // Number of bytes read since the stream was opened boolean chunked; // True if Transfer-Encoding: chunked boolean eof; // True if EOF seen PrivateInputStream() throws IOException { bytesleft = 0; bytesread = 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); } } /** * 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. */ public int available() throws IOException { if (connected) { return bytesleft ; } 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(); eof = (ch == -1); bytesleft--; bytesread++; return ch; } /** * Reads some number of bytes from the input stream and * stores them into the buffer array <code>b</code>. * The number of bytes actually read is returned as an integer. * This method blocks until input data is available, end of * file is detected, or an exception is thrown. * (For HTTP requests where the content length has been * specified in the response headers, the size of the read * may be reduced if there are fewer bytes left than the * size of the supplied input buffer.) * * @param b the buffer into which the data is read. * @return the total number of bytes read into the buffer, * or <code>-1</code> is there is no more data * because the end of the stream has been reached. * @exception IOException if an I/O error occurs. * @see java.io.InputStream#read(byte[]) */ public int read(byte[]b) throws IOException { long len = getLength(); if (len != -1) { // More bytes are expected len -= bytesread; } else { // Buffered reading in chunks len = b.length; } if (len == 0) { eof = true ; // All expected bytes have been read return -1; } return read (b, 0, (int)(len < b.length ? len : b.length)) ; } /* 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 { if (--opens == 0 && connected) disconnect(); } } // End of class PrivateInputStream /** * Private OutputStream to allow the buffering of output * so the "Content-Length" header can be supplied. */ class PrivateOutputStream extends OutputStream { private ByteArrayOutputStream output; public PrivateOutputStream() { output = new ByteArrayOutputStream(); } public void write(int b) throws IOException { output.write(b); } public void write(byte[] b) throws IOException{ // Create the headers reqProperties.put("Content-Length", "" + b.length); String reqLine = method + " " + getFile() + (getRef() == null ? "" : "#" + getRef()) + (getQuery() == null ? "" : "?" + getQuery()) + " " + http_version + "\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); } 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 { flush(); if (--opens == 0 && connected) disconnect(); } } // End of class PrivateOutputStream 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 { if (connected) { throw new IOException("connection already open"); } if (!method.equals(HEAD) && !method.equals(GET) && !method.equals(POST)) { throw new IOException("unsupported method: " + method);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -