Sunday, April 19, 2015

Bad URL day

When you create URL from string parameters it is crucial to encode those parameters properly (and then decode them). Imagine that yours REST service is consumed by client like this:
Response response = myServiceRestClient.getWebTarget().path("/queries/something").path(stringParameter)
        .queryParam("a", a)
        .queryParam("b", b)
        .request().get();
This is standard javax.ws.rs.core way to do things, but you can expect different behavior from path method. Let me introduce the wrongdoer:
/** * Create a new {@code WebTarget} instance by appending path to the URI of * the current target instance. * <p> * When constructing the final path, a '/' separator will be inserted between * the existing path and the supplied path if necessary. Existing '/' characters * are preserved thus a single value can represent multiple URI path segments. * </p> * <p> * A snapshot of the present configuration of the current (parent) target * instance is taken and is inherited by the newly constructed (child) target * instance. * </p> * * @param path the path, may contain URI template parameters. * @return a new target instance. * @throws NullPointerException if path is {@code null}. */public WebTarget path(String path);
Do you know how wrong will be your URL when you pass "a/b" as a path string? Of course you can easily pass / as part of URL in many ways. What can go wrong this time? Crucial is this sentence "single value can represent multiple URI path segments". As you can expect "/" will cause problems (I have found a lot of 404s in logs and this is why I called this Bad URL day).

How to repair this?
Of course it is very easy to replace / with something different in url and then back to / on the other side.

A. To guava or not to guava?
In guava we can find UrlEscapers. They are quite nice, but do we need guava in every project we do? What about other languages? In microservices environment every node should know about guava way of doing encoding to communicate with other parties.

B. Java API
Java API has two quite useful classes: URLEncoder and URLDecoder. They will allow us to do things we want to. I will present few tests:
@Unrolldef "should return space for encoded space [#character]"() {
    when:
    String result = RequestUtils.decodeParameterFromUrl(character)
    then:
    result == ' '    where:
    character << [' ', '%20', '+']
}
This means that we can write space(' ') in three different ways: ' ', '%20' and '+'.
Of course we should take a look at method definition:
import java.io.UnsupportedEncodingException;import java.net.URLDecoder;import java.nio.charset.StandardCharsets;
public class RequestUtils {

    public static String decodeParameterFromUrl(String parameterFromUrl) {
        try {
            return URLDecoder.decode(parameterFromUrl, StandardCharsets.UTF_8.name());        } catch (UnsupportedEncodingException e) {
            throw new IllegalArgumentException("JVM should implement encoding from StandardCharsets set");        }
    }
}
This is quite hard to test 100% of code above because UnsupportedEncodingException is thrown only on JVMs that do not implement StandardCharsets.UTF_8, but charsets from Standard Charsets should be implemented in every JVM. According to javadoc:
package java.nio.charset;
/** * Constant definitions for the standard {@link Charset Charsets}. These * charsets are guaranteed to be available on every implementation of the Java * platform. * * @see <a href="Charset#standard">Standard Charsets</a> * @since 1.7 */public final class StandardCharsets {
/..../
What about our "/":
@Unrolldef "should allow me to pass / encoded to [#character]"() {
    when:
    String result = RequestUtils.decodeParameterFromUrl(character)
    then:
    result == '/'    where:
    character << ['/', '%2F', '%2f']
}
And what out our famous ĄĘ characters?


Do you think I did forgot about something? Maybe there is another way of testing this part with UnsupportedEncodingException? Please leave suggestions in comments below - I will try every hint and describe my trial on this blog.

Spock even more advanced

On Fridays we have technological meetings in our company. Everyone can prepare presentation and "test" this presentation in friendly environment. This time Marcin Zajączkowski - from blog: solidsoft.wordpress.com was presenting his brand new "Advanced Spock". If you work with Spock on daily basis you should attend this presentation (even if you don't feel you need the »advanced« part).
During presentation I was curious what will happen in "where:" section when we pass two arrays with different length and how to cycle over values to test every combination of parameters. Marcin told me he wants to read answers on my blog. Challenge accepted:
First things first, but not necessarily in that order - I will start with combinations. How to test all combinations of parameters?
@Unrolldef "should return combinations pair (#a,#b)" () {
    when:
    println("a=${a} b=${b}")
    then:
    (...)
    where:
    [a,b] << [[1,2,3],[8,9]].combinations()
}
This test will end up with:
a=1 b=8
a=2 b=8
a=3 b=8
a=1 b=9
a=2 b=9
a=3 b=9
Now original problem - what will happen when you pass two arrays of different size?
@Unrolldef "should return combinations pair (#a,#b)" () {
    when:
    println("a = ${a} b=${b}")
    then:
    1==1    where:
    a << [1,2,3]
    b << [8,9]
}
a = 1 b=8
a = 2 b=9

org.spockframework.runtime.SpockExecutionException: Data provider for variable 'b' has fewer values than previous data provider(s)
at com.ofg.loans.domain.model.ledger.acl.RequestUtilsSpec.should return combinations pair (#a,#b)(RequestUtilsSpec.groovy:60)

This error doesn't leave us any hope - we can't pass two arrays of different length.

Thursday, April 9, 2015

JavaScript (-1)*0

JavaScript implemented in Chrome and in Nodejs should give the same results? Lets try multiply (-1) and 0:
Chrome:

Nodejs:

Wednesday, April 1, 2015

Lanterna Scroll

This was my second attempt to build something with lanterna library from:
https://code.google.com/p/lanterna/
For the very first time I did slightly different project. This time I focused on producing something completely useless like old school scroll but on terminal output and via telnet on the net.

You can find my scroll on github:
https://github.com/piotrpietrzak/lanternaScroll
It looks something like this:
this is scroll rendered on telnet localhost 1024 with atari characters set. This is very tricky way of rendering this text.
To understand how it works first you have to visit: http://www.atariarchives.org/c3ba/charset_full.php
Now you are ready to read code - especially this:
boolean bitForCharAtXY(String symbol, int x, int y) {
    return charset[symbol][y] >> x & 1}
<sad_jokes>
This time bit shift operator was used to ... well this time this was sad bit shift. Of course our & operator ... oh wait - this is and.
No operator overloading - is this groovy or what?
</sad_jokes>

This was so '80 to encode chars as bits and then shift them and apply bit mask to check if they are 1 or 0. But how to encode entire message?
def computeBit(String message, int i, int j) {
    return atariFont.bitForCharAtXY(message[(int) Math.floor(i / 8) % message.length()], 7 - i % 8, j)
}
So - we can provide message and then at any (i,j) position of bits we can find if we should flash or turn off our "pixel".

Lets go "forward" to the early internet. Telnet was the king. Hackers was too busy to hack other people and plain text passwords in public net was accepted by admins so they use telnet even to manage their servers.

Lanterna in new version allows us to provide telnet service. This is really easy - we have to setup server:
TelnetTerminalServer server = new TelnetTerminalServer(1024, Charset.forName("utf-8"));
Then we can accept connections:
TelnetTerminal telnetTerminal = server.acceptConnection();
Then we can start new thread per connection:
new ConnectionHandler().handleNewConnection(telnetTerminal, {
    String message, Exception e ->
        log.error(message,e)
})
In this thread we can run our scroll:
new AtariFontScroll().scroll(terminal, screen, writer);
or even modern ascii art version:
new SimpleScroll().scroll(terminal, screen, writer);

This project was just for fun, but I plan to use lanterna in gradle plugin to show progress bar via net - directly from your CI to your laptop. I hope security will not cool down my enthusiasm...
OK, with apache mina I can upgrade telnet to ssh.