Java: HttpURLConnection and ignored setRequestProperty headers

Si usas HttpURLConnection es posible que haya ciertas cabeceras que no puedas usar mediante setRequestProperty (esto no siempre fué así, fue un cambio q añadieron en Java 1.6.0_22, sin aviso ni documentación oficial que yo haya encontrado), quizá por una cuestion de seguridad en Applets/Navegadores Java?.

Aquí está el origen del problema:

sun.net.www.protocol.http.HttpURLConnection
/*
 * Restrict setting of request headers through the public api
 * consistent with JavaScript XMLHttpRequest2 with a few
 * exceptions. Disallowed headers are silently ignored for
 * backwards compatibility reasons rather than throwing a
 * SecurityException. For example, some applets set the
 * Host header since old JREs did not implement HTTP 1.1.
 * Additionally, any header starting with Sec- is
 * disallowed.
 *
 * The following headers are allowed for historical reasons:
 *
 * Accept-Charset, Accept-Encoding, Cookie, Cookie2, Date,
 * Referer, TE, User-Agent, headers beginning with Proxy-.
 *
 * The following headers are allowed in a limited form:
 *
 * Connection: close
 *
 * See http://www.w3.org/TR/XMLHttpRequest2.
 */
private static final boolean allowRestrictedHeaders;
private static final String[] restrictedHeaders = {
    /* Restricted by XMLHttpRequest2 */
    //"Accept-Charset",
    //"Accept-Encoding",
    "Access-Control-Request-Headers",
    "Access-Control-Request-Method",
    "Connection", /* close is allowed */
    "Content-Length",
    //"Cookie",
    //"Cookie2",
    "Content-Transfer-Encoding",
    //"Date",
    //"Expect",
    "Host",
    "Keep-Alive",
    "Origin",
    // "Referer",
    // "TE",
    "Trailer",
    "Transfer-Encoding",
    "Upgrade",
    //"User-Agent",
    "Via"
};
// ... cut ...

static {
  // ... cut ...
  allowRestrictedHeaders = ((Boolean)java.security.AccessController.doPrivileged(
    new sun.security.action.GetBooleanAction(
      "sun.net.http.allowRestrictedHeaders"))).booleanValue();
  // ... cut ...
}

Así, por ejemplo, si quieres hacer una peticion HTTP PUT/POST con el body comprimido usando un HttpURLConnection + GZIPOutputStream con su correspondiente setRequestProperty(“Content-Transfer-Encoding”, “gzip”); cuando llegue al servidor no será capaz de entender los datos.

El workarround, definir la System Property:

-Dsun.net.http.allowRestrictedHeaders=true

Habría sido buena idea añadir un comentario en el metodo “setRequestProperty”, verdad?

Referencias:
Java BUG#6996110: Won’t Fix
Source of HttpURLConnection

Java: Country names and iso3166 for i18n

Para sacar la lista de Paises en un Idioma concreto con su codigo ISO:

import java.util.Locale;

public class Countries {
  public static void main(final String[] args) {
    // ENGLISH, SPANISH, FRENCH, ITALIAN, GERMAN
    final Locale lang = Locale.ENGLISH;
    final String[] countries = Locale.getISOCountries();
    for (final String countryCode : countries) {
      final Locale locale = new Locale("", countryCode);
      System.out.println(locale.getCountry() + "=" + 
                         locale.getDisplayCountry(lang));
    }
  }
}

Esto devuelve el codigo de pais en formato ISO-3166-1 (2 letras) y su texto humano, tal que así:

ES=Spain
FR=France
...etc...

Para recuperar el Texto de un pais partiendo del codigo ISO (2 letras) sería:

public static String getCountry(final String isoCode, final Locale lang) {
  return new Locale("", isoCode).getDisplayCountry(lang);
}
getCountry("ES", Locale.GERMAN) => "Spanien"

Referencia: Locale

Linux: Performance Analysis and Tools

Java: Speed Limit on InputStream/OutputStream

Si necesitas reducir la velocidad de un InputStream/OutputStream de un modo sencillo, esta es la solución!

public class ThrottleStream {
	protected final int speedLimitBytesSecond;
	protected int currentTransferredBytes = 0;
	protected long lastTimeStampSeconds = 0;

	protected ThrottleStream(final int maxSpeedInKilobytesPerSecond) {
		this.speedLimitBytesSecond = maxSpeedInKilobytesPerSecond * 1024;
	}

	protected void addAndWait(final int transferred) throws IOException {
		long now = System.currentTimeMillis() / 1000;
		if (now != lastTimeStampSeconds) {
			currentTransferredBytes = 0;
			lastTimeStampSeconds = now;
		}
		currentTransferredBytes += transferred;
		if (currentTransferredBytes >= speedLimitBytesSecond) {
			try {
				while (true) {
					Thread.sleep(10);
					now = System.currentTimeMillis() / 1000;
					if (now != lastTimeStampSeconds) {
						currentTransferredBytes = 0;
						lastTimeStampSeconds = now;
						break;
					}
				}
			} catch (InterruptedException e) {
				Thread.currentThread().interrupt();
				throw new IOException(e);
			}
		}
	}

	/**
	 * Speed Limit de un InputStream
	 */
	public static class ThrottleInputStream extends FilterInputStream {
		final ThrottleStream limit;

		public ThrottleInputStream(final InputStream in, 
				final int maxSpeedInKilobytesPerSecond) {
			super(in);
			limit = new ThrottleStream(maxSpeedInKilobytesPerSecond);
		}

		@Override
		public int read() throws IOException {
			final int read = super.read();
			limit.addAndWait(read);
			return read;
		}

		@Override
		public int read(final byte[] b) throws IOException {
			final int read = super.read(b);
			limit.addAndWait(read);
			return read;
		}

		@Override
		public int read(final byte[] b, final int off, final int len) 
				throws IOException {
			final int read = super.read(b, off, len);
			limit.addAndWait(read);
			return read;
		}
	}

	/**
	 * Speed Limit de un OutputStream
	 */
	public static class ThrottleOutputStream extends FilterOutputStream {
		final ThrottleStream limit;

		public ThrottleOutputStream(final OutputStream out, 
				final int maxSpeedInKilobytesPerSecond) {
			super(out);
			limit = new ThrottleStream(maxSpeedInKilobytesPerSecond);
		}

		@Override
		public void write(final int b) throws IOException {
			super.write(b);
			limit.addAndWait(1);
		}

		@Override
		public void write(final byte[] b) throws IOException {
			super.write(b);
			limit.addAndWait(b.length);
		}

		@Override
		public void write(final byte[] b, final int off, final int len) 
				throws IOException {
			super.write(b, off, len);
			limit.addAndWait(len);
		}
	}
}

Source code: ThrottleStream.java

Linux: Broken sar on vm clone

Si clonas una maquina en caliente lo más posible es que el “sar” (sysstat) deje de funcionar cuando le pides el historico:

# sar
Requested activities not available in file /var/log/sa/sa12

Para resetearlo (Open-SuSE):

# rm -f /var/log/sa/sa*; /etc/init.d/boot.sysstat start
Running sadc [done]

# sar
Linux x.x.xx.xx-x.x-pae (box1)  02/12/14        _i686_

01:39:29 PM       LINUX RESTART

01:45:02 PM     CPU     %user     %nice   %system   %iowait    %steal     %idle
01:55:01 PM     all      4.59      0.00      0.40      0.00      0.00     95.01
02:05:01 PM     all      7.11      0.00      0.67      0.00      0.00     92.22
02:15:01 PM     all     11.34      0.00      1.12      0.00      0.00     87.53
02:25:01 PM     all     14.48      0.00      1.44      0.00      0.00     84.08
02:35:01 PM     all      8.17      0.00      0.80      0.00      0.00     91.03
02:45:01 PM     all      4.52      0.00      0.41      0.00      0.00     95.06
02:55:01 PM     all      3.31      0.00      0.32      0.00      0.00     96.37
03:05:01 PM     all     24.12      0.00      2.98      0.00      0.00     72.89
03:15:01 PM     all      6.82      0.00      0.62      0.00      0.00     92.56
03:25:01 PM     all      5.43      0.00      0.48      0.00      0.00     94.09
Average:        all      8.98      0.00      0.92      0.00      0.00     90.10

Linux: Setting Hostname from DHCP-Server in Ubuntu

Si quieres que tu Linux coja el nombre de Host desde DHCP (probado en Ubuntu 12.04 -Precise-)

#!/bin/sh
# Filename: /etc/dhcp/dhclient-exit-hooks.d/hostname
# Purpose:  Set the hostname of the system provided by DHCP (option 12).
#
case "$reason" in
  BOUND|RENEW|REBIND|REBOOT)
    if [ "$new_host_name" != "" ]; then
      echo $new_host_name > /etc/hostname;
      hostname $new_host_name;
    fi
    echo dhclient-exit-hooks.d/hostname: Dynamic Hostname = $new_host_name;
  ;;
esac

El script aparentemente tiene acceso a estas variables de entorno (la documentacion no las explica todas):

interface=eth0
reason=RENEW
# ---
new_expiry=1391268994
new_dhcp_lease_time=300
new_dhcp_message=ACME
new_dhcp_message_type=5
new_dhcp_server_identifier=192.168.1.1
new_network_number=192.168.1.0
new_subnet_mask=255.255.255.0
new_broadcast_address=192.168.1.255
new_ip_address=192.168.1.101
new_routers=192.168.1.1
new_host_name=test1
new_domain_name=acme.com
new_domain_name_servers=192.168.1.1 192.168.1.2
# ---
old_expiry=1391268724
old_dhcp_lease_time=300
old_dhcp_message=ACME
old_dhcp_message_type=5
old_dhcp_server_identifier=192.168.1.1
old_network_number=192.168.1.0
old_subnet_mask=255.255.255.0
old_broadcast_address=192.168.1.255
old_ip_address=192.168.1.101
old_routers=192.168.1.1
old_host_name=test1
old_domain_name=acme.com
old_domain_name_servers=192.168.1.1 192.168.1.2

Referencias:
man 8 dhclient-script
man 5 dhclient.conf

Java: Universal Placeholder for InputStreams

Los ficheros XML tienen una sintaxis muy completa y los ficheros de Properties un formato sencillo de manipular:

# placeholders.properties

user = root
password = secret

Y como queremos ambas cosas, los “placeholders” nos permiten hacer el mix de lo mejor de un lenguaje de marcado como XML con la sencillez de un fichero de propiedades, ejemplo:

# config.xml

<config>
  <init-param>
    <param-name>user</param-name>
    <param-value>${user}</param-value>
  </init-param>
  <init-param>
    <param-name>password</param-name>
    <param-value>${password}</param-value>
  </init-param>
</config>

La idea detras de esto es incluir el valor de “user” y “password” (extraidos del fichero de propiedades) dentro del XML. Y aqui una implementación muy sencilla y generica que permite usar placeholders en cualquier InputStream, independiente del formato:

Leer más de esta entrada

Java: ResponseHeaders Servlet Filter

Un simple filtro de Tomcat para incluir Headers en las cabeceras de respuesta HTTP (util cuando tienes un Tomcat standalone y quieres incluir un Cache-Control o similar):

import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

public final class ResponseHeadersFilter implements Filter {
	private Map<String, String> headers = null;

	@Override
	public void init(final FilterConfig filterConfig) throws ServletException {
		final LinkedHashMap<String, String> headers = 
				new LinkedHashMap<String, String>();
		final Enumeration<String> e = filterConfig.getInitParameterNames();
		while (e.hasMoreElements()) {
			final String name = e.nextElement();
			final String value = filterConfig.getInitParameter(name);
			headers.put(name, value);
		}
		this.headers = Collections.unmodifiableMap(headers);
	}

	@Override
	public void doFilter(final ServletRequest request, 
			final ServletResponse response, final FilterChain chain)
			throws IOException, ServletException {
		if (response instanceof HttpServletResponse) {
			final HttpServletResponse res = ((HttpServletResponse) response);
			for (final Entry<String, String> e : headers.entrySet()) {
				res.setHeader(e.getKey(), e.getValue());
			}
		}
		chain.doFilter(request, response);
	}

	@Override
	public void destroy() {
	}
}

Y la configuracion del web.xml:

    <filter>
        <filter-name>ResponseHeadersFilter</filter-name>
        <filter-class>com.package.ResponseHeadersFilter</filter-class>
        <init-param>
           <param-name>Cache-Control</param-name>
           <param-value>must-revalidate, max-age=0, no-cache, no-store</param-value>
        </init-param>
        <init-param>
           <param-name>Expires</param-name>
           <param-value>0</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>ResponseHeadersFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

La salida sera algo asi:

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Cache-Control: must-revalidate, max-age=0, no-cache, no-store
Expires: 0
Content-Length: 3
Date: Sat, 04 Jan 2014 05:40:23 GMT

...

Source code: ResponseHeadersFilter.java

SCM-VCS: Git & Subversion

Despues de mirar unos cuantos SCM/VCS para Git/Subversion que corrieran sobre Java (en un contenedor de Servlets, como Tomcat o Jetty -por cuestión de seguridad, portabilidad y demás-), aquí están los ganadores:

  1. SCM-Manager (BSD-License) > Ganador sin duda: tiene soporte by default para Git y Subversion, usa las librerías JGit (Eclipse) y SVNKit, ambas hechas tambien en Java, un command-line para automatizar tareas y bastantes plug-ins (para integración con Jenkins entre otros), muy funcional.
  2. GitBlit (Apache License 2.0) > Otra buena opción, aunque solo soporta Git (usando JGit), las gráficas parecen mejores.
  3. Gerrit (Apache License 2.0) > Descartado por varios motivos: la instalación es más compleja, el alta de usuarios tb es más complejo y la interface de usuario es bastante limitada (y un poco tortuosa).

Java: Collections

Algunas librerias muy buenas para la gestion de Colecciones en Java { Maps, Queues, Lists, Sorted, Native Types, Iterables with predicate (callbacks), Structures, Unions, Hashes, BloomFilters, IO-Utils… y más }. Algunas centradas en la optimización del uso de memoria, otras en velocidad y otras añadiendo funcionalidad a las java.util standard, todas ellas Open Source:

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

%d personas les gusta esto: