Package net.noderunner.http

Source Code of net.noderunner.http.HttpUtil

/*
* E-XML Library:  For XML, XML-RPC, HTTP, and related.
* Copyright (C) 2002-2008  Elias Ross
*
* genman@noderunner.net
* http://noderunner.net/~genman
*
* 1025 NE 73RD ST
* SEATTLE WA 98115
* USA
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* $Id$
*/

package net.noderunner.http;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

/**
* Contains utility functions for common HTTP I/O tasks.
*/
public class HttpUtil
{
  static final String CRLF = "\r\n";

  private static byte junk[] = null;
  private static final BitSet noencode;
  private static final byte hex[] = {
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    'A', 'B', 'C', 'D', 'E', 'F'};

  static {
    noencode = new BitSet(256);
    for (int i = 'a'; i <= 'z'; i++)
      noencode.set(i);
    for (int i = 'A'; i <= 'Z'; i++)
      noencode.set(i);
    for (int i = '0'; i <= '9'; i++)
      noencode.set(i);
    noencode.set('-');
    noencode.set('_');
    noencode.set('.');
    noencode.set('*');
  }

  /**
   * Cannot be instantiated.
   */
  private HttpUtil() {
  }

  /**
   * Reads an input stream until EOF.
   * Returns the number of bytes read.
   */
  public static int readFully(InputStream stream)
    throws IOException
  {
    if (stream == null)
      throw new IllegalArgumentException("Null stream");
    // not synchronized, but not a big deal
    if (junk == null)
      junk = new byte[1024];

    int total = 0;
    int got = 0;
    while (true) {
      got = stream.read(junk, 0, junk.length);
      if (got == -1)
        return total;
      total += got;
    }
  }

  /**
   * Reads an input stream until EOF.
   * Returns the bytes read.
   */
  public static byte[] read(InputStream stream)
    throws IOException
  {
    byte b[] = new byte[128];
    ByteArrayOutputStream bo = new ByteArrayOutputStream();
    int got = 0;
    while (true) {
      got = stream.read(b, 0, b.length);
      if (got == -1)
        return bo.toByteArray();
      bo.write(b, 0, got);
    }
  }
 
  /**
   * Returns a character reader for reading HTTP data.
   * Returns either an <code>InputStreamReader</code>, wrapping
   * either a {@link ChunkedInputStream} or
   * {@link LimitedInputStream}, based on the supplied headers.
   * If the headers did not indicate data was being sent, returns
   * an input stream for reading the rest of the document.
   */
  public static InputStream wrapInputStream(InputStream stream, MessageHeaders hl)
    throws IOException
  {
    if (stream == null)
      throw new IllegalArgumentException("Null stream");
    if (hl == null)
      throw new IllegalArgumentException("Null headers");

    boolean chunked = hl.contains(MessageHeader.MH_TRANSFER_ENCODING_CHUNKED);
    if (chunked)
      return new ChunkedInputStream(stream);

    String s = hl.getFieldContent(MessageHeader.FN_CONTENT_LENGTH);
    if (s == null)
      return stream;

    try {
      int length = Integer.parseInt(s);
      return new LimitedInputStream(stream, length);
    } catch (NumberFormatException e) {
      throw new HttpException("content-length not a number:" + s);
    }
  }

  /**
   * Returns a new input stream delegating to the given input stream
   * that does nothing when closed.
   */
  public static InputStream uncloseableInputStream(final InputStream stream)
    throws IOException
  {
      return new FilterInputStream(stream) {

                @Override
                public void close() throws IOException {
                }
             
      };
  }
 
  /**
   * Returns a single line from a 8-bit <code>InputStream</code>
   * (which is assumed to be ASCII).
   */
  public static String readHttpLine(InputStream in)
    throws IOException
  {
    StringBuilder sb = new StringBuilder();
    int c = 0;
    while (true) {
      c = in.read();
      if (c == -1)
        throw new EOFException("EOF processing HTTP request");
      if (c == '\n')
        break;
      if (c == '\r') {
        c = in.read();
        if (c != '\n')
          throw new HttpException("Expected LN character reading HTTP information");
        break;
      }
      sb.append((char)c);
      if (sb.length() > 1024)
        throw new HttpException("Header line too long");
    }
    return sb.toString();
  }


  /**
   * URL encodes a series of parameters.
   * Names are followed by their value in the array.  For example:
   * <pre>
   * byte[] buf;
   * buf = HttpUtil.urlEncode(new String[] { "name1", "value1", "name2", "value2" });
   * </pre>
   * results in the byte array:
   * <pre>
   * "name1=value1&amp;name2=value2"
   * </pre>
   * Strings are converted to bytes using the given encoding.
   * @param nvPairs array of name-value pairs, must be
   * even in length
   * @param encoding Java encoding, or null to use the default encoding
   * @return a byte array which can be used in {@link EasyHttpClient#doPostUrlEncoded}
   * @throws UnsupportedEncodingException if the encoding is invalid
   */
  public static byte[] urlEncode(String[] nvPairs, String encoding)
    throws UnsupportedEncodingException
  {
    if (nvPairs == null)
      throw new IllegalArgumentException("Null array");
    if (nvPairs.length % 2 != 0)
      throw new IllegalArgumentException("Odd length array");
    ByteArrayOutputStream bb;
    bb = new ByteArrayOutputStream(16 * nvPairs.length);
    for (int i = 0; i < nvPairs.length; i += 2)
      appendNV(bb, nvPairs[i], nvPairs[i + 1], encoding);
    return bb.toByteArray();
  }

  /**
   * URL encodes a series of parameters, using the
   * Strings are converted to the default platform encoding.
   */
  public static byte[] urlEncode(String[] nvPairs) {
    try {
      return urlEncode(nvPairs, null);
    } catch (UnsupportedEncodingException e) {
      throw new Error("should not be here");
    }
  }

  /**
   * URL encodes a single value, writing its value to
   * an output stream.
   */
  public static void urlEncode(ByteArrayOutputStream os, byte[] buf)
  {
    for (int i = 0; i < buf.length; i++) {
      byte b = buf[i];
      if (noencode.get(b)) {
        os.write(b);
      } else if (b == ' ') {
        os.write((byte)'+');
      } else {
        os.write((byte)'%');
        int low = (b & 0x0f);
        int high = ((b & 0xf0) >> 4);
        os.write(hex[high]);
        os.write(hex[low]);
      }
    }
  }

  /**
   * Appends a key-value pair to an output stream buffer.
   */
  private static void appendNV(ByteArrayOutputStream os, String n, String v, String enc)
    throws java.io.UnsupportedEncodingException
  {
    if (os.size() > 0)
      os.write((byte)'&');
    if (enc == null)
      urlEncode(os, n.getBytes());
    else
      urlEncode(os, n.getBytes(enc));
    if (v != null) {
      os.write((byte)'=');
      if (enc == null)
        urlEncode(os, v.getBytes());
      else
        urlEncode(os, v.getBytes(enc));
    }
  }

  /**
   * URL encodes a <code>Map</code>.
   * @param map a name-value map of entries to encode
   * @param encoding Java encoding, or null to use the default encoding
   * @return a byte buffer which can be used in {@link EasyHttpClient#doPostUrlEncoded}
   * @throws UnsupportedEncodingException if the encoding is invalid
   */
  public static byte[] urlEncode(Map<String, String> map, String encoding)
    throws UnsupportedEncodingException
  {
    if (map == null)
      throw new IllegalArgumentException("Null map");
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    for (Map.Entry<String, String> me : map.entrySet()) {
      appendNV(os, me.getKey(), me.getValue(), encoding);
    }
    return os.toByteArray();
  }

  /**
   * URL encodes a <code>Map</code>.
   * @param map a name-value map of entries to encode
   * @return a byte buffer which can be used in {@link EasyHttpClient#doPostUrlEncoded}
   * Strings are converted to the default platform encoding.
   */
  public static byte[] urlEncode(Map<String, String> map) {
    try {
      return urlEncode(map, null);
    } catch (UnsupportedEncodingException e) {
      throw new Error("should not be here");
    }
  }

  /**
   * URL decodes a string.
   */
  @SuppressWarnings("deprecation")
  private static String decode(String s) {
    return URLDecoder.decode(s);
  }

  /**
   * Returns the number of times a character appears in a string.
   */
  private static int count(String s, char c) {
    int count = 0;
    for (int i = 0; i < s.length(); i++)
      if (s.charAt(i) == c)
        count++;
    return count;
  }
 
  /**
   * Creates and returns a new array one entry longer, with a new value at the end.
   * @param sa existing array
   * @param s new value
   * @return new array
   */
  public static String[] add(String sa[], String s) {
    String sa2[] = new String[sa.length + 1];
    System.arraycopy(sa, 0, sa2, 0, sa.length);
    sa2[sa.length] = s;
    return sa2;
   
  }
 
  private static void put(Map<String, String[]> map, String n, String v) {
    String[] sa = map.get(n);
    if (sa == null)
      map.put(n, new String[] { v });
    else
      map.put(n, add(sa, v));
  }
 
  /**
   * URL decodes a string into a <code>Map</code>.
   * Names without values are placed in the map, but given a
   * <code>null</code> value.  For example:
   * <pre>
   * foo&amp;bar=&amp;baz
   * </pre>
   * results in a map with keys <code>foo</code>, <code>bar</code>,
   * <code>baz</code> mapped to <code>""</code>.
   * If the same key appears more than once, it is added to the array.
   * This method will not throw exceptions
   * even if the data is irregular.
   *
   * @param urlEncodedData data in the URL encoded format
   * @return a map containing the decoded name-value pairs
   * @throws IllegalArgumentException if null data is passed in
   * @see #urlEncode
   */
  public static Map<String, String[]> urlDecode(String urlEncodedData) {
    if (urlEncodedData == null)
      throw new IllegalArgumentException("Null urlEncodedData");
    urlEncodedData = urlEncodedData.trim();
    StringTokenizer st = new StringTokenizer(urlEncodedData, "&=", true);
    Map<String, String[]> map = new HashMap<String, String[]>();
    String n;
    String eq;
    String v;
    while (st.hasMoreTokens()) {
      n = st.nextToken();
      if (!st.hasMoreTokens()) {
          put(map, decode(n), "");
        break;
      }
      eq = st.nextToken();
      if (!st.hasMoreTokens()) {
          put(map, decode(n), "");
        break;
      }
      if (eq.equals("&")) {
          put(map, decode(n), "");
        continue;
      }
      v = st.nextToken();
      if (!v.equals("&")) {
        String dn = decode(n);
        String dv = decode(v);
        put(map, dn, dv);
      } else {
          put(map, decode(n), "");
        continue;
      }
      if (st.hasMoreTokens())
        st.nextToken(); // &
    }
    return map;
  }

  /**
   * URL decodes an input stream.
   * @return mapping of name value pairs
   */
  public static Map<String, String[]> urlDecode(InputStream is) throws IOException {
    byte[] bs = HttpUtil.read(is);
    return urlDecode(new String(bs, "ASCII"));
  }

  /**
   * URL decodes an input stream.
   * @return array of name value pairs
   */
  public static String[] urlDecodeToArray(InputStream is) throws IOException {
    byte[] bs = HttpUtil.read(is);
    return urlDecodeToArray(new String(bs, "ASCII"));
  }
 
  /**
   * Performs the same operation as {@link #urlDecode}, except the
   * returned data is in an ordered array.
   *
   * @see #urlEncode
   */
  public static String[] urlDecodeToArray(String urlEncodedData) {
    if (urlEncodedData == null)
      throw new IllegalArgumentException("Null urlEncodedData");
    urlEncodedData = urlEncodedData.trim();
    StringTokenizer st = new StringTokenizer(urlEncodedData, "&=", true);
    int amps = count(urlEncodedData, '&');
    String array[] = new String[(amps + 1) * 2];
    String n;
    String eq;
    String v;
    int i = -2;
    while (st.hasMoreTokens()) {
      i += 2;
      n = st.nextToken();
      array[i] = decode(n);
      if (!st.hasMoreTokens())
        break;
      eq = st.nextToken();
      if (!st.hasMoreTokens())
        break;
      if (eq.equals("&"))
        continue;
      v = st.nextToken();
      if (!v.equals("&"))
        array[i + 1] = decode(v);
      else
        continue;
      if (st.hasMoreTokens())
        st.nextToken(); // &
    }
    return array;
  }

  /**
   * Discards the contents of a <code>BufferedReader</code>.
   * If the reader is <code>null</code>, does nothing:
   * This is to facilitate the coding of cases when the data is of
   * no use to the client.
   */
  public static void discard(BufferedReader r)
    throws IOException
  {
    if (r == null)
      return;
    while (r.readLine() != null);
  }

  /**
   * Returns the contents of a <code>BufferedReader</code> as a String.
   * By default, each line is appended with
   * <code>System.getProperty("line.separator")</code>.
   * This may or may not be the original line termination character used.
   *
   * @throws IllegalArgumentException if the reader is null
   */
  public static String read(BufferedReader r)
    throws IOException
  {
    if (r == null)
      throw new IllegalArgumentException("null reader");
    String sep = System.getProperty("line.separator", "\n");
    StringBuilder sb = new StringBuilder(128);
    String line;
    while ((line = r.readLine()) != null) {
      sb.append(line).append(sep);
    }
    return sb.toString();
  }

}
TOP

Related Classes of net.noderunner.http.HttpUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.