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

com.goebl.david.WebbUtils Maven / Gradle / Ivy

package com.goebl.david;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;

/**
 * Static utility method and tools for HTTP traffic parsing and encoding.
 *
 * @author hgoebl
 */
public class WebbUtils {

    protected WebbUtils() {}

    /**
     * Convert a Map to a query string.
     * @param values the map with the values
     *               null will be encoded as empty string, all other objects are converted to
     *               String by calling its toString() method.
     * @return e.g. "key1=value&key2=&email=max%40example.com"
     */
    public static String queryString(Map values) {
        StringBuilder sbuf = new StringBuilder();
        String separator = "";

        for (Map.Entry entry : values.entrySet()) {
            Object entryValue = entry.getValue();
            if (entryValue instanceof Object[]) {
                for (Object value : (Object[]) entryValue) {
                    appendParam(sbuf, separator, entry.getKey(), value);
                    separator = "&";
                }
            } else if (entryValue instanceof Iterable) {
                for (Object multiValue : (Iterable) entryValue) {
                    appendParam(sbuf, separator, entry.getKey(), multiValue);
                    separator = "&";
                }
            } else {
                appendParam(sbuf, separator, entry.getKey(), entryValue);
                separator = "&";
            }
        }

        return sbuf.toString();
    }

    private static void appendParam(StringBuilder sbuf, String separator, String entryKey, Object value) {
        String sValue = value == null ? "" : String.valueOf(value);
        sbuf.append(separator);
        sbuf.append(urlEncode(entryKey));
        sbuf.append('=');
        sbuf.append(urlEncode(sValue));
    }

    /**
     * Convert a byte array to a JSONObject.
     * @param bytes a UTF-8 encoded string representing a JSON object.
     * @return the parsed object
     * @throws WebbException in case of error (usually a parsing error due to invalid JSON)
     */
    public static JSONObject toJsonObject(byte[] bytes) {
        String json;
        try {
            json = new String(bytes, Const.UTF8);
            return new JSONObject(json);
        } catch (UnsupportedEncodingException e) {
            throw new WebbException(e);
        } catch (JSONException e) {
            throw new WebbException("payload is not a valid JSON object", e);
        }
    }

    /**
     * Convert a byte array to a JSONArray.
     * @param bytes a UTF-8 encoded string representing a JSON array.
     * @return the parsed JSON array
     * @throws WebbException in case of error (usually a parsing error due to invalid JSON)
     */
    public static JSONArray toJsonArray(byte[] bytes) {
        String json;
        try {
            json = new String(bytes, Const.UTF8);
            return new JSONArray(json);
        } catch (UnsupportedEncodingException e) {
            throw new WebbException(e);
        } catch (JSONException e) {
            throw new WebbException("payload is not a valid JSON array", e);
        }
    }

    /**
     * Read an InputStream into byte[] until EOF.
     * 
* Does not close the InputStream! * * @param is the stream to read the bytes from * @return all read bytes as an array * @throws IOException when read or write operation fails */ public static byte[] readBytes(InputStream is) throws IOException { if (is == null) { return null; } ByteArrayOutputStream baos = new ByteArrayOutputStream(); copyStream(is, baos); return baos.toByteArray(); } /** * Copy complete content of InputStream to OutputStream until EOF. *
* Does not close the InputStream nor OutputStream! * * @param input the stream to read the bytes from * @param output the stream to write the bytes to * @throws IOException when read or write operation fails */ public static void copyStream(InputStream input, OutputStream output) throws IOException { byte[] buffer = new byte[1024]; int count; while ((count = input.read(buffer)) != -1) { output.write(buffer, 0, count); } } /** * Creates a new instance of a DateFormat for RFC1123 compliant dates. *
* Should be stored for later use but be aware that this DateFormat is not Thread-safe! *
* If you have to deal with dates in this format with JavaScript, it's easy, because the JavaScript * Date object has a constructor for strings formatted this way. * @return a new instance */ public static DateFormat getRfc1123DateFormat() { DateFormat format = new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH); format.setLenient(false); format.setTimeZone(TimeZone.getTimeZone("UTC")); return format; } static String urlEncode(String value) { try { return URLEncoder.encode(value, "UTF-8"); } catch (UnsupportedEncodingException e) { return value; } } static void addRequestProperties(HttpURLConnection connection, Map map) { if (map == null || map.isEmpty()) { return; } for (Map.Entry entry : map.entrySet()) { addRequestProperty(connection, entry.getKey(), entry.getValue()); } } static void addRequestProperty(HttpURLConnection connection, String name, Object value) { if (name == null || name.length() == 0 || value == null) { throw new IllegalArgumentException("name and value must not be empty"); } String valueAsString; if (value instanceof Date) { valueAsString = getRfc1123DateFormat().format((Date) value); } else if (value instanceof Calendar) { valueAsString = getRfc1123DateFormat().format(((Calendar) value).getTime()); } else { valueAsString = value.toString(); } connection.addRequestProperty(name, valueAsString); } static void ensureRequestProperty(HttpURLConnection connection, String name, Object value) { if (!connection.getRequestProperties().containsKey(name)) { addRequestProperty(connection, name, value); } } static byte[] getPayloadAsBytesAndSetContentType( HttpURLConnection connection, Request request, boolean compress, int jsonIndentFactor) throws JSONException, UnsupportedEncodingException { byte[] requestBody = null; String bodyStr = null; if (request.params != null) { WebbUtils.ensureRequestProperty(connection, Const.HDR_CONTENT_TYPE, Const.APP_FORM); bodyStr = WebbUtils.queryString(request.params); } else if (request.payload == null) { return null; } else if (request.payload instanceof JSONObject) { WebbUtils.ensureRequestProperty(connection, Const.HDR_CONTENT_TYPE, Const.APP_JSON); bodyStr = jsonIndentFactor >= 0 ? ((JSONObject) request.payload).toString(jsonIndentFactor) : request.payload.toString(); } else if (request.payload instanceof JSONArray) { WebbUtils.ensureRequestProperty(connection, Const.HDR_CONTENT_TYPE, Const.APP_JSON); bodyStr = jsonIndentFactor >= 0 ? ((JSONArray) request.payload).toString(jsonIndentFactor) : request.payload.toString(); } else if (request.payload instanceof byte[]) { WebbUtils.ensureRequestProperty(connection, Const.HDR_CONTENT_TYPE, Const.APP_BINARY); requestBody = (byte[]) request.payload; } else { WebbUtils.ensureRequestProperty(connection, Const.HDR_CONTENT_TYPE, Const.TEXT_PLAIN); bodyStr = request.payload.toString(); } if (bodyStr != null) { requestBody = bodyStr.getBytes(Const.UTF8); } if (requestBody == null) { throw new IllegalStateException(); } // only compress if the new body is smaller than uncompressed body if (compress && requestBody.length > Const.MIN_COMPRESSED_ADVANTAGE) { byte[] compressedBody = gzip(requestBody); if (requestBody.length - compressedBody.length > Const.MIN_COMPRESSED_ADVANTAGE) { requestBody = compressedBody; connection.setRequestProperty(Const.HDR_CONTENT_ENCODING, "gzip"); } } connection.setFixedLengthStreamingMode(requestBody.length); return requestBody; } static void setContentTypeAndLengthForStreaming( HttpURLConnection connection, Request request, boolean compress) { long length; if (request.payload instanceof File) { length = compress ? -1L : ((File) request.payload).length(); } else if (request.payload instanceof InputStream) { length = -1L; } else { throw new IllegalStateException(); } if (length > Integer.MAX_VALUE) { length = -1L; // use chunked streaming mode } WebbUtils.ensureRequestProperty(connection, Const.HDR_CONTENT_TYPE, Const.APP_BINARY); if (length < 0) { connection.setChunkedStreamingMode(-1); // use default chunk size if (compress) { connection.setRequestProperty(Const.HDR_CONTENT_ENCODING, "gzip"); } } else { connection.setFixedLengthStreamingMode((int) length); } } static byte[] gzip(byte[] input) { GZIPOutputStream gzipOS = null; try { ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream(); gzipOS = new GZIPOutputStream(byteArrayOS); gzipOS.write(input); gzipOS.flush(); gzipOS.close(); gzipOS = null; return byteArrayOS.toByteArray(); } catch (Exception e) { throw new WebbException(e); } finally { if (gzipOS != null) { try { gzipOS.close(); } catch (Exception ignored) {} } } } static InputStream wrapStream(String contentEncoding, InputStream inputStream) throws IOException { if (contentEncoding == null || "identity".equalsIgnoreCase(contentEncoding)) { return inputStream; } if ("gzip".equalsIgnoreCase(contentEncoding)) { return new GZIPInputStream(inputStream); } if ("deflate".equalsIgnoreCase(contentEncoding)) { return new InflaterInputStream(inputStream, new Inflater(false), 512); } throw new WebbException("unsupported content-encoding: " + contentEncoding); } static void parseResponseBody(Class clazz, Response response, InputStream responseBodyStream) throws UnsupportedEncodingException, IOException { if (responseBodyStream == null || clazz == Void.class) { return; } else if (clazz == InputStream.class) { response.setBody(responseBodyStream); return; } byte[] responseBody = WebbUtils.readBytes(responseBodyStream); // we are ignoring headers describing the content type of the response, instead // try to force the content based on the type the client is expecting it (clazz) if (clazz == String.class) { response.setBody(new String(responseBody, Const.UTF8)); } else if (clazz == Const.BYTE_ARRAY_CLASS) { response.setBody(responseBody); } else if (clazz == JSONObject.class) { response.setBody(WebbUtils.toJsonObject(responseBody)); } else if (clazz == JSONArray.class) { response.setBody(WebbUtils.toJsonArray(responseBody)); } } static void parseErrorResponse(Class clazz, Response response, InputStream responseBodyStream) throws UnsupportedEncodingException, IOException { if (responseBodyStream == null) { return; } else if (clazz == InputStream.class) { response.errorBody = responseBodyStream; return; } byte[] responseBody = WebbUtils.readBytes(responseBodyStream); String contentType = response.connection.getContentType(); if (contentType == null || contentType.startsWith(Const.APP_BINARY) || clazz == Const.BYTE_ARRAY_CLASS) { response.errorBody = responseBody; return; } if (contentType.startsWith(Const.APP_JSON) && clazz == JSONObject.class) { try { response.errorBody = WebbUtils.toJsonObject(responseBody); return; } catch (Exception ignored) { // ignored - was just a try! } } // fallback to String if bytes are valid UTF-8 characters ... try { response.errorBody = new String(responseBody, Const.UTF8); return; } catch (Exception ignored) { // ignored - was just a try! } // last fallback - return error object as byte[] response.errorBody = responseBody; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy