Repository: yegor256/takes
Issue: RqHref.Base reads first line of header without checking format

`org.takes.rq.RqHref.Base` just reads first header and takes second element after space splitting :
`this.head().iterator().next().split(" ", 3)[1]`
It should check if the header format satisfies HTTP specifications.

<hr id='w'>
- `445-5323c196`/#473 (by Vladimir Maximenko)


Changed files:
src/main/java/org/takes/rq/RqHref.java, src/test/java/org/takes/rq/RqHrefTest.java

diff --git a/src/main/java/org/takes/rq/RqHref.java b/src/main/java/org/takes/rq/RqHref.java
index 6a84fa107..f52b61321 100644
--- a/src/main/java/org/takes/rq/RqHref.java
+++ b/src/main/java/org/takes/rq/RqHref.java
@@ -27,6 +27,8 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.HttpURLConnection;
 import java.util.Iterator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import lombok.EqualsAndHashCode;
 import org.takes.HttpException;
 import org.takes.Request;
@@ -60,6 +62,16 @@ public interface RqHref extends Request {
      */
     @EqualsAndHashCode(callSuper = true)
     final class Base extends RqWrap implements RqHref {
+
+        /**
+         * HTTP Request-Line pattern.
+         * @see <a href="http://www
+         * .w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1">RFC 2616</a>
+         */
+        private static final Pattern REQUEST_PATTERN = Pattern.compile(
+            "([!-~]+) ([^ ]+)( [^ ]+)?"
+        );
+
         /**
          * Ctor.
          * @param req Original request
@@ -67,16 +79,36 @@ public interface RqHref extends Request {
         public Base(final Request req) {
             super(req);
         }
+
+        // @todo #445:30min/DEV RqMethod already validates Request-Line and
+        //  extracts HTTP Method from it. We should extract all important
+        //  information from Request-Line (HTTP method, URI and HTTP version)
+        //  in one place to enforce DRY principle.
         @Override
         public Href href() throws IOException {
+            if (!this.head().iterator().hasNext()) {
+                throw new HttpException(
+                    HttpURLConnection.HTTP_BAD_REQUEST,
+                    "HTTP Request should have Request-Line"
+                );
+            }
+            final String line = this.head().iterator().next();
+            final Matcher matcher =
+                REQUEST_PATTERN.matcher(line);
+            if (!matcher.matches()) {
+                throw new HttpException(
+                    HttpURLConnection.HTTP_BAD_REQUEST,
+                    String.format("Illegal Request-Line: %s", line)
+                );
+            }
+            final String uri = matcher.group(2);
             return new Href(
                 String.format(
                     "http://%s%s",
                     new RqHeaders.Smart(
                         new RqHeaders.Base(this)
                     ).single("host"),
-                    // @checkstyle MagicNumber (1 line)
-                    this.head().iterator().next().split(" ", 3)[1]
+                    uri
                 )
             );
         }
diff --git a/src/test/java/org/takes/rq/RqHrefTest.java b/src/test/java/org/takes/rq/RqHrefTest.java
index 52c3787d2..ff2ab8b0b 100644
--- a/src/test/java/org/takes/rq/RqHrefTest.java
+++ b/src/test/java/org/takes/rq/RqHrefTest.java
@@ -25,9 +25,11 @@ package org.takes.rq;
 
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.Collections;
 import org.hamcrest.MatcherAssert;
 import org.hamcrest.Matchers;
 import org.junit.Test;
+import org.takes.HttpException;
 
 /**
  * Test case for {@link RqHref.Base}.
@@ -58,6 +60,36 @@ public final class RqHrefTest {
         );
     }
 
+    /**
+     * RqHref.Base should throw {@link HttpException} when parsing
+     * Request without Request-Line.
+     * @throws IOException If some problem inside
+     */
+    @Test(expected = HttpException.class)
+    public void failsOnAbsentRequestLine() throws IOException {
+        new RqHref.Base(
+            new RqSimple(Collections.<String>emptyList(), null)
+        ).href();
+    }
+
+    /**
+     * RqHref.Base should throw {@link HttpException} when parsing
+     * Request with illegal Request-Line.
+     * @throws IOException If some problem inside
+     */
+    @Test(expected = HttpException.class)
+    public void failsOnIllegalRequestLine() throws IOException {
+        new RqHref.Base(
+            new RqFake(
+                Arrays.asList(
+                    "GIVE/contacts",
+                    "Host: 2.example.com"
+                ),
+                ""
+            )
+        ).href();
+    }
+
     /**
      * RqHref.Base can extract params.
      * @throws IOException If some problem inside