diff --git a/.rultor.yml b/.rultor.yml
index 5b8d81de8..e0aadd56a 100644
--- a/.rultor.yml
+++ b/.rultor.yml
@@ -34,7 +34,6 @@ merge:
mvn clean
pdd --source=$(pwd) --verbose --file=/dev/null
commanders:
- - alevohin
- carlosmiranda
- darkled
- ggajos
@@ -66,4 +65,4 @@ release:
mvn clean deploy -Ptakes -Psonatype --errors --settings ../settings.xml
mvn clean site-deploy -Ptakes -Psite --errors --settings ../settings.xml
commanders:
- - yegor256
+ - yegor256
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index ee0cf21ce..c84939b7c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,6 +5,10 @@ cache:
- $HOME/.m2
script:
- mvn clean install -Pqulice --errors --batch-mode
+env:
+ global:
+ - MAVEN_OPTS="-Xmx256m -Dfile.encoding=UTF-8"
+ - JAVA_OPTS="-Xmx256m -Dfile.encoding=UTF-8"
jdk:
- oraclejdk8
- oraclejdk7
diff --git a/src/main/java/org/takes/facets/auth/codecs/CcBase64.java b/src/main/java/org/takes/facets/auth/codecs/CcBase64.java
new file mode 100644
index 000000000..780463480
--- /dev/null
+++ b/src/main/java/org/takes/facets/auth/codecs/CcBase64.java
@@ -0,0 +1,73 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Yegor Bugayenko
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.takes.facets.auth.codecs;
+
+import java.io.IOException;
+import lombok.EqualsAndHashCode;
+import org.takes.facets.auth.Identity;
+
+/**
+ * Base64 codec.
+ *
+ *
The class is immutable and thread-safe.
+ *
+ * @author Igor Khvostenkov (ikhvostenkov@gmail.com)
+ * @version $Id$
+ * @since 0.13
+ */
+@EqualsAndHashCode
+public final class CcBase64 implements Codec {
+ /**
+ * Original codec.
+ */
+ private final transient Codec origin;
+ /**
+ * Ctor.
+ * @param codec Original codec
+ */
+ public CcBase64(final Codec codec) {
+ this.origin = codec;
+ }
+
+ //@todo #19:30min to implement own simple Base64 encode algorithm
+ // without using 3d-party Base64 encode libraries. Tests for this
+ // method have been already created, do not forget to remove Ignore
+ // annotation on it.
+ @Override
+ public byte[] encode(final Identity identity) throws IOException {
+ assert this.origin != null;
+ throw new UnsupportedOperationException("#encode()");
+ }
+
+ //@todo #19:30min to implement own simple Base64 decode algorithm
+ // without using 3d-party Base64 decode libraries. Tests for this
+ // method have been already created, do not forget to remove Ignore
+ // annotation on it.
+ @Override
+ public Identity decode(final byte[] bytes) throws IOException {
+ assert this.origin != null;
+ throw new UnsupportedOperationException("#decode()");
+ }
+
+}
diff --git a/src/main/java/org/takes/facets/auth/social/PsGithub.java b/src/main/java/org/takes/facets/auth/social/PsGithub.java
index 0530968cc..9ac012abf 100644
--- a/src/main/java/org/takes/facets/auth/social/PsGithub.java
+++ b/src/main/java/org/takes/facets/auth/social/PsGithub.java
@@ -147,7 +147,7 @@ private static Identity parse(final JsonObject json) {
final ConcurrentMap props =
new ConcurrentHashMap(json.size());
// @checkstyle MultipleStringLiteralsCheck (1 line)
- props.put("login", json.getString("login", "?"));
+ props.put("login", json.getString("login", "unknown"));
props.put("avatar", json.getString("avatar_url", "#"));
return new Identity.Simple(
String.format("urn:github:%d", json.getInt("id")), props
diff --git a/src/main/java/org/takes/facets/auth/social/PsGoogle.java b/src/main/java/org/takes/facets/auth/social/PsGoogle.java
index 34d441539..c89e487d3 100644
--- a/src/main/java/org/takes/facets/auth/social/PsGoogle.java
+++ b/src/main/java/org/takes/facets/auth/social/PsGoogle.java
@@ -26,11 +26,12 @@
import com.jcabi.http.request.JdkRequest;
import com.jcabi.http.response.JsonResponse;
import com.jcabi.http.response.RestResponse;
-import com.jcabi.http.wire.VerboseWire;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Collections;
import java.util.Iterator;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import javax.json.JsonObject;
import lombok.EqualsAndHashCode;
import org.takes.Request;
@@ -50,7 +51,7 @@
* @since 0.9
* @checkstyle MultipleStringLiteralsCheck (500 lines)
*/
-@EqualsAndHashCode(of = { "app", "key" })
+@EqualsAndHashCode(of = { "app", "key", "redir" })
public final class PsGoogle implements Pass {
/**
@@ -63,14 +64,22 @@ public final class PsGoogle implements Pass {
*/
private final transient String key;
+ /**
+ * Redirect URI.
+ */
+ private final transient String redir;
+
/**
* Ctor.
* @param gapp Google app
* @param gkey Google key
+ * @param uri Redirect URI (exactly as registered in Google console)
*/
- public PsGoogle(final String gapp, final String gkey) {
+ public PsGoogle(final String gapp, final String gkey,
+ final String uri) {
this.app = gapp;
this.key = gkey;
+ this.redir = uri;
}
@Override
@@ -84,7 +93,7 @@ public Iterator enter(final Request request)
);
}
return Collections.singleton(
- PsGoogle.fetch(this.token(href, code.next()))
+ PsGoogle.fetch(this.token(code.next()))
).iterator();
}
@@ -115,22 +124,19 @@ private static Identity fetch(final String token) throws IOException {
/**
* Retrieve Google access token.
- * @param home Home of this page
* @param code Google "authorization code"
* @return The token
* @throws IOException If failed
*/
- private String token(final Href home, final String code)
- throws IOException {
+ private String token(final String code) throws IOException {
return new JdkRequest("https://accounts.google.com/o/oauth2/token")
.body()
.formParam("client_id", this.app)
- .formParam("redirect_uri", home)
+ .formParam("redirect_uri", this.redir)
.formParam("client_secret", this.key)
.formParam("grant_type", "authorization_code")
.formParam("code", code)
.back()
- .through(VerboseWire.class)
.header("Content-Type", "application/x-www-form-urlencoded")
.method(com.jcabi.http.Request.POST)
.fetch().as(RestResponse.class)
@@ -146,9 +152,13 @@ private String token(final Href home, final String code)
* @return Identity found
*/
private static Identity parse(final JsonObject json) {
+ final ConcurrentMap props =
+ new ConcurrentHashMap(json.size());
+ // @checkstyle MultipleStringLiteralsCheck (1 line)
+ props.put("picture", json.getString("picture", "#"));
+ props.put("name", json.getString("name", "unknown"));
return new Identity.Simple(
- String.format("urn:google:%s", json.getString("id")),
- Collections.emptyMap()
+ String.format("urn:google:%s", json.getString("id")), props
);
}
diff --git a/src/main/java/org/takes/facets/auth/social/XeGoogleLink.java b/src/main/java/org/takes/facets/auth/social/XeGoogleLink.java
index ca82c2747..70f3a9c59 100644
--- a/src/main/java/org/takes/facets/auth/social/XeGoogleLink.java
+++ b/src/main/java/org/takes/facets/auth/social/XeGoogleLink.java
@@ -51,10 +51,20 @@ public final class XeGoogleLink extends XeWrap {
*/
public XeGoogleLink(final Request req, final CharSequence app)
throws IOException {
- this(
- req, app, "takes:google",
- new RqHref.Smart(new RqHref.Base(req)).home()
- );
+ this(req, app, new RqHref.Smart(new RqHref.Base(req)).home());
+ }
+
+ /**
+ * Ctor.
+ * @param req Request
+ * @param app Google application ID
+ * @param redir Redirect URI
+ * @throws IOException If fails
+ * @since 0.14
+ */
+ public XeGoogleLink(final Request req, final CharSequence app,
+ final CharSequence redir) throws IOException {
+ this(req, app, "takes:google", redir);
}
/**
@@ -64,6 +74,7 @@ public XeGoogleLink(final Request req, final CharSequence app)
* @param rel Related
* @param redir Redirect URI
* @throws IOException If fails
+ * @since 0.14
* @checkstyle ParameterNumberCheck (4 lines)
*/
public XeGoogleLink(final Request req, final CharSequence app,
diff --git a/src/main/java/org/takes/facets/fork/FkMethods.java b/src/main/java/org/takes/facets/fork/FkMethods.java
index b7a3a7129..2b16321fa 100644
--- a/src/main/java/org/takes/facets/fork/FkMethods.java
+++ b/src/main/java/org/takes/facets/fork/FkMethods.java
@@ -87,7 +87,7 @@ public FkMethods(final Collection mtds, final Take tke) {
@Override
public Iterator route(final Request req) throws IOException {
- final String mtd = new RqMethod(req).method();
+ final String mtd = new RqMethod.Base(req).method();
final Collection list = new ArrayList(1);
if (this.methods.contains(mtd)) {
list.add(this.take.act(req));
diff --git a/src/main/java/org/takes/facets/fork/TkProduces.java b/src/main/java/org/takes/facets/fork/TkProduces.java
new file mode 100644
index 000000000..be9cb1f51
--- /dev/null
+++ b/src/main/java/org/takes/facets/fork/TkProduces.java
@@ -0,0 +1,64 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Yegor Bugayenko
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.takes.facets.fork;
+
+import java.io.IOException;
+import lombok.EqualsAndHashCode;
+import org.takes.Request;
+import org.takes.Response;
+import org.takes.Take;
+import org.takes.tk.TkWrap;
+
+/**
+ * Take that acts on request with specified "Accept" HTTP headers only.
+ *
+ * The class is immutable and thread-safe.
+ *
+ * @author Eugene Kondrashev (eugene.kondrashev@gmail.com)
+ * @version $Id$
+ * @since 0.14
+ */
+@EqualsAndHashCode(callSuper = true)
+public final class TkProduces extends TkWrap {
+
+ /**
+ * Ctor.
+ * @param take Original take
+ * @param types Accept types
+ */
+ public TkProduces(final Take take, final String types) {
+ super(
+ new Take() {
+ @Override
+ public Response act(final Request req) throws IOException {
+ return new RsFork(
+ req,
+ new FkTypes(types, take.act(req))
+ );
+ }
+ }
+ );
+ }
+
+}
diff --git a/src/main/java/org/takes/facets/forward/RsForward.java b/src/main/java/org/takes/facets/forward/RsForward.java
index 6f28b02df..20879a572 100644
--- a/src/main/java/org/takes/facets/forward/RsForward.java
+++ b/src/main/java/org/takes/facets/forward/RsForward.java
@@ -33,6 +33,7 @@
import org.takes.rs.RsEmpty;
import org.takes.rs.RsWithHeader;
import org.takes.rs.RsWithStatus;
+import org.takes.rs.RsWithoutHeader;
/**
* Forwarding response.
@@ -71,6 +72,15 @@ public RsForward(final Response res) {
this(res, "/");
}
+ /**
+ * Ctor.
+ * @param res Original response
+ * @since 0.14
+ */
+ public RsForward(final RsForward res) {
+ this(res.origin);
+ }
+
/**
* Ctor.
* @param res Original response
@@ -80,6 +90,16 @@ public RsForward(final Response res, final CharSequence loc) {
this(res, HttpURLConnection.HTTP_SEE_OTHER, loc);
}
+ /**
+ * Ctor.
+ * @param res Original response
+ * @param loc Location
+ * @since 0.14
+ */
+ public RsForward(final RsForward res, final CharSequence loc) {
+ this(res.origin, loc);
+ }
+
/**
* Ctor.
* @param loc Location
@@ -97,6 +117,18 @@ public RsForward(final int code, final CharSequence loc) {
this(new RsEmpty(), code, loc);
}
+ /**
+ * Ctor.
+ * @param res Original
+ * @param code HTTP status code
+ * @param loc Location
+ * @since 0.14
+ */
+ public RsForward(final RsForward res, final int code,
+ final CharSequence loc) {
+ this(res.origin, code, loc);
+ }
+
/**
* Ctor.
* @param res Original
@@ -107,7 +139,10 @@ public RsForward(final Response res, final int code,
final CharSequence loc) {
super(code, res.toString());
this.origin = new RsWithHeader(
- new RsWithStatus(res, code),
+ new RsWithoutHeader(
+ new RsWithStatus(res, code),
+ "Location"
+ ),
new Sprintf("Location: %s", loc)
);
}
diff --git a/src/main/java/org/takes/facets/forward/package-info.java b/src/main/java/org/takes/facets/forward/package-info.java
index 0cb4d71b8..23f979499 100644
--- a/src/main/java/org/takes/facets/forward/package-info.java
+++ b/src/main/java/org/takes/facets/forward/package-info.java
@@ -37,7 +37,7 @@
* @Override
* public Response act(final Request req) {
* final InputStream content =
- * new RqMultipart(req).part("file").body();
+ * new RqMultipart.Base(req).part("file").body();
* // save content to whenever you want
* return new RsForward(new RqHref(req).href());
* }
diff --git a/src/main/java/org/takes/misc/Href.java b/src/main/java/org/takes/misc/Href.java
index 7e9acf5b3..2e265c739 100644
--- a/src/main/java/org/takes/misc/Href.java
+++ b/src/main/java/org/takes/misc/Href.java
@@ -123,10 +123,7 @@ public CharSequence subSequence(final int start, final int end) {
@Override
public String toString() {
- final StringBuilder text = new StringBuilder(this.uri.toString());
- if (this.uri.getPath().isEmpty()) {
- text.append('/');
- }
+ final StringBuilder text = new StringBuilder(this.bare());
if (!this.params.isEmpty()) {
boolean first = true;
for (final Map.Entry> ent
@@ -157,6 +154,19 @@ public String path() {
return this.uri.getPath();
}
+ /**
+ * Get URI without params.
+ * @return Bare URI
+ * @since 0.14
+ */
+ public String bare() {
+ final StringBuilder text = new StringBuilder(this.uri.toString());
+ if (this.uri.getPath().isEmpty()) {
+ text.append('/');
+ }
+ return text.toString();
+ }
+
/**
* Get query param.
* @param key Param name
diff --git a/src/main/java/org/takes/misc/Transformer.java b/src/main/java/org/takes/misc/Transformer.java
index 8f4ad5472..a24922ecb 100644
--- a/src/main/java/org/takes/misc/Transformer.java
+++ b/src/main/java/org/takes/misc/Transformer.java
@@ -28,18 +28,18 @@
import java.util.List;
/**
- * Transform elements in an iterable into others.
+ * Transform elements in an iterable (in type T) into others (in type K).
*
* @author Jason Wong (super132j@yahoo.com)
* @version $Id$
* @since 0.13.8
*/
-public class Transformer implements Iterable {
+public class Transformer implements Iterable {
/**
* Internal storage.
*/
- private final transient List storage = new LinkedList();
+ private final transient List storage = new LinkedList();
/**
* Transform elements in the supplied iterable by the action supplied.
@@ -48,7 +48,7 @@ public class Transformer implements Iterable {
* @param action The actual transformation implementation
*/
public Transformer(final Iterable list,
- final Transformer.Action action) {
+ final Transformer.Action action) {
final Iterator itr = list.iterator();
while (itr.hasNext()) {
this.storage.add(action.transform(itr.next()));
@@ -56,18 +56,18 @@ public Transformer(final Iterable list,
}
@Override
- public final Iterator iterator() {
+ public final Iterator iterator() {
return this.storage.iterator();
}
- public interface Action {
+ public interface Action {
/**
- * The transform action of the element.
+ * The transform action of the element of type T to K.
*
* @param element Element of the iterable
* @return Transformed element
*/
- T transform(T element);
+ K transform(T element);
}
/**
@@ -79,12 +79,29 @@ public interface Action {
*
*/
public static final class Trim implements
- Transformer.Action {
+ Transformer.Action {
@Override
public String transform(final String element) {
return element.trim();
}
+ }
+
+ /**
+ * Convert CharSequence into String
+ *
+ * @author Jason Wong (super132j@yahoo.com)
+ * @version $Id$
+ * @since 0.13.8
+ *
+ */
+ public static final class ToString implements
+ Transformer.Action {
-}
+ @Override
+ public String transform(CharSequence element) {
+ return element.toString();
+ }
+
+ }
}
diff --git a/src/main/java/org/takes/rq/RqForm.java b/src/main/java/org/takes/rq/RqForm.java
index c3ee25c8c..b4ae011e3 100644
--- a/src/main/java/org/takes/rq/RqForm.java
+++ b/src/main/java/org/takes/rq/RqForm.java
@@ -25,16 +25,20 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.Collections;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import lombok.EqualsAndHashCode;
+import org.takes.HttpException;
import org.takes.Request;
import org.takes.misc.Sprintf;
import org.takes.misc.VerboseIterable;
@@ -44,7 +48,7 @@
* {@code application/x-www-form-urlencoded} format (RFC 1738).
*
* For {@code multipart/form-data} format use
- * {@link org.takes.rq.RqMultipart}.
+ * {@link org.takes.rq.RqMultipart.Base}.
*
*
It is highly recommended to use {@link org.takes.rq.RqGreedy}
* decorator before passing request to this class.
@@ -57,94 +61,177 @@
* @link Forms in HTML
* @see org.takes.rq.RqGreedy
*/
-@EqualsAndHashCode(callSuper = true, of = "map")
-public final class RqForm extends RqWrap {
+@SuppressWarnings("PMD.TooManyMethods")
+public interface RqForm extends Request {
/**
- * Map of params and values.
+ * Get single parameter.
+ * @param name Parameter name
+ * @return List of values (can be empty)
*/
- private final transient ConcurrentMap> map;
+ Iterable param(CharSequence name);
/**
- * Ctor.
- * @param req Original request
- * @throws IOException If fails
+ * Get all parameter names.
+ * @return All names
*/
- @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
- public RqForm(final Request req) throws IOException {
- super(req);
- final ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new RqPrint(req).printBody(baos);
- final String body = new String(baos.toByteArray());
- this.map = new ConcurrentHashMap>(0);
- for (final String pair : body.split("&")) {
- if (pair.isEmpty()) {
- continue;
+ Iterable names();
+
+ /**
+ * Base implementation of @link RqForm.
+ * @author Aleksey Popov (alopen@yandex.ru)
+ * @version $Id$
+ */
+ @EqualsAndHashCode(callSuper = true, of = "map")
+ final class Base extends RqWrap implements RqForm {
+ /**
+ * Map of params and values.
+ */
+ private final transient ConcurrentMap> map;
+ /**
+ * Ctor.
+ * @param req Original request
+ * @throws IOException If fails
+ */
+ @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
+ public Base(final Request req) throws IOException {
+ super(req);
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ new RqPrint(req).printBody(baos);
+ final String body = new String(baos.toByteArray());
+ this.map = new ConcurrentHashMap>(0);
+ for (final String pair : body.split("&")) {
+ if (pair.isEmpty()) {
+ continue;
+ }
+ final String[] parts = pair.split("=", 2);
+ if (parts.length < 2) {
+ throw new IOException(
+ String.format("invalid form body pair: %s", pair)
+ );
+ }
+ final String key = RqForm.Base.decode(
+ parts[0].trim().toLowerCase(Locale.ENGLISH)
+ );
+ this.map.putIfAbsent(key, new LinkedList());
+ this.map.get(key).add(RqForm.Base.decode(parts[1].trim()));
}
- final String[] parts = pair.split("=", 2);
- if (parts.length < 2) {
- throw new IOException(
- String.format("invalid form body pair: %s", pair)
+ }
+ @Override
+ public Iterable param(final CharSequence key) {
+ final List values =
+ this.map.get(key.toString().toLowerCase(Locale.ENGLISH));
+ final Iterable iter;
+ if (values == null) {
+ iter = new VerboseIterable(
+ Collections.emptyList(),
+ new Sprintf(
+ "there're no params by name \"%s\" among %d others: %s",
+ key, this.map.size(), this.map.keySet()
+ )
+ );
+ } else {
+ iter = new VerboseIterable(
+ values,
+ new Sprintf(
+ "there are only %d params by name \"%s\"",
+ values.size(), key
+ )
);
}
- final String key = RqForm.decode(
- parts[0].trim().toLowerCase(Locale.ENGLISH)
- );
- this.map.putIfAbsent(key, new LinkedList());
- this.map.get(key).add(RqForm.decode(parts[1].trim()));
+ return iter;
}
- }
-
- /**
- * Get single parameter.
- * @param key Parameter name
- * @return List of values (can be empty)
- */
- public Iterable param(final CharSequence key) {
- final List values =
- this.map.get(key.toString().toLowerCase(Locale.ENGLISH));
- final Iterable iter;
- if (values == null) {
- iter = new VerboseIterable(
- Collections.emptyList(),
- new Sprintf(
- "there are no params by name \"%s\" among %d others: %s",
- key, this.map.size(), this.map.keySet()
- )
- );
- } else {
- iter = new VerboseIterable(
- values,
- new Sprintf(
- "there are only %d params by name \"%s\"",
- values.size(), key
- )
- );
+ @Override
+ public Iterable names() {
+ return this.map.keySet();
+ }
+ /**
+ * Decode from URL.
+ * @param txt Text
+ * @return Decoded
+ */
+ private static String decode(final CharSequence txt) {
+ try {
+ return URLDecoder.decode(
+ txt.toString(), Charset.defaultCharset().name()
+ );
+ } catch (final UnsupportedEncodingException ex) {
+ throw new IllegalStateException(ex);
+ }
}
- return iter;
- }
-
- /**
- * Get all parameter names.
- * @return All names
- */
- public Iterable names() {
- return this.map.keySet();
}
-
/**
- * Decode from URL.
- * @param txt Text
- * @return Decoded
+ * Smart decorator, with extra features.
+ *
+ * The class is immutable and thread-safe.
+ *
+ * @author Yegor Bugayenko (yegor@teamed.io)
+ * @since 0.14
*/
- private static String decode(final CharSequence txt) {
- try {
- return URLDecoder.decode(
- txt.toString(), Charset.defaultCharset().name()
- );
- } catch (final UnsupportedEncodingException ex) {
- throw new IllegalStateException(ex);
+ @EqualsAndHashCode(of = "origin")
+ final class Smart implements RqForm {
+ /**
+ * Original.
+ */
+ private final transient RqForm origin;
+ /**
+ * Ctor.
+ * @param req Original request
+ */
+ public Smart(final RqForm req) {
+ this.origin = req;
+ }
+ @Override
+ public Iterable param(final CharSequence name) {
+ return this.origin.param(name);
+ }
+ @Override
+ public Iterable names() {
+ return this.origin.names();
+ }
+ @Override
+ public Iterable head() throws IOException {
+ return this.origin.head();
+ }
+ @Override
+ public InputStream body() throws IOException {
+ return this.origin.body();
+ }
+ /**
+ * Get single param or throw HTTP exception.
+ * @param name Name of query param
+ * @return Value of it
+ * @throws IOException If fails
+ */
+ public String single(final CharSequence name) throws IOException {
+ final Iterator params = this.origin
+ .param(name).iterator();
+ if (!params.hasNext()) {
+ throw new HttpException(
+ HttpURLConnection.HTTP_BAD_REQUEST,
+ String.format(
+ "form param \"%s\" is mandatory", name
+ )
+ );
+ }
+ return params.next();
+ }
+ /**
+ * Get single param or default.
+ * @param name Name of query param
+ * @param def Default, if not found
+ * @return Value of it
+ */
+ public String single(final CharSequence name, final String def) {
+ final String value;
+ final Iterator params = this.origin
+ .param(name).iterator();
+ if (params.hasNext()) {
+ value = params.next();
+ } else {
+ value = def;
+ }
+ return value;
}
}
-
}
diff --git a/src/main/java/org/takes/rq/RqHref.java b/src/main/java/org/takes/rq/RqHref.java
index 44c943edc..afacc51fb 100644
--- a/src/main/java/org/takes/rq/RqHref.java
+++ b/src/main/java/org/takes/rq/RqHref.java
@@ -137,7 +137,7 @@ public Href home() throws IOException {
* @return Value of it
* @throws IOException If fails
*/
- public String param(final String name) throws IOException {
+ public String single(final CharSequence name) throws IOException {
final Iterator params = this.origin.href()
.param(name).iterator();
if (!params.hasNext()) {
@@ -157,7 +157,7 @@ public String param(final String name) throws IOException {
* @return Value of it
* @throws IOException If fails
*/
- public String param(final String name, final String def)
+ public String single(final CharSequence name, final CharSequence def)
throws IOException {
final String value;
final Iterator params = this.origin.href()
@@ -165,7 +165,7 @@ public String param(final String name, final String def)
if (params.hasNext()) {
value = params.next();
} else {
- value = def;
+ value = def.toString();
}
return value;
}
diff --git a/src/main/java/org/takes/rq/RqMethod.java b/src/main/java/org/takes/rq/RqMethod.java
index 9154d6e1b..125653ca9 100644
--- a/src/main/java/org/takes/rq/RqMethod.java
+++ b/src/main/java/org/takes/rq/RqMethod.java
@@ -29,69 +29,83 @@
import org.takes.Request;
/**
- * Request decorator, for HTTP method parsing.
+ * HTTP method parsing.
*
- * The class is immutable and thread-safe.
+ *
All implementations of this interface must be immutable and thread-safe.
*
* @author Yegor Bugayenko (yegor@teamed.io)
* @version $Id$
- * @since 0.1
+ * @since 0.13.7
*/
-@EqualsAndHashCode(callSuper = true)
-public final class RqMethod extends RqWrap {
+public interface RqMethod extends Request {
/**
* GET method.
*/
- public static final String GET = "GET";
+ String GET = "GET";
/**
* POST method.
*/
- public static final String POST = "POST";
+ String POST = "POST";
/**
* PUT method.
*/
- public static final String PUT = "PUT";
+ String PUT = "PUT";
/**
* DELETE method.
*/
- public static final String DELETE = "DELETE";
+ String DELETE = "DELETE";
/**
* HEAD method.
*/
- public static final String HEAD = "HEAD";
+ String HEAD = "HEAD";
/**
* OPTIONS method.
*/
- public static final String OPTIONS = "OPTIONS";
+ String OPTIONS = "OPTIONS";
/**
* PATCH method.
*/
- public static final String PATCH = "PATCH";
-
- /**
- * Ctor.
- * @param req Original request
- */
- public RqMethod(final Request req) {
- super(req);
- }
+ String PATCH = "PATCH";
/**
* Get method.
* @return HTTP method
* @throws IOException If fails
*/
- public String method() throws IOException {
- final String line = this.head().iterator().next();
- final String[] parts = line.split(" ", 2);
- return parts[0].toUpperCase(Locale.ENGLISH);
- }
+ String method() throws IOException;
+ /**
+ * Request decorator, for HTTP method parsing.
+ *
+ *
The class is immutable and thread-safe.
+ *
+ * @author Dmitry Zaytsev (dmitry.zaytsev@gmail.com)
+ * @version $Id$
+ * @since 0.13.7
+ */
+ @EqualsAndHashCode(callSuper = true)
+ final class Base extends RqWrap implements RqMethod {
+
+ /**
+ * Ctor.
+ * @param req Original request
+ */
+ public Base(final Request req) {
+ super(req);
+ }
+
+ @Override
+ public String method() throws IOException {
+ final String line = this.head().iterator().next();
+ final String[] parts = line.split(" ", 2);
+ return parts[0].toUpperCase(Locale.ENGLISH);
+ }
+ }
}
diff --git a/src/main/java/org/takes/rq/RqMultipart.java b/src/main/java/org/takes/rq/RqMultipart.java
index 3753295a5..59cbe6bc9 100644
--- a/src/main/java/org/takes/rq/RqMultipart.java
+++ b/src/main/java/org/takes/rq/RqMultipart.java
@@ -42,200 +42,220 @@
import org.takes.misc.VerboseIterable;
/**
- * Request decorator that decodes FORM data from
- * {@code multipart/form-data} format (RFC 2045).
+ * HTTP multipart FORM data decoding.
*
- *
For {@code application/x-www-form-urlencoded}
- * format use {@link org.takes.rq.RqForm}.
- *
- *
It is highly recommended to use {@link org.takes.rq.RqGreedy}
- * decorator before passing request to this class.
- *
- *
The class is immutable and thread-safe.
+ *
All implementations of this interface must be immutable and thread-safe.
*
* @author Yegor Bugayenko (yegor@teamed.io)
* @version $Id$
* @since 0.9
- * @link Forms in HTML
- * @checkstyle ClassDataAbstractionCouplingCheck (500 lines)
- * @see org.takes.rq.RqGreedy
*/
-@EqualsAndHashCode(callSuper = true)
-public final class RqMultipart extends RqWrap {
-
- /**
- * Pattern to get boundary from header.
- */
- private static final Pattern BOUNDARY = Pattern.compile(
- ".*[^a-z]boundary=([^;]+).*"
- );
-
- /**
- * Pattern to get name from header.
- */
- private static final Pattern NAME = Pattern.compile(
- ".*[^a-z]name=\"([^\"]+)\".*"
- );
-
- /**
- * Map of params and values.
- */
- private final transient ConcurrentMap> map;
-
- /**
- * Ctor.
- * @param req Original request
- * @throws IOException If fails
- */
- public RqMultipart(final Request req) throws IOException {
- super(req);
- final String header = new RqHeaders(req).header("Content-Type")
- .iterator().next();
- if (!header.toLowerCase(Locale.ENGLISH)
- .startsWith("multipart/form-data")) {
- throw new IOException(
- String.format(
- // @checkstyle LineLength (1 line)
- "RqMultipart can only parse multipart/form-data, while Content-Type specifies a different type: %s",
- header
- )
- );
- }
- final Matcher matcher = RqMultipart.BOUNDARY.matcher(header);
- if (!matcher.matches()) {
- throw new IOException(
- String.format(
- "boundary is not specified in Content-Type header: %s",
- header
- )
- );
- }
- final Collection requests = new LinkedList();
- final ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new RqPrint(req).printBody(baos);
- final byte[] boundary = matcher.group(1).getBytes();
- final byte[] body = baos.toByteArray();
- int pos = 0;
- while (pos < body.length) {
- int start = pos + boundary.length + 2;
- if (body[start] == '-') {
- break;
- }
- start += 2;
- final int stop = RqMultipart.indexOf(body, boundary, start) - 2;
- requests.add(this.make(body, start, stop - 2));
- pos = stop;
- }
- this.map = RqMultipart.asMap(requests);
- }
-
+public interface RqMultipart extends Request {
/**
* Get single part.
* @param name Name of the part to get
* @return List of parts (can be empty)
*/
- public Iterable part(final CharSequence name) {
- final List values = this.map
- .get(name.toString().toLowerCase(Locale.ENGLISH));
- final Iterable iter;
- if (values == null) {
- iter = new VerboseIterable(
- Collections.emptyList(),
- new Sprintf(
- "there are no parts by name \"%s\" among %d others: %s",
- name, this.map.size(), this.map.keySet()
- )
- );
- } else {
- iter = new VerboseIterable(
- values,
- new Sprintf(
- "there are just %d parts by name \"%s\"",
- values.size(), name
- )
- );
- }
- return iter;
- }
+ Iterable part(final CharSequence name);
/**
* Get all part names.
* @return All names
*/
- public Iterable names() {
- return this.map.keySet();
- }
+ Iterable names();
/**
- * Make a request.
- * @param body Body
- * @param start Start position
- * @param stop Stop position
- * @return Request
- * @throws IOException If fails
+ * Request decorator, that decodes FORM data from
+ * {@code multipart/form-data} format (RFC 2045).
+ *
+ * For {@code application/x-www-form-urlencoded}
+ * format use {@link org.takes.rq.RqForm}.
+ *
+ *
It is highly recommended to use {@link org.takes.rq.RqGreedy}
+ * decorator before passing request to this class.
+ *
+ *
The class is immutable and thread-safe.
+ *
+ * @author Yegor Bugayenko (yegor@teamed.io)
+ * @version $Id$
+ * @since 0.9
+ * @link Forms in HTML
+ * @checkstyle ClassDataAbstractionCouplingCheck (500 lines)
+ * @see org.takes.rq.RqGreedy
*/
- private Request make(final byte[] body, final int start,
- final int stop) throws IOException {
- final ByteArrayOutputStream baos = new ByteArrayOutputStream();
- baos.write(this.head().iterator().next().getBytes());
- baos.write("\r\n".getBytes());
- baos.write(Arrays.copyOfRange(body, start, stop));
- return new RqLive(new ByteArrayInputStream(baos.toByteArray()));
- }
+ @EqualsAndHashCode(callSuper = true)
+ final class Base extends RqWrap implements RqMultipart {
+ /**
+ * Pattern to get boundary from header.
+ */
+ private static final Pattern BOUNDARY = Pattern.compile(
+ ".*[^a-z]boundary=([^;]+).*"
+ );
- /**
- * Convert a list of requests to a map.
- * @param reqs Requests
- * @return Map of them
- * @throws IOException If fails
- */
- @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
- private static ConcurrentMap> asMap(
- final Collection reqs) throws IOException {
- final ConcurrentMap> map =
- new ConcurrentHashMap>(reqs.size());
- for (final Request req : reqs) {
- final String header = new RqHeaders(req)
- .header("Content-Disposition").iterator().next();
- final Matcher matcher = RqMultipart.NAME.matcher(header);
+ /**
+ * Pattern to get name from header.
+ */
+ private static final Pattern NAME = Pattern.compile(
+ ".*[^a-z]name=\"([^\"]+)\".*"
+ );
+
+ /**
+ * Map of params and values.
+ */
+ private final transient ConcurrentMap> map;
+
+ /**
+ * Ctor.
+ * @param req Original request
+ * @throws IOException If fails
+ */
+ public Base(final Request req) throws IOException {
+ super(req);
+ final String header = new RqHeaders(req).header("Content-Type")
+ .iterator().next();
+ if (!header.toLowerCase(Locale.ENGLISH)
+ .startsWith("multipart/form-data")) {
+ throw new IOException(
+ String.format(
+ // @checkstyle LineLength (1 line)
+ "RqMultipart.Base can only parse multipart/form-data, while Content-Type specifies a different type: %s",
+ header
+ )
+ );
+ }
+ final Matcher matcher = Base.BOUNDARY.matcher(header);
if (!matcher.matches()) {
throw new IOException(
String.format(
- "\"name\" not found in Content-Disposition header: %s",
+ "boundary is not specified in Content-Type header: %s",
header
)
);
}
- final String name = matcher.group(1);
- map.putIfAbsent(name, new LinkedList());
- map.get(name).add(req);
+ final Collection requests = new LinkedList();
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ new RqPrint(req).printBody(baos);
+ final byte[] boundary = matcher.group(1).getBytes();
+ final byte[] body = baos.toByteArray();
+ int pos = 0;
+ while (pos < body.length) {
+ int start = pos + boundary.length + 2;
+ if (body[start] == '-') {
+ break;
+ }
+ start += 2;
+ final int stop = RqMultipart.Base.indexOf(
+ body,
+ boundary,
+ start
+ ) - 2;
+ requests.add(this.make(body, start, stop - 2));
+ pos = stop;
+ }
+ this.map = RqMultipart.Base.asMap(requests);
}
- return map;
- }
- /**
- * Find position of array inside another array.
- * @param outer Big array
- * @param inner Small array
- * @param start Where to start searching
- * @return Position
- * @throws IOException If fails
- */
- private static int indexOf(final byte[] outer, final byte[] inner,
- final int start) throws IOException {
- for (int idx = start; idx < outer.length - inner.length; ++idx) {
- boolean found = true;
- for (int sub = 0; sub < inner.length; ++sub) {
- if (outer[idx + sub] != inner[sub]) {
- found = false;
- break;
+ @Override
+ public Iterable part(final CharSequence name) {
+ final List values = this.map
+ .get(name.toString().toLowerCase(Locale.ENGLISH));
+ final Iterable iter;
+ if (values == null) {
+ iter = new VerboseIterable(
+ Collections.emptyList(),
+ new Sprintf(
+ "there are no parts by name \"%s\" among %d others: %s",
+ name, this.map.size(), this.map.keySet()
+ )
+ );
+ } else {
+ iter = new VerboseIterable(
+ values,
+ new Sprintf(
+ "there are just %d parts by name \"%s\"",
+ values.size(), name
+ )
+ );
+ }
+ return iter;
+ }
+
+ @Override
+ public Iterable names() {
+ return this.map.keySet();
+ }
+
+ /**
+ * Make a request.
+ * @param body Body
+ * @param start Start position
+ * @param stop Stop position
+ * @return Request
+ * @throws IOException If fails
+ */
+ private Request make(final byte[] body, final int start,
+ final int stop) throws IOException {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(this.head().iterator().next().getBytes());
+ baos.write("\r\n".getBytes());
+ baos.write(Arrays.copyOfRange(body, start, stop));
+ return new RqLive(new ByteArrayInputStream(baos.toByteArray()));
+ }
+
+ /**
+ * Convert a list of requests to a map.
+ * @param reqs Requests
+ * @return Map of them
+ * @throws IOException If fails
+ */
+ @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
+ private static ConcurrentMap> asMap(
+ final Collection reqs) throws IOException {
+ final ConcurrentMap> map =
+ new ConcurrentHashMap>(reqs.size());
+ for (final Request req : reqs) {
+ final String header = new RqHeaders(req)
+ .header("Content-Disposition").iterator().next();
+ final Matcher matcher = Base.NAME.matcher(header);
+ if (!matcher.matches()) {
+ throw new IOException(
+ String.format(
+ // @checkstyle LineLength (1 line)
+ "\"name\" not found in Content-Disposition header: %s",
+ header
+ )
+ );
}
+ final String name = matcher.group(1);
+ map.putIfAbsent(name, new LinkedList());
+ map.get(name).add(req);
}
- if (found) {
- return idx;
+ return map;
+ }
+
+ /**
+ * Find position of array inside another array.
+ * @param outer Big array
+ * @param inner Small array
+ * @param start Where to start searching
+ * @return Position
+ * @throws IOException If fails
+ */
+ private static int indexOf(final byte[] outer, final byte[] inner,
+ final int start) throws IOException {
+ for (int idx = start; idx < outer.length - inner.length; ++idx) {
+ boolean found = true;
+ for (int sub = 0; sub < inner.length; ++sub) {
+ if (outer[idx + sub] != inner[sub]) {
+ found = false;
+ break;
+ }
+ }
+ if (found) {
+ return idx;
+ }
}
+ throw new IOException("closing boundary not found");
}
- throw new IOException("closing boundary not found");
}
-
}
diff --git a/src/main/java/org/takes/rs/RsFluent.java b/src/main/java/org/takes/rs/RsFluent.java
index bc45c9507..9f949b25f 100644
--- a/src/main/java/org/takes/rs/RsFluent.java
+++ b/src/main/java/org/takes/rs/RsFluent.java
@@ -68,7 +68,7 @@ public RsFluent withStatus(final int code) {
* @param header The header
* @return New fluent response
*/
- public RsFluent withHeader(final String header) {
+ public RsFluent withHeader(final CharSequence header) {
return new RsFluent(new RsWithHeader(this, header));
}
@@ -78,7 +78,8 @@ public RsFluent withHeader(final String header) {
* @param value Value
* @return New fluent response
*/
- public RsFluent withHeader(final String key, final String value) {
+ public RsFluent withHeader(final CharSequence key,
+ final CharSequence value) {
return new RsFluent(new RsWithHeader(this, key, value));
}
@@ -87,7 +88,7 @@ public RsFluent withHeader(final String key, final String value) {
* @param ctype Content type
* @return New fluent response
*/
- public RsFluent withType(final String ctype) {
+ public RsFluent withType(final CharSequence ctype) {
return new RsFluent(new RsWithType(this, ctype));
}
@@ -96,7 +97,7 @@ public RsFluent withType(final String ctype) {
* @param body Body
* @return New fluent response
*/
- public RsFluent withBody(final String body) {
+ public RsFluent withBody(final CharSequence body) {
return new RsFluent(new RsWithBody(this, body));
}
diff --git a/src/main/java/org/takes/rs/RsHTML.java b/src/main/java/org/takes/rs/RsHTML.java
index 90bc39a16..be9f07f2b 100644
--- a/src/main/java/org/takes/rs/RsHTML.java
+++ b/src/main/java/org/takes/rs/RsHTML.java
@@ -53,7 +53,7 @@ public RsHTML() {
* Ctor.
* @param body HTML body
*/
- public RsHTML(final String body) {
+ public RsHTML(final CharSequence body) {
this(new RsEmpty(), body);
}
@@ -87,7 +87,7 @@ public RsHTML(final InputStream body) {
* @param res Original response
* @param body HTML body
*/
- public RsHTML(final Response res, final String body) {
+ public RsHTML(final Response res, final CharSequence body) {
this(new RsWithBody(res, body));
}
diff --git a/src/main/java/org/takes/rs/RsRedirect.java b/src/main/java/org/takes/rs/RsRedirect.java
index c1c8900a5..cfe6c6115 100644
--- a/src/main/java/org/takes/rs/RsRedirect.java
+++ b/src/main/java/org/takes/rs/RsRedirect.java
@@ -49,7 +49,7 @@ public RsRedirect() {
* Ctor.
* @param location Where to redirect
*/
- public RsRedirect(final String location) {
+ public RsRedirect(final CharSequence location) {
this(location, HttpURLConnection.HTTP_SEE_OTHER);
}
@@ -58,7 +58,7 @@ public RsRedirect(final String location) {
* @param location Location
* @param code HTTP redirect status code
*/
- public RsRedirect(final String location, final int code) {
+ public RsRedirect(final CharSequence location, final int code) {
super(
new RsWithHeader(
new RsWithStatus(new RsEmpty(), code),
diff --git a/src/main/java/org/takes/rs/RsText.java b/src/main/java/org/takes/rs/RsText.java
index 111c6ac68..385bab64a 100644
--- a/src/main/java/org/takes/rs/RsText.java
+++ b/src/main/java/org/takes/rs/RsText.java
@@ -53,7 +53,7 @@ public RsText() {
* Ctor.
* @param body Plain text body
*/
- public RsText(final String body) {
+ public RsText(final CharSequence body) {
this(new RsEmpty(), body);
}
@@ -87,7 +87,7 @@ public RsText(final URL url) {
* @param res Original response
* @param body HTML body
*/
- public RsText(final Response res, final String body) {
+ public RsText(final Response res, final CharSequence body) {
this(new RsWithBody(res, body));
}
diff --git a/src/main/java/org/takes/rs/RsVelocity.java b/src/main/java/org/takes/rs/RsVelocity.java
index 4216fa219..40420c78d 100644
--- a/src/main/java/org/takes/rs/RsVelocity.java
+++ b/src/main/java/org/takes/rs/RsVelocity.java
@@ -76,9 +76,9 @@ public final class RsVelocity extends RsWrap {
* @param params List of params
* @since 0.11
*/
- public RsVelocity(final String template,
+ public RsVelocity(final CharSequence template,
final RsVelocity.Pair... params) {
- this(new ByteArrayInputStream(template.getBytes()), params);
+ this(new ByteArrayInputStream(template.toString().getBytes()), params);
}
/**
@@ -108,7 +108,8 @@ public RsVelocity(final InputStream template,
* @param tpl Template
* @param params Map of params
*/
- public RsVelocity(final InputStream tpl, final Map params) {
+ public RsVelocity(final InputStream tpl, final Map params) {
super(
new Response() {
@Override
@@ -131,7 +132,7 @@ public InputStream body() throws IOException {
* @throws IOException If fails
*/
private static InputStream render(final InputStream page,
- final Map params) throws IOException {
+ final Map params) throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final Writer writer = new OutputStreamWriter(baos);
final VelocityEngine engine = new VelocityEngine();
@@ -154,11 +155,11 @@ private static InputStream render(final InputStream page,
* @param entries Entries
* @return Map
*/
- private static Map asMap(
- final Map.Entry... entries) {
- final ConcurrentMap map =
- new ConcurrentHashMap(entries.length);
- for (final Map.Entry ent : entries) {
+ private static Map asMap(
+ final Map.Entry... entries) {
+ final ConcurrentMap map =
+ new ConcurrentHashMap(entries.length);
+ for (final Map.Entry ent : entries) {
map.put(ent.getKey(), ent.getValue());
}
return map;
@@ -168,7 +169,7 @@ private static Map asMap(
* Pair of values.
*/
public static final class Pair
- extends AbstractMap.SimpleEntry {
+ extends AbstractMap.SimpleEntry {
/**
* Serialization marker.
*/
@@ -178,7 +179,7 @@ public static final class Pair
* @param key Key
* @param obj Pass
*/
- public Pair(final String key, final Object obj) {
+ public Pair(final CharSequence key, final Object obj) {
super(key, obj);
}
}
diff --git a/src/main/java/org/takes/rs/RsWithBody.java b/src/main/java/org/takes/rs/RsWithBody.java
index 846ed6fa8..5180b6742 100644
--- a/src/main/java/org/takes/rs/RsWithBody.java
+++ b/src/main/java/org/takes/rs/RsWithBody.java
@@ -46,7 +46,7 @@ public final class RsWithBody extends RsWrap {
* Ctor.
* @param body Body
*/
- public RsWithBody(final String body) {
+ public RsWithBody(final CharSequence body) {
this(new RsEmpty(), body);
}
@@ -79,8 +79,8 @@ public RsWithBody(final URL url) {
* @param res Original response
* @param body Body
*/
- public RsWithBody(final Response res, final String body) {
- this(res, body.getBytes());
+ public RsWithBody(final Response res, final CharSequence body) {
+ this(res, body.toString().getBytes());
}
/**
diff --git a/src/main/java/org/takes/rs/RsWithCookie.java b/src/main/java/org/takes/rs/RsWithCookie.java
index 7bf7b132d..3f68288b4 100644
--- a/src/main/java/org/takes/rs/RsWithCookie.java
+++ b/src/main/java/org/takes/rs/RsWithCookie.java
@@ -23,12 +23,18 @@
*/
package org.takes.rs;
+import java.util.regex.Pattern;
import lombok.EqualsAndHashCode;
import org.takes.Response;
/**
* Response decorator, with an additional cookie.
*
+ * The decorator validates cookie name according
+ * @see RFC 2616
+ * and cookie value according
+ * @see RFC 6265
+ *
* Use this decorator in order to return a response with a "Set-Cookie"
* header inside, for example:
*
@@ -51,14 +57,29 @@
@EqualsAndHashCode(callSuper = true)
public final class RsWithCookie extends RsWrap {
+ /**
+ * Cookie value validation regexp.
+ * @checkstyle LineLengthCheck (3 lines)
+ */
+ private static final Pattern CVALUE_PTRN = Pattern.compile(
+ "[\\x21\\x23-\\x2B\\x2D-\\x3A\\x3C-\\x5B\\x5D-\\x7E]*|\"[\\x21\\x23-\\x2B\\x2D-\\x3A\\x3C-\\x5B\\x5D-\\x7E]*\""
+ );
+
+ /**
+ * Cookie name validation regexp.
+ */
+ private static final Pattern CNAME_PTRN = Pattern.compile(
+ "[\\x20-\\x7E&&[^()<>@,;:\\\"/\\[\\]?={} ]]+"
+ );
+
/**
* Ctor.
* @param name Cookie name
* @param value Value of it
* @param attrs Optional attributes, for example "Path=/"
*/
- public RsWithCookie(final String name, final String value,
- final String... attrs) {
+ public RsWithCookie(final CharSequence name, final CharSequence value,
+ final CharSequence... attrs) {
this(new RsEmpty(), name, value, attrs);
}
@@ -70,11 +91,17 @@ public RsWithCookie(final String name, final String value,
* @param attrs Optional attributes, for example "Path=/"
* @checkstyle ParameterNumberCheck (5 lines)
*/
- public RsWithCookie(final Response res, final String name,
- final String value, final String... attrs) {
+ public RsWithCookie(final Response res, final CharSequence name,
+ final CharSequence value, final CharSequence... attrs) {
super(
new RsWithHeader(
- res, "Set-Cookie", RsWithCookie.make(name, value, attrs)
+ res,
+ "Set-Cookie",
+ RsWithCookie.make(
+ RsWithCookie.checkName(name),
+ RsWithCookie.checkValue(value),
+ attrs
+ )
)
);
}
@@ -86,15 +113,48 @@ public RsWithCookie(final Response res, final String name,
* @param attrs Optional attributes, for example "Path=/"
* @return Text
*/
- private static String make(final String name,
- final String value, final String... attrs) {
+ private static String make(final CharSequence name,
+ final CharSequence value, final CharSequence... attrs) {
final StringBuilder text = new StringBuilder(
String.format("%s=%s", name, value)
);
- for (final String attr : attrs) {
+ for (final CharSequence attr : attrs) {
text.append(';').append(attr);
}
return text.toString();
}
+ /**
+ * Checks value according RFC 6265 section 4.1.1.
+ * @param value Cookie value
+ * @return Cookie value
+ */
+ private static CharSequence checkValue(final CharSequence value) {
+ if (!RsWithCookie.CVALUE_PTRN.matcher(value).matches()) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Cookie value %s contains invalid characters",
+ value
+ )
+ );
+ }
+ return value;
+ }
+
+ /**
+ * Checks name according RFC 2616, section 2.2.
+ * @param name Cookie name;
+ * @return Cookie name
+ */
+ private static CharSequence checkName(final CharSequence name) {
+ if (!RsWithCookie.CNAME_PTRN.matcher(name).matches()) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Cookie name %s contains invalid characters",
+ name
+ )
+ );
+ }
+ return name;
+ }
}
diff --git a/src/main/java/org/takes/rs/RsWithHeader.java b/src/main/java/org/takes/rs/RsWithHeader.java
index 20dbc2ce7..271ad3755 100644
--- a/src/main/java/org/takes/rs/RsWithHeader.java
+++ b/src/main/java/org/takes/rs/RsWithHeader.java
@@ -123,12 +123,12 @@ public InputStream body() throws IOException {
*/
private static Iterable extend(final Iterable head,
final String header) throws IOException {
- if (!HEADER.matcher(header).matches()) {
+ if (!RsWithHeader.HEADER.matcher(header).matches()) {
throw new IllegalArgumentException(
String.format(
// @checkstyle LineLength (1 line)
"header line of HTTP response \"%s\" doesn't match \"%s\" regular expression, but it should, according to RFC 7230",
- header, HEADER
+ header, RsWithHeader.HEADER
)
);
}
diff --git a/src/main/java/org/takes/rs/RsWithHeaders.java b/src/main/java/org/takes/rs/RsWithHeaders.java
index 372cf6959..784e62f81 100644
--- a/src/main/java/org/takes/rs/RsWithHeaders.java
+++ b/src/main/java/org/takes/rs/RsWithHeaders.java
@@ -26,10 +26,13 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
+
import lombok.EqualsAndHashCode;
+
import org.takes.Response;
import org.takes.misc.Concat;
import org.takes.misc.Transformer;
+import org.takes.misc.Transformer.Trim;
/**
* Response decorator, with an additional headers.
@@ -48,7 +51,7 @@ public final class RsWithHeaders extends RsWrap {
* @param res Original response
* @param headers Headers
*/
- public RsWithHeaders(final Response res, final String... headers) {
+ public RsWithHeaders(final Response res, final CharSequence... headers) {
this(res, Arrays.asList(headers));
}
@@ -57,17 +60,22 @@ public RsWithHeaders(final Response res, final String... headers) {
* @param res Original response
* @param headers Headers
*/
- public RsWithHeaders(final Response res, final Iterable headers) {
+ public RsWithHeaders(final Response res,
+ final Iterable extends CharSequence> headers) {
super(
new Response() {
@Override
+ @SuppressWarnings("unchecked")
public Iterable head() throws IOException {
return new Concat(
res.head(),
- new Transformer(
- headers,
- new Transformer.Trim()
- )
+ new Transformer(
+ new Transformer(
+ (Iterable)headers,
+ new Transformer.ToString()
+ )
+ , new Transformer.Trim()
+ )
);
}
@Override
diff --git a/src/main/java/org/takes/rs/RsWithStatus.java b/src/main/java/org/takes/rs/RsWithStatus.java
index f006cb41b..5bdd77d2e 100644
--- a/src/main/java/org/takes/rs/RsWithStatus.java
+++ b/src/main/java/org/takes/rs/RsWithStatus.java
@@ -76,7 +76,8 @@ public RsWithStatus(final Response res, final int code) {
* @param code Status code
* @param rsn Reason
*/
- public RsWithStatus(final Response res, final int code, final String rsn) {
+ public RsWithStatus(final Response res, final int code,
+ final CharSequence rsn) {
super(
new Response() {
@Override
@@ -101,7 +102,7 @@ public InputStream body() throws IOException {
*/
@SuppressWarnings("unchecked")
private static Iterable head(final Response origin,
- final int status, final String reason) throws IOException {
+ final int status, final CharSequence reason) throws IOException {
// @checkstyle MagicNumber (1 line)
if (status < 100 || status > 999) {
throw new IllegalArgumentException(
diff --git a/src/main/java/org/takes/rs/RsWithType.java b/src/main/java/org/takes/rs/RsWithType.java
index ab236cfcd..4c54aaa8f 100644
--- a/src/main/java/org/takes/rs/RsWithType.java
+++ b/src/main/java/org/takes/rs/RsWithType.java
@@ -44,7 +44,7 @@ public final class RsWithType extends RsWrap {
* @param res Original response
* @param type Content type
*/
- public RsWithType(final Response res, final String type) {
+ public RsWithType(final Response res, final CharSequence type) {
super(
new RsWithHeader(
new RsWithStatus(res, HttpURLConnection.HTTP_OK),
diff --git a/src/main/java/org/takes/rs/RsWithoutHeader.java b/src/main/java/org/takes/rs/RsWithoutHeader.java
index 029908731..e162f0ad8 100644
--- a/src/main/java/org/takes/rs/RsWithoutHeader.java
+++ b/src/main/java/org/takes/rs/RsWithoutHeader.java
@@ -49,14 +49,14 @@ public final class RsWithoutHeader extends RsWrap {
* @param res Original response
* @param name Header name
*/
- public RsWithoutHeader(final Response res, final String name) {
+ public RsWithoutHeader(final Response res, final CharSequence name) {
super(
new Response() {
@Override
@SuppressWarnings("unchecked")
public Iterable head() throws IOException {
final String prefix = String.format(
- "%s:", name.toLowerCase(Locale.ENGLISH)
+ "%s:", name.toString().toLowerCase(Locale.ENGLISH)
);
return new Concat(
res.head(),
diff --git a/src/main/java/org/takes/tk/TkVerbose.java b/src/main/java/org/takes/tk/TkVerbose.java
index 67ac17f30..177e08182 100644
--- a/src/main/java/org/takes/tk/TkVerbose.java
+++ b/src/main/java/org/takes/tk/TkVerbose.java
@@ -60,7 +60,7 @@ public Response act(final Request request) throws IOException {
ex.code(),
String.format(
"%s %s",
- new RqMethod(request).method(),
+ new RqMethod.Base(request).method(),
new RqHref.Base(request).href()
),
ex
diff --git a/src/test/java/org/takes/facets/auth/codecs/CcBase64Test.java b/src/test/java/org/takes/facets/auth/codecs/CcBase64Test.java
new file mode 100644
index 000000000..f94ac9060
--- /dev/null
+++ b/src/test/java/org/takes/facets/auth/codecs/CcBase64Test.java
@@ -0,0 +1,140 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Yegor Bugayenko
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.takes.facets.auth.codecs;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.util.Map;
+import nl.jqno.equalsverifier.EqualsVerifier;
+import nl.jqno.equalsverifier.Warning;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.takes.facets.auth.Identity;
+
+/**
+ * Test case for {@link CcBase64}.
+ * @author Igor Khvostenkov (ikhvostenkov@gmail.com)
+ * @version $Id$
+ * @since 0.13
+ */
+@Ignore
+public final class CcBase64Test {
+ /**
+ * CcBase64 can encode.
+ * @throws IOException If some problem inside
+ */
+ @Test
+ public void encodes() throws IOException {
+ MatcherAssert.assertThat(
+ new String(
+ new CcBase64(new CcPlain()).encode(
+ new Identity.Simple("urn:test:3")
+ )
+ ),
+ Matchers.equalTo("dXJuJTNBdGVzdCUzQTM=")
+ );
+ }
+ /**
+ * CcBase64 can decode.
+ * @throws IOException If some problem inside
+ */
+ @Test
+ public void decodes() throws IOException {
+ MatcherAssert.assertThat(
+ new CcBase64(new CcPlain()).decode(
+ "dXJuJTNBdGVzdCUzQXRlc3Q="
+ .getBytes()
+ ).urn(),
+ Matchers.equalTo("urn:test:test")
+ );
+ }
+ /**
+ * CcBase64 can encode and decode.
+ * @throws IOException If some problem inside
+ */
+ @Test
+ public void encodesAndDecodes() throws IOException {
+ final String urn = "urn:test:Hello World!";
+ final Map properties =
+ ImmutableMap.of("userName", "user");
+ final Codec codec = new CcBase64(new CcPlain());
+ final Identity expected = codec.decode(
+ codec.encode(new Identity.Simple(urn, properties))
+ );
+ MatcherAssert.assertThat(
+ expected.urn(),
+ Matchers.equalTo(urn)
+ );
+ MatcherAssert.assertThat(
+ expected.properties(),
+ Matchers.equalTo(properties)
+ );
+ }
+ /**
+ * CcBase64 can encode empty byte array.
+ * @throws IOException If some problem inside
+ */
+ @Test
+ public void encodesEmptyByteArray() throws IOException {
+ MatcherAssert.assertThat(
+ new String(
+ new CcBase64(new CcPlain()).encode(
+ new Identity.Simple("")
+ )
+ ),
+ Matchers.equalTo("")
+ );
+ }
+ /**
+ * CcBase64 can decode non Base64 alphabet symbols.
+ * @throws IOException If some problem inside
+ */
+ @Test
+ public void decodesNonBaseSixtyFourAlphabetSymbols() throws IOException {
+ try {
+ new CcStrict(new CcBase64(new CcPlain())).decode(
+ " ^^^".getBytes()
+ );
+ } catch (final DecodingException ex) {
+ MatcherAssert.assertThat(
+ ex.getMessage(),
+ Matchers.equalTo(
+ "Illegal character in Base64 encoded data. [32, 94, 94, 94]"
+ )
+ );
+ }
+ }
+ /**
+ * Checks CcBase64 equals method.
+ * @throws Exception If some problem inside
+ */
+ @Test
+ public void equalsAndHashCodeEqualTest() throws Exception {
+ EqualsVerifier.forClass(CcBase64.class)
+ .suppress(Warning.TRANSIENT_FIELDS)
+ .verify();
+ }
+}
diff --git a/src/test/java/org/takes/facets/auth/codecs/CcXORTest.java b/src/test/java/org/takes/facets/auth/codecs/CcXORTest.java
new file mode 100644
index 000000000..3104b9ef7
--- /dev/null
+++ b/src/test/java/org/takes/facets/auth/codecs/CcXORTest.java
@@ -0,0 +1,66 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Yegor Bugayenko
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.takes.facets.auth.codecs;
+
+import java.io.IOException;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.Test;
+import org.takes.facets.auth.Identity;
+
+/**
+ * Test case for {@link CcXOR}.
+ * @author Dmitry Zaytsev (dmitry.zaytsev@gmail.com)
+ * @version $Id$
+ * @since 0.13.7
+ */
+public final class CcXORTest {
+
+ /**
+ * CcXor can encode and decode.
+ * @throws IOException If some problem inside
+ */
+ @Test
+ public void encodesAndDecodes() throws IOException {
+ final String urn = "urn:domain:9";
+ final Codec codec = new CcXOR(
+ new Codec() {
+ @Override
+ public byte[] encode(final Identity identity) {
+ return identity.urn().getBytes();
+ }
+ @Override
+ public Identity decode(final byte[] bytes) {
+ return new Identity.Simple(new String(bytes));
+ }
+ },
+ "secret"
+ );
+ MatcherAssert.assertThat(
+ codec.decode(codec.encode(new Identity.Simple(urn))).urn(),
+ Matchers.equalTo(urn)
+ );
+ }
+}
diff --git a/src/test/java/org/takes/facets/fork/TkProducesTest.java b/src/test/java/org/takes/facets/fork/TkProducesTest.java
new file mode 100644
index 000000000..af6e74473
--- /dev/null
+++ b/src/test/java/org/takes/facets/fork/TkProducesTest.java
@@ -0,0 +1,104 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Yegor Bugayenko
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.takes.facets.fork;
+
+import com.google.common.base.Joiner;
+import java.io.IOException;
+import java.util.Arrays;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.Test;
+import org.takes.HttpException;
+import org.takes.Response;
+import org.takes.Take;
+import org.takes.rq.RqFake;
+import org.takes.rs.RsEmpty;
+import org.takes.rs.RsJSON;
+import org.takes.rs.RsPrint;
+import org.takes.tk.TkEmpty;
+import org.takes.tk.TkFixed;
+
+/**
+ * Test case for {@link TkProduces}.
+ * @author Eugene Kondrashev (eugene.kondrashev@gmail.com)
+ * @version $Id$
+ * @since 0.14
+ */
+public final class TkProducesTest {
+
+ /**
+ * TkProduces can fail on unsupported Accept header.
+ * @throws IOException If some problem inside
+ */
+ @Test(expected = HttpException.class)
+ public void failsOnUnsupportedAcceptHeader() throws IOException {
+ final Take produces = new TkProduces(
+ new TkEmpty(),
+ "text/json,application/json"
+ );
+ produces.act(
+ new RqFake(
+ Arrays.asList(
+ "GET /hz0",
+ "Host: as0.example.com",
+ "Accept: text/xml"
+ ),
+ ""
+ )
+ ).head();
+ }
+
+ /**
+ * TkProduce can produce correct type response.
+ * @throws IOException If some problem inside
+ */
+ @Test
+ public void producesCorrectContentTypeResponse() throws IOException {
+ final Take produces = new TkProduces(
+ new TkFixed(new RsJSON(new RsEmpty())),
+ "text/json"
+ );
+ final Response response = produces.act(
+ new RqFake(
+ Arrays.asList(
+ "GET /hz09",
+ "Host: as.example.com",
+ "Accept: text/json"
+ ),
+ ""
+ )
+ );
+ MatcherAssert.assertThat(
+ new RsPrint(response).print(),
+ Matchers.equalTo(
+ Joiner.on("\r\n").join(
+ "HTTP/1.1 200 OK",
+ "Content-Type: application/json",
+ "",
+ ""
+ )
+ )
+ );
+ }
+}
diff --git a/src/test/java/org/takes/misc/IterableTransformTest.java b/src/test/java/org/takes/misc/IterableTransformTest.java
index ed734e679..7ee0e8806 100644
--- a/src/test/java/org/takes/misc/IterableTransformTest.java
+++ b/src/test/java/org/takes/misc/IterableTransformTest.java
@@ -48,9 +48,9 @@ public void iterableTransform() {
alist.add("b1");
alist.add("c1");
MatcherAssert.assertThat(
- new Transformer(
+ new Transformer(
alist,
- new Transformer.Action() {
+ new Transformer.Action() {
@Override
public String transform(final String element) {
return element.concat("t");
diff --git a/src/test/java/org/takes/rq/RqFormTest.java b/src/test/java/org/takes/rq/RqFormTest.java
index eb99846f5..6135e43a0 100644
--- a/src/test/java/org/takes/rq/RqFormTest.java
+++ b/src/test/java/org/takes/rq/RqFormTest.java
@@ -43,7 +43,7 @@ public final class RqFormTest {
*/
@Test
public void parsesHttpBody() throws IOException {
- final RqForm req = new RqForm(
+ final RqForm req = new RqForm.Base(
new RqFake(
Arrays.asList(
"GET /h?a=3",
diff --git a/src/test/java/org/takes/rq/RqHrefTest.java b/src/test/java/org/takes/rq/RqHrefTest.java
index f85d2e960..52c3787d2 100644
--- a/src/test/java/org/takes/rq/RqHrefTest.java
+++ b/src/test/java/org/takes/rq/RqHrefTest.java
@@ -138,7 +138,7 @@ public void extractsParamByDefault() throws IOException {
""
)
)
- ).param("absent", "def-5"),
+ ).single("absent", "def-5"),
Matchers.startsWith("def-")
);
}
diff --git a/src/test/java/org/takes/rq/RqMethodTest.java b/src/test/java/org/takes/rq/RqMethodTest.java
index fb12ad94d..b1d841fd5 100644
--- a/src/test/java/org/takes/rq/RqMethodTest.java
+++ b/src/test/java/org/takes/rq/RqMethodTest.java
@@ -43,7 +43,7 @@ public final class RqMethodTest {
@Test
public void returnsMethod() throws IOException {
MatcherAssert.assertThat(
- new RqMethod(new RqFake(RqMethod.POST)).method(),
+ new RqMethod.Base(new RqFake(RqMethod.POST)).method(),
Matchers.equalTo(RqMethod.POST)
);
}
diff --git a/src/test/java/org/takes/rq/RqMultipartTest.java b/src/test/java/org/takes/rq/RqMultipartTest.java
index 965f6d5a1..6aee89245 100644
--- a/src/test/java/org/takes/rq/RqMultipartTest.java
+++ b/src/test/java/org/takes/rq/RqMultipartTest.java
@@ -35,7 +35,7 @@
import org.takes.Request;
/**
- * Test case for {@link RqMultipart}.
+ * Test case for {@link RqMultipart.Base}.
* @author Yegor Bugayenko (yegor@teamed.io)
* @version $Id$
* @since 0.9
@@ -49,7 +49,7 @@ public final class RqMultipartTest {
*/
private static final String CR = "\r\n";
/**
- * RqMultipart can satisfy equals contract.
+ * RqMultipart.Base can satisfy equals contract.
* @throws IOException if some problem inside
*/
@Test
@@ -63,13 +63,13 @@ public void satisfiesEqualsContract() throws IOException {
"Content-Disposition: form-data; name=\"data\"; filename=\"a.bin\""
);
MatcherAssert.assertThat(
- new RqMultipart(req),
- Matchers.equalTo(new RqMultipart(req))
+ new RqMultipart.Base(req),
+ Matchers.equalTo(new RqMultipart.Base(req))
);
}
/**
- * RqMultipart can throw exception on no closing boundary found.
+ * RqMultipart.Base can throw exception on no closing boundary found.
* @throws IOException if some problem inside
*/
@Test(expected = IOException.class)
@@ -89,11 +89,11 @@ public void throwsExceptionOnNoClosingBounaryFound() throws IOException {
"Content-Transfer-Encoding: uwf-8"
)
);
- new RqMultipart(req);
+ new RqMultipart.Base(req);
}
/**
- * RqMultipart can throw exception on no name
+ * RqMultipart.Base can throw exception on no name
* at Content-Disposition header.
* @throws IOException if some problem inside
*/
@@ -107,11 +107,12 @@ public void throwsExceptionOnNoNameAtContentDispositionHeader()
"340 N Wolfe Rd, Sunnyvale, CA 94085"
)
);
- new RqMultipart(req);
+ new RqMultipart.Base(req);
}
/**
- * RqMultipart can throw exception on no boundary at Content-Type header.
+ * RqMultipart.Base can throw exception on no boundary
+ * at Content-Type header.
* @throws IOException if some problem inside
*/
@Test(expected = IOException.class)
@@ -126,11 +127,11 @@ public void throwsExceptionOnNoBoundaryAtContentTypeHeader()
),
""
);
- new RqMultipart(req);
+ new RqMultipart.Base(req);
}
/**
- * RqMultipart can throw exception on invalid Content-Type header.
+ * RqMultipart.Base can throw exception on invalid Content-Type header.
* @throws IOException if some problem inside
*/
@Test(expected = IOException.class)
@@ -144,11 +145,11 @@ public void throwsExceptionOnInvalidContentTypeHeader() throws IOException {
),
""
);
- new RqMultipart(req);
+ new RqMultipart.Base(req);
}
/**
- * RqMultipart can parse http body.
+ * RqMultipart.Base can parse http body.
* @throws IOException If some problem inside
*/
@Test
@@ -161,7 +162,7 @@ public void parsesHttpBody() throws IOException {
),
"Content-Disposition: form-data; name=\"data\"; filename=\"a.bin\""
);
- final RqMultipart multi = new RqMultipart(req);
+ final RqMultipart.Base multi = new RqMultipart.Base(req);
MatcherAssert.assertThat(
new RqHeaders(
multi.part("address").iterator().next()
@@ -179,7 +180,7 @@ public void parsesHttpBody() throws IOException {
}
/**
- * RqMultipart can return empty iterator on invalid part request.
+ * RqMultipart.Base can return empty iterator on invalid part request.
* @throws IOException If some problem inside
*/
@Test
@@ -192,7 +193,7 @@ public void returnsEmptyIteratorOnInvalidPartRequest() throws IOException {
),
"Content-Disposition: form-data; name=\"data\"; filename=\"a.zip\""
);
- final RqMultipart multi = new RqMultipart(req);
+ final RqMultipart.Base multi = new RqMultipart.Base(req);
MatcherAssert.assertThat(
multi.part("fake").iterator().hasNext(),
Matchers.is(false)
@@ -200,7 +201,7 @@ public void returnsEmptyIteratorOnInvalidPartRequest() throws IOException {
}
/**
- * RqMultipart can return correct name set.
+ * RqMultipart.Base can return correct name set.
* @throws IOException If some problem inside
*/
@Test
@@ -213,7 +214,7 @@ public void returnsCorrectNamesSet() throws IOException {
),
"Content-Disposition: form-data; name=\"data\"; filename=\"a.bin\""
);
- final RqMultipart multi = new RqMultipart(req);
+ final RqMultipart.Base multi = new RqMultipart.Base(req);
MatcherAssert.assertThat(
multi.names(),
Matchers.>equalTo(
diff --git a/src/test/java/org/takes/rs/RsWithCookieTest.java b/src/test/java/org/takes/rs/RsWithCookieTest.java
index d56d04a55..dbd1050a2 100644
--- a/src/test/java/org/takes/rs/RsWithCookieTest.java
+++ b/src/test/java/org/takes/rs/RsWithCookieTest.java
@@ -61,4 +61,21 @@ public void addsCookieToResponse() throws IOException {
);
}
+ /**
+ * RsWithCookie can reject invalid cookie name.
+ * @throws IOException If some problem inside
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void rejectsInvalidName() throws IOException {
+ new RsWithCookie(new RsEmpty(), "f oo", "works");
+ }
+
+ /**
+ * RsWithCookie can reject invalid cookie value.
+ * @throws IOException If some problem inside
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void rejectsInvalidValue() throws IOException {
+ new RsWithCookie(new RsEmpty(), "bar", "wo\"rks");
+ }
}