list = new LinkedList<>();
- protected FastByteBuffer last;
- protected int size;
-
- /**
- * Appends string content to buffer.
- */
- public Buffer append(final String string) {
- ensureLast();
-
- final byte[] bytes = string.getBytes(StandardCharsets.ISO_8859_1);
- last.append(bytes);
- size += bytes.length;
-
- return this;
- }
-
- /**
- * Appends a char.
- */
- public Buffer append(final char c) {
- append(Character.toString(c));
- return this;
- }
-
- /**
- * Appends a number.
- */
- public Buffer append(final int number) {
- append(Integer.toString(number));
- return this;
- }
-
- /**
- * Appends {@link jodd.http.up.Uploadable} to buffer.
- */
- public Buffer append(final Uploadable uploadable) {
- list.add(uploadable);
- size += uploadable.getSize();
- last = null;
- return this;
- }
-
- /**
- * Appends other buffer to this one.
- */
- public Buffer append(final Buffer buffer) {
- if (buffer.list.isEmpty()) {
- // nothing to append
- return buffer;
- }
- list.addAll(buffer.list);
- last = buffer.last;
- size += buffer.size;
- return this;
- }
-
- /**
- * Returns buffer size.
- */
- public int size() {
- return size;
- }
-
- /**
- * Ensures that last buffer exist.
- */
- private void ensureLast() {
- if (last == null) {
- last = new FastByteBuffer();
- list.add(last);
- }
- }
-
- // ---------------------------------------------------------------- write
-
- /**
- * Writes content to the writer.
- */
- public void writeTo(final Writer writer) throws IOException {
- for (final Object o : list) {
- if (o instanceof FastByteBuffer) {
- final FastByteBuffer fastByteBuffer = (FastByteBuffer) o;
-
- final byte[] array = fastByteBuffer.toArray();
-
- writer.write(new String(array, StandardCharsets.ISO_8859_1));
- }
- else if (o instanceof Uploadable) {
- final Uploadable uploadable = (Uploadable) o;
-
- final InputStream inputStream = uploadable.openInputStream();
-
- try {
- IOUtil.copy(inputStream, writer, StandardCharsets.ISO_8859_1);
- }
- finally {
- IOUtil.close(inputStream);
- }
- }
- }
- }
-
- /**
- * Writes content to the output stream.
- */
- public void writeTo(final OutputStream out) throws IOException {
- for (final Object o : list) {
- if (o instanceof FastByteBuffer) {
- final FastByteBuffer fastByteBuffer = (FastByteBuffer) o;
-
- out.write(fastByteBuffer.toArray());
- }
- else if (o instanceof Uploadable) {
- final Uploadable uploadable = (Uploadable) o;
-
- final InputStream inputStream = uploadable.openInputStream();
-
- try {
- IOUtil.copy(inputStream, out);
- }
- finally {
- IOUtil.close(inputStream);
- }
- }
- }
- }
-
- /**
- * Writes content to the output stream, using progress listener to track the sending progress.
- */
- public void writeTo(final OutputStream out, final HttpProgressListener progressListener) throws IOException {
-
- // start
-
- final int size = size();
- final int callbackSize = progressListener.callbackSize(size);
- int count = 0; // total count
- int step = 0; // step is offset in current chunk
-
- progressListener.transferred(count);
-
- // loop
-
- for (final Object o : list) {
- if (o instanceof FastByteBuffer) {
- final FastByteBuffer fastByteBuffer = (FastByteBuffer) o;
- final byte[] bytes = fastByteBuffer.toArray();
-
- int offset = 0;
-
- while (offset < bytes.length) {
- // calc the remaining sending chunk size
- int chunk = callbackSize - step;
-
- // check if this chunk size fits the bytes array
- if (offset + chunk > bytes.length) {
- chunk = bytes.length - offset;
- }
-
- // writes the chunk
- out.write(bytes, offset, chunk);
-
- offset += chunk;
- step += chunk;
- count += chunk;
-
- // listener
- if (step >= callbackSize) {
- progressListener.transferred(count);
- step -= callbackSize;
- }
- }
- }
- else if (o instanceof Uploadable) {
- final Uploadable uploadable = (Uploadable) o;
-
- final InputStream inputStream = uploadable.openInputStream();
-
- int remaining = uploadable.getSize();
-
- try {
- while (remaining > 0) {
- // calc the remaining sending chunk size
- int chunk = callbackSize - step;
-
- // check if this chunk size fits the remaining size
- if (chunk > remaining) {
- chunk = remaining;
- }
-
- // writes remaining chunk
- IOUtil.copy(inputStream, out, chunk);
-
- remaining -= chunk;
- step += chunk;
- count += chunk;
-
- // listener
- if (step >= callbackSize) {
- progressListener.transferred(count);
- step -= callbackSize;
- }
- }
- }
- finally {
- IOUtil.close(inputStream);
- }
- }
- }
-
- // end
-
- if (step != 0) {
- progressListener.transferred(count);
- }
- }
-
-}
diff --git a/jodd-http/src/main/java/jodd/http/Cookie.java b/jodd-http/src/main/java/jodd/http/Cookie.java
deleted file mode 100644
index 465408604..000000000
--- a/jodd-http/src/main/java/jodd/http/Cookie.java
+++ /dev/null
@@ -1,356 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.util.StringUtil;
-
-/**
- * Cookie object. Simple cookie data holder, cookie header parser and generator.
- */
-public class Cookie {
-
- // the value of the cookie itself
-
- private String name;
- private String value;
-
- // attributes encoded in the header's cookie fields
-
- private String comment; // ;Comment=VALUE ... describes cookie's use
- // ;Discard ... implied by maxAge < 0
- private String domain; // ;Domain=VALUE ... domain that sees cookie
- private Integer maxAge; // ;Max-Age=VALUE ... cookies auto-expire
- private String expires; // ;Expires= ... expires values
- private String path; // ;Path=VALUE ... URLs that see the cookie
- private boolean secure; // ;Secure ... e.g. use SSL
- private Integer version; // ;Version=1 ... means RFC 2109++ style
- private boolean httpOnly; // ;HttpOnly
-
-
- /**
- * Creates cookie with specified name and value.
- *
- * The name must conform to RFC 2109. That means it can contain
- * only ASCII alphanumeric characters and cannot contain commas,
- * semicolons, or white space or begin with a $ character.
- *
- * The value can be anything the server chooses to send.
- */
- public Cookie(final String name, final String value) {
- setName(name);
- setValue(value);
- }
-
- /**
- * Parses cookie data from given user-agent string.
- */
- public Cookie(String cookie) {
- int from = 0;
- int ndx = 0;
-
- cookie = cookie.trim();
-
- while (ndx < cookie.length()) {
- ndx = cookie.indexOf(';', from);
-
- if (ndx == -1) {
- // last chunk
- ndx = cookie.length();
- }
- int ndx2 = cookie.indexOf('=', from);
-
- String name;
- String value;
- if (ndx2 != -1 && ndx2 < ndx) {
- name = cookie.substring(from, ndx2).trim();
- value = cookie.substring(ndx2 + 1, ndx).trim();
- } else {
- if (from == ndx) {
- ndx++;
- continue;
- }
- name = cookie.substring(from, ndx).trim();
- value = null;
- }
-
- if (value != null && name.equalsIgnoreCase("Max-Age")) {
- setMaxAge(Integer.parseInt(value));
- } else if (name.equalsIgnoreCase("Comment")) {
- setComment(value);
- } else if (name.equalsIgnoreCase("Domain")) {
- setDomain(value);
- } else if (name.equalsIgnoreCase("Path")) {
- setPath(value);
- } else if (name.equalsIgnoreCase("Secure")) {
- setSecure(true);
- } else if (value != null && name.equalsIgnoreCase("Version")) {
- setVersion(Integer.parseInt(value));
- } else if (name.equalsIgnoreCase("HttpOnly")) {
- setHttpOnly(true);
- } else if (name.equalsIgnoreCase("Expires")) {
- setExpires(value);
- } else if (this.name == null && !StringUtil.isBlank(name)) {
- setName(name);
- setValue(value);
- }
-
- // continue
- from = ndx + 1;
- }
- }
-
- /**
- * Sets the cookie name and checks for validity.
- */
- private void setName(final String name) {
- if (name.contains(";") || name.contains(",") || name.startsWith("$")) {
- throw new IllegalArgumentException("Invalid cookie name:" + name);
- }
-
- for (int n = 0; n < name.length(); n++) {
- char c = name.charAt(n);
- if (c <= 0x20 || c >= 0x7f) {
- throw new IllegalArgumentException("Invalid cookie name:" + name);
- }
- }
- this.name = name;
- }
-
- /**
- * Returns the comment describing the purpose of this cookie, or
- * null
if the cookie has no comment.
- */
- public String getComment() {
- return comment;
- }
-
- /**
- * Specifies a comment that describes a cookie's purpose.
- * The comment is useful if the browser presents the cookie
- * to the user.
- */
- public Cookie setComment(final String purpose) {
- comment = purpose;
- return this;
- }
-
- /**
- * Returns the domain name set for this cookie. The form of
- * the domain name is set by RFC 2109.
- */
-
- public String getDomain() {
- return domain;
- }
-
- /**
- * Specifies the domain within which this cookie should be presented.
- *
- * The form of the domain name is specified by RFC 2109. A domain
- * name begins with a dot (.foo.com
) and means that
- * the cookie is visible to servers in a specified Domain Name System
- * (DNS) zone (for example, www.foo.com
, but not
- * a.b.foo.com
). By default, cookies are only returned
- * to the server that sent them.
- */
-
- public Cookie setDomain(final String pattern) {
- domain = pattern.toLowerCase(); // IE allegedly needs this
- return this;
- }
-
- /**
- * Returns the maximum age of the cookie, specified in seconds,
- * By default, -1
indicating the cookie will persist
- * until browser shutdown.
- */
-
- public Integer getMaxAge() {
- return maxAge;
- }
-
- /**
- * Sets the maximum age of the cookie in seconds.
- *
- * A positive value indicates that the cookie will expire
- * after that many seconds have passed. Note that the value is
- * the maximum age when the cookie will expire, not the cookie's
- * current age.
- *
- * A negative value means
- * that the cookie is not stored persistently and will be deleted
- * when the Web browser exits. A zero value causes the cookie
- * to be deleted.
- */
-
- public Cookie setMaxAge(final int expiry) {
- maxAge = Integer.valueOf(expiry);
- return this;
- }
-
- /**
- * Returns the path on the server
- * to which the browser returns this cookie. The
- * cookie is visible to all subpaths on the server.
- */
- public String getPath() {
- return path;
- }
-
- /**
- * Specifies a path for the cookie
- * to which the client should return the cookie.
- *
- * The cookie is visible to all the pages in the directory
- * you specify, and all the pages in that directory's subdirectories.
- * A cookie's path must include the servlet that set the cookie,
- * for example, /catalog , which makes the cookie
- * visible to all directories on the server under /catalog .
- *
- *
Consult RFC 2109 (available on the Internet) for more
- * information on setting path names for cookies.
- */
- public Cookie setPath(final String uri) {
- path = uri;
- return this;
- }
-
- /**
- * Returns true
if the browser is sending cookies
- * only over a secure protocol, or false
if the
- * browser can send cookies using any protocol.
- */
- public boolean isSecure() {
- return secure;
- }
-
- /**
- * Indicates to the browser whether the cookie should only be sent
- * using a secure protocol, such as HTTPS or SSL.
- */
- public Cookie setSecure(final boolean flag) {
- secure = flag;
- return this;
- }
-
- /**
- * Returns the name of the cookie. The name cannot be changed after
- * creation.
- */
- public String getName() {
- return name;
- }
-
- /**
- * Returns the value of the cookie.
- */
- public String getValue() {
- return value;
- }
-
- /**
- * Assigns a new value to a cookie after the cookie is created.
- * If you use a binary value, you may want to use BASE64 encoding.
- */
- public Cookie setValue(final String newValue) {
- value = newValue;
- return this;
- }
-
- /**
- * Returns the version of the protocol this cookie complies
- * with. Version 1 complies with RFC 2109,
- * and version 0 complies with the original
- * cookie specification drafted by Netscape. Cookies provided
- * by a browser use and identify the browser's cookie version.
- */
- public Integer getVersion() {
- return version;
- }
-
- /**
- * Sets the version of the cookie protocol this cookie complies
- * with. Version 0 complies with the original Netscape cookie
- * specification. Version 1 complies with RFC 2109.
- */
- public Cookie setVersion(final int version) {
- this.version = Integer.valueOf(version);
- return this;
- }
-
- public boolean isHttpOnly() {
- return httpOnly;
- }
-
- public Cookie setHttpOnly(final boolean httpOnly) {
- this.httpOnly = httpOnly;
- return this;
- }
-
- public String getExpires() {
- return expires;
- }
-
- public Cookie setExpires(final String expires) {
- this.expires = expires;
- return this;
- }
-
- @Override
- public String toString() {
- StringBuilder cookie = new StringBuilder();
-
- cookie.append(name).append('=').append(value);
-
- if (maxAge != null) {
- cookie.append("; Max-Age=").append(maxAge);
- }
- if (expires != null) {
- cookie.append("; Expires=").append(expires);
- }
- if (comment != null) {
- cookie.append("; Comment=").append(comment);
- }
- if (domain != null) {
- cookie.append("; Domain=").append(domain);
- }
- if (path != null) {
- cookie.append("; Path=").append(path);
- }
- if (secure) {
- cookie.append("; Secure");
- }
- if (version != null) {
- cookie.append("; Version=").append(version);
- }
- if (httpOnly) {
- cookie.append("; HttpOnly");
- }
-
- return cookie.toString();
- }
-
-}
diff --git a/jodd-http/src/main/java/jodd/http/HeadersMultiMap.java b/jodd-http/src/main/java/jodd/http/HeadersMultiMap.java
deleted file mode 100644
index 6ae0b8119..000000000
--- a/jodd-http/src/main/java/jodd/http/HeadersMultiMap.java
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import java.util.List;
-
-public class HeadersMultiMap extends HttpMultiMap {
-
- protected HeadersMultiMap() {
- super(false);
- }
-
- /**
- * Adds new header value. If existing value exist, it will be removed
- * so the store the new key value.
- */
- public void addHeader(final String name, final String value) {
- List valuesList = super.getAll(name);
- if (valuesList.isEmpty()) {
- super.add(name, value);
- return;
- }
- super.remove(name);
- valuesList.add(value);
- super.addAll(name, valuesList);
- }
-
- public void setHeader(final String name, final String value) {
- super.set(name, value);
- }
-
- public String getHeader(final String name) {
- return super.get(name);
- }
-}
diff --git a/jodd-http/src/main/java/jodd/http/HttpBase.java b/jodd-http/src/main/java/jodd/http/HttpBase.java
deleted file mode 100644
index 9af4448c4..000000000
--- a/jodd-http/src/main/java/jodd/http/HttpBase.java
+++ /dev/null
@@ -1,1099 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.http.up.ByteArrayUploadable;
-import jodd.http.up.FileUploadable;
-import jodd.http.up.Uploadable;
-import jodd.io.FastCharArrayWriter;
-import jodd.io.FileNameUtil;
-import jodd.io.IOUtil;
-import jodd.io.upload.FileUpload;
-import jodd.io.upload.MultipartStreamParser;
-import jodd.net.MimeTypes;
-import jodd.time.TimeUtil;
-import jodd.util.RandomString;
-import jodd.util.StringPool;
-import jodd.util.StringUtil;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-import static jodd.util.StringPool.CRLF;
-
-/**
- * Base class for {@link HttpRequest} and {@link HttpResponse}.
- */
-public abstract class HttpBase {
-
- public static class Defaults {
-
- public static final int DEFAULT_PORT = -1;
-
- /**
- * Default HTTP query parameters encoding (UTF-8).
- */
- public static String queryEncoding = "UTF-8";
- /**
- * Default form encoding (UTF-8).
- */
- public static String formEncoding = "UTF-8";
- /**
- * Default body media type.
- */
- public static String bodyMediaType = MimeTypes.MIME_TEXT_HTML;
- /**
- * Default body encoding (UTF-8).
- */
- public static String bodyEncoding = "UTF-8";
- /**
- * Default user agent value.
- */
- public static String userAgent = "Jodd HTTP";
- /**
- * Flag that controls if headers should be rewritten and capitalized in PascalCase.
- * When disabled, header keys are used as they are passed.
- * When flag is enabled, header keys will be capitalized.
- */
- public static boolean capitalizeHeaderKeys = true;
-
- }
-
- public static final String HEADER_ACCEPT = "Accept";
- public static final String HEADER_AUTHORIZATION = "Authorization";
- public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
- public static final String HEADER_CONTENT_TYPE = "Content-Type";
- public static final String HEADER_CONTENT_LENGTH = "Content-Length";
- public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
- public static final String HEADER_HOST = "Host";
- public static final String HEADER_ETAG = "ETag";
- public static final String HEADER_CONNECTION = "Connection";
- public static final String HEADER_KEEP_ALIVE = "Keep-Alive";
- public static final String HEADER_CLOSE = "Close";
- public static final String HTTP_1_0 = "HTTP/1.0";
- public static final String HTTP_1_1 = "HTTP/1.1";
-
- protected String httpVersion = HTTP_1_1;
- protected boolean capitalizeHeaderKeys = Defaults.capitalizeHeaderKeys;
- protected final HeadersMultiMap headers = new HeadersMultiMap();
-
- protected HttpMultiMap> form; // holds form data (when used)
- protected String body; // holds raw body string (always)
-
- @SuppressWarnings("unchecked")
- protected T _this() {
- return (T) this;
- }
-
- // ---------------------------------------------------------------- properties
-
- /**
- * Returns HTTP version string. By default it's "HTTP/1.1".
- */
- public String httpVersion() {
- return httpVersion;
- }
-
- /**
- * Sets the HTTP version string. Must be formed like "HTTP/1.1".
- */
- public T httpVersion(final String httpVersion) {
- this.httpVersion = httpVersion;
- return _this();
- }
-
- /**
- * Returns whether header keys should be strict or not, when they are
- * modified by changing them to PascalCase.
- * @see Defaults#capitalizeHeaderKeys
- */
- public boolean capitalizeHeaderKeys() {
- return capitalizeHeaderKeys;
- }
-
- /**
- * Sets headers behavior.
- * @see Defaults#capitalizeHeaderKeys
- */
- public T capitalizeHeaderKeys(final boolean capitalizeHeaderKeys) {
- this.capitalizeHeaderKeys = capitalizeHeaderKeys;
- return _this();
- }
-
- // ---------------------------------------------------------------- headers
-
- /**
- * Returns value of header parameter.
- * If multiple headers with the same names exist,
- * the first value will be returned. Returns null
- * if header doesn't exist.
- */
- public String header(final String name) {
- return headers.getHeader(name);
- }
-
- /**
- * Returns all values for given header name.
- */
- public List headers(final String name) {
- return headers.getAll(name);
- }
-
- /**
- * Removes all header parameters for given name.
- */
- public void headerRemove(final String name) {
- headers.remove(name.trim());
- }
-
- /**
- * Adds header parameter. If a header with the same name exist,
- * it will not be overwritten, but the new header with the same
- * name is going to be added.
- * The order of header parameters is preserved.
- * Also detects 'Content-Type' header and extracts
- * {@link #mediaType() media type} and {@link #charset() charset}
- * values.
- */
- public T header(final String name, final String value) {
- return _header(name, value, false);
- }
-
- /**
- * Adds many header parameters at once.
- * @see #header(String, String)
- */
- public T header(final Map headerMap) {
- for (final Map.Entry entry : headerMap.entrySet()) {
- header(entry.getKey(), entry.getValue());
- }
- return _this();
- }
-
- /**
- * Sets the header by overwriting it.
- * @see #header(String, String)
- */
- public T headerOverwrite(final String name, final String value) {
- return _header(name, value, true);
- }
-
- /**
- * Adds or sets header parameter.
- * @see #header(String, String)
- */
- protected T _header(final String name, String value, final boolean overwrite) {
- final String key = name.trim();
-
- if (key.equalsIgnoreCase(HEADER_CONTENT_TYPE)) {
- value = value.trim();
-
- mediaType = HttpUtil.extractMediaType(value);
- charset = HttpUtil.extractContentTypeCharset(value);
- }
-
- _headerRaw(name, value, overwrite);
-
- return _this();
- }
-
- /**
- * Internal direct header setting.
- */
- protected void _headerRaw(String name, String value, final boolean overwrite) {
- name = name.trim();
- value = value.trim();
-
- if (overwrite) {
- headers.setHeader(name, value);
- } else {
- headers.addHeader(name, value);
- }
- }
-
- /**
- * Adds int
value as header parameter,
- * @see #header(String, String)
- */
- public T header(final String name, final int value) {
- _headerRaw(name, String.valueOf(value), false);
- return _this();
- }
-
- /**
- * Adds date value as header parameter.
- * @see #header(String, String)
- */
- public T header(final String name, final long millis) {
- _headerRaw(name, TimeUtil.formatHttpDate(millis), false);
- return _this();
- }
-
- /**
- * Returns collection of all header names. Depends on
- * {@link #capitalizeHeaderKeys()} flag.
- */
- public Collection headerNames() {
- return headers.names();
- }
-
- /**
- * Returns Bearer token or {@code null} if not set.
- */
- public String tokenAuthentication() {
- final String value = headers.get(HEADER_AUTHORIZATION);
- if (value == null) {
- return null;
- }
- final int ndx = value.indexOf("Bearer ");
- if (ndx == -1) {
- return null;
- }
- return value.substring(ndx + 7).trim();
- }
-
-
- // ---------------------------------------------------------------- content type
-
- protected String charset;
-
- /**
- * Returns charset, as defined by 'Content-Type' header.
- * If not set, returns null
- indicating
- * the default charset (ISO-8859-1).
- */
- public String charset() {
- return charset;
- }
-
- /**
- * Defines just content type charset. Setting this value to
- * null
will remove the charset information from
- * the header.
- */
- public T charset(final String charset) {
- this.charset = null;
- contentType(null, charset);
- return _this();
- }
-
-
- protected String mediaType;
-
- /**
- * Returns media type, as defined by 'Content-Type' header.
- * If not set, returns null
- indicating
- * the default media type, depending on request/response.
- */
- public String mediaType() {
- return mediaType;
- }
-
- /**
- * Defines just content media type.
- * Setting this value to null
will
- * not have any effects.
- */
- public T mediaType(final String mediaType) {
- contentType(mediaType, null);
- return _this();
- }
-
- /**
- * Returns full "Content-Type" header.
- * It consists of {@link #mediaType() media type}
- * and {@link #charset() charset}.
- */
- public String contentType() {
- return header(HEADER_CONTENT_TYPE);
- }
-
- /**
- * Sets full "Content-Type" header. Both {@link #mediaType() media type}
- * and {@link #charset() charset} are overridden.
- */
- public T contentType(final String contentType) {
- headerOverwrite(HEADER_CONTENT_TYPE, contentType);
- return _this();
- }
-
- /**
- * Sets "Content-Type" header by defining media-type and/or charset parameter.
- * This method may be used to update media-type and/or charset by passing
- * non-null
value for changes.
- *
- * Important: if Content-Type header has some other parameters, they will be removed!
- */
- public T contentType(String mediaType, String charset) {
- if (mediaType == null) {
- mediaType = this.mediaType;
- } else {
- this.mediaType = mediaType;
- }
-
- if (charset == null) {
- charset = this.charset;
- } else {
- this.charset = charset;
- }
-
- String contentType = mediaType;
- if (charset != null) {
- contentType += ";charset=" + charset;
- }
-
- _headerRaw(HEADER_CONTENT_TYPE, contentType, true);
- return _this();
- }
-
- // ---------------------------------------------------------------- keep-alive
-
- /**
- * Defines "Connection" header as "Keep-Alive" or "Close".
- * Existing value is overwritten.
- */
- public T connectionKeepAlive(final boolean keepAlive) {
- if (keepAlive) {
- headerOverwrite(HEADER_CONNECTION, HEADER_KEEP_ALIVE);
- } else {
- headerOverwrite(HEADER_CONNECTION, HEADER_CLOSE);
- }
- return _this();
- }
-
- /**
- * Returns true
if connection is persistent.
- * If "Connection" header does not exist, returns true
- * for HTTP 1.1 and false
for HTTP 1.0. If
- * "Connection" header exist, checks if it is equal to "Close".
- *
- * In HTTP 1.1, all connections are considered persistent unless declared otherwise.
- * Under HTTP 1.0, there is no official specification for how keepalive operates.
- */
- public boolean isConnectionPersistent() {
- final String connection = header(HEADER_CONNECTION);
- if (connection == null) {
- return !httpVersion.equalsIgnoreCase(HTTP_1_0);
- }
-
- return !connection.equalsIgnoreCase(HEADER_CLOSE);
- }
-
- // ---------------------------------------------------------------- common headers
-
- /**
- * Returns full "Content-Length" header or
- * null
if not set. Returned value is raw and unchecked, exactly the same
- * as it was specified or received. It may be even invalid.
- */
- public String contentLength() {
- return header(HEADER_CONTENT_LENGTH);
- }
-
- /**
- * Sets the full "Content-Length" header.
- */
- public T contentLength(final int value) {
- _headerRaw(HEADER_CONTENT_LENGTH, String.valueOf(value), true);
- return _this();
- }
-
- /**
- * Returns "Content-Encoding" header.
- */
- public String contentEncoding() {
- return header(HEADER_CONTENT_ENCODING);
- }
-
- /**
- * Returns "Accept" header.
- */
- public String accept() {
- return header(HEADER_ACCEPT);
- }
-
- /**
- * Sets "Accept" header.
- */
- public T accept(final String encodings) {
- headerOverwrite(HEADER_ACCEPT, encodings);
- return _this();
- }
-
- /**
- * Returns "Accept-Encoding" header.
- */
- public String acceptEncoding() {
- return header(HEADER_ACCEPT_ENCODING);
- }
-
- /**
- * Sets "Accept-Encoding" header.
- */
- public T acceptEncoding(final String encodings) {
- headerOverwrite(HEADER_ACCEPT_ENCODING, encodings);
- return _this();
- }
-
- // ---------------------------------------------------------------- form
-
- /**
- * Initializes form.
- */
- protected void initForm() {
- if (form == null) {
- form = HttpMultiMap.newCaseInsensitiveMap();
- }
- }
-
- /**
- * Wraps non-Strings form values with {@link jodd.http.up.Uploadable uploadable content}.
- * Detects invalid types and throws an exception. So all uploadable values
- * are of the same type.
- */
- protected Object wrapFormValue(final Object value) {
- if (value == null) {
- return null;
- }
- if (value instanceof CharSequence) {
- return value.toString();
- }
- if (value instanceof Number) {
- return value.toString();
- }
- if (value instanceof Boolean) {
- return value.toString();
- }
- if (value instanceof File) {
- return new FileUploadable((File) value);
- }
- if (value instanceof byte[]) {
- return new ByteArrayUploadable((byte[]) value, null);
- }
- if (value instanceof Uploadable) {
- return value;
- }
-
- throw new HttpException("Unsupported value type: " + value.getClass().getName());
- }
-
- /**
- * Adds the form parameter. Existing parameter will not be overwritten.
- */
- public T form(final String name, Object value) {
- initForm();
-
- value = wrapFormValue(value);
- ((HttpMultiMap)form).add(name, value);
-
- return _this();
- }
-
- /**
- * Sets form parameter by overwriting.
- */
- public T formOverwrite(final String name, Object value) {
- initForm();
-
- value = wrapFormValue(value);
- ((HttpMultiMap)form).set(name, value);
-
- return _this();
- }
-
- /**
- * Sets many form parameters at once.
- */
- public T form(String name, final Object value, final Object... parameters) {
- initForm();
-
- form(name, value);
-
- for (int i = 0; i < parameters.length; i += 2) {
- name = parameters[i].toString();
-
- form(name, parameters[i + 1]);
- }
- return _this();
- }
-
- /**
- * Sets many form parameters at once.
- */
- public T form(final Map formMap) {
- initForm();
-
- for (final Map.Entry entry : formMap.entrySet()) {
- form(entry.getKey(), entry.getValue());
- }
- return _this();
- }
-
- /**
- * Return map of form parameters.
- * Note that all uploadable values are wrapped with {@link jodd.http.up.Uploadable}.
- */
- public HttpMultiMap> form() {
- return form;
- }
-
- // ---------------------------------------------------------------- form encoding
-
- protected String formEncoding = Defaults.formEncoding;
-
- /**
- * Defines encoding for forms parameters. Default value is
- * copied from {@link Defaults#formEncoding}.
- * It is overridden by {@link #charset() charset} value.
- */
- public T formEncoding(final String encoding) {
- this.formEncoding = encoding;
- return _this();
- }
-
- // ---------------------------------------------------------------- cookies
-
- /**
- * Parses cookie information from the header.
- */
- public Cookie[] cookies() {
- final String cookieHeader = header("cookie");
- if (!StringUtil.isNotBlank(cookieHeader)) {
- return new Cookie[0];
- }
-
- return Arrays
- .stream(StringUtil.splitc(cookieHeader, ';'))
- .map(Cookie::new)
- .toArray(Cookie[]::new);
- }
-
- // ---------------------------------------------------------------- body
-
- /**
- * Returns raw body as received or set (always in ISO-8859-1 encoding).
- * If body content is a text, use {@link #bodyText()} to get it converted.
- * Returns null
if body is not specified!
- */
- public String body() {
- return body;
- }
-
- /**
- * Returns raw body bytes. Returns null
if body is not specified.
- */
- public byte[] bodyBytes() {
- if (body == null) {
- return null;
- }
- return body.getBytes(StandardCharsets.ISO_8859_1);
- }
-
- /**
- * Returns {@link #body() body content} as text. If {@link #charset() charset parameter}
- * of "Content-Type" header is defined, body string charset is converted, otherwise
- * the same raw body content is returned. Never returns null
.
- */
- public String bodyText() {
- if (body == null) {
- return StringPool.EMPTY;
- }
- if (charset != null) {
- return StringUtil.convertCharset(body, StandardCharsets.ISO_8859_1, Charset.forName(charset));
- }
- return body();
- }
-
- /**
- * Sets raw body content and discards all form parameters.
- * Important: body string is in RAW format, meaning, ISO-8859-1 encoding.
- * Also sets "Content-Length" parameter. However, "Content-Type" is not set
- * and it is expected from user to set this one.
- */
- public T body(final String body) {
- this.body = body;
- this.form = null;
- contentLength(body.length());
- return _this();
- }
-
- /**
- * Defines body text and content type (as media type and charset).
- * Body string will be converted to {@link #body(String) raw body string}
- * and "Content-Type" header will be set.
- */
- public T bodyText(String body, final String mediaType, final String charset) {
- body = StringUtil.convertCharset(body, Charset.forName(charset), StandardCharsets.ISO_8859_1);
- contentType(mediaType, charset);
- body(body);
- return _this();
- }
-
- /**
- * Defines {@link #bodyText(String, String, String) body text content}
- * that will be encoded in {@link Defaults#bodyEncoding default body encoding}.
- */
- public T bodyText(final String body, final String mediaType) {
- return bodyText(body, mediaType, charset != null ? charset : Defaults.bodyEncoding);
- }
- /**
- * Defines {@link #bodyText(String, String, String) body text content}
- * that will be encoded as {@link Defaults#bodyMediaType default body media type}
- * in {@link Defaults#bodyEncoding default body encoding} if missing.
- */
- public T bodyText(final String body) {
- return bodyText(
- body,
- mediaType != null ? mediaType : Defaults.bodyMediaType,
- charset != null ? charset : Defaults.bodyEncoding);
- }
-
- /**
- * Sets raw body content and discards form parameters.
- * Also sets "Content-Length" and "Content-Type" parameter.
- * @see #body(String)
- */
- public T body(final byte[] content, final String contentType) {
- String body = null;
- try {
- body = new String(content, StandardCharsets.ISO_8859_1.name());
- } catch (final UnsupportedEncodingException ignore) {
- }
- contentType(contentType);
- return body(body);
- }
-
- // ---------------------------------------------------------------- body form
-
- protected boolean multipart = false;
-
- /**
- * Returns true
if form contains {@link jodd.http.up.Uploadable}.
- */
- protected boolean isFormMultipart() {
- if (multipart) {
- return true;
- }
-
- for (final Map.Entry entry : form) {
- final Object value = entry.getValue();
- if (value instanceof Uploadable) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Creates form {@link jodd.http.Buffer buffer} and sets few headers.
- */
- protected Buffer formBuffer() {
- final Buffer buffer = new Buffer();
- if (form == null || form.isEmpty()) {
- return buffer;
- }
-
- if (!isFormMultipart()) {
- final String formEncoding = resolveFormEncoding();
-
- // encode
- final String formQueryString = HttpUtil.buildQuery(form, formEncoding);
-
- contentType("application/x-www-form-urlencoded", null);
- contentLength(formQueryString.length());
-
- buffer.append(formQueryString);
- return buffer;
- }
-
- final String boundary = StringUtil.repeat('-', 10) + RandomString.get().randomAlphaNumeric(10);
-
- for (final Map.Entry entry : form) {
-
- buffer.append("--");
- buffer.append(boundary);
- buffer.append(CRLF);
-
- final String name = entry.getKey();
- final Object value = entry.getValue();
-
- if (value instanceof String) {
- final String string = (String) value;
- buffer.append("Content-Disposition: form-data; name=\"").append(name).append('"').append(CRLF);
- buffer.append(CRLF);
-
- final String formEncoding = resolveFormEncoding();
-
- final String utf8String = StringUtil.convertCharset(
- string, Charset.forName(formEncoding), StandardCharsets.ISO_8859_1);
-
- buffer.append(utf8String);
- }
- else if (value instanceof Uploadable) {
- final Uploadable uploadable = (Uploadable) value;
-
- String fileName = uploadable.getFileName();
- if (fileName == null) {
- fileName = name;
- } else {
- final String formEncoding = resolveFormEncoding();
-
- fileName = StringUtil.convertCharset(
- fileName, Charset.forName(formEncoding), StandardCharsets.ISO_8859_1);
- }
-
- buffer.append("Content-Disposition: form-data; name=\"").append(name);
- buffer.append("\"; filename=\"").append(fileName).append('"').append(CRLF);
-
- String mimeType = uploadable.getMimeType();
- if (mimeType == null) {
- mimeType = MimeTypes.getMimeType(FileNameUtil.getExtension(fileName));
- }
- buffer.append(HEADER_CONTENT_TYPE).append(": ").append(mimeType).append(CRLF);
-
- buffer.append("Content-Transfer-Encoding: binary").append(CRLF);
- buffer.append(CRLF);
-
- buffer.append(uploadable);
-
- //byte[] bytes = uploadable.getBytes();
- //for (byte b : bytes) {
- //buffer.append(CharUtil.toChar(b));
- //}
- } else {
- // should never happened!
- throw new HttpException("Unsupported type");
- }
- buffer.append(CRLF);
- }
-
- buffer.append("--").append(boundary).append("--");
- buffer.append(CRLF);
-
- // the end
- contentType("multipart/form-data; boundary=" + boundary);
- contentLength(buffer.size());
-
- return buffer;
- }
-
- /**
- * Resolves form encodings.
- */
- protected String resolveFormEncoding() {
- // determine form encoding
- String formEncoding = charset;
-
- if (formEncoding == null) {
- formEncoding = this.formEncoding;
- }
- return formEncoding;
- }
-
- // ---------------------------------------------------------------- buffer
-
- /**
- * Returns string representation of this request or response.
- */
- @Override
- public String toString() {
- return toString(true);
- }
-
- /**
- * Returns full request/response, or just headers.
- * Useful for debugging.
- */
- public String toString(final boolean fullResponse) {
- final Buffer buffer = buffer(fullResponse);
-
- final StringWriter stringWriter = new StringWriter();
-
- try {
- buffer.writeTo(stringWriter);
- }
- catch (final IOException ioex) {
- throw new HttpException(ioex);
- }
-
- return stringWriter.toString();
- }
-
- /**
- * Returns byte array of request or response.
- */
- public byte[] toByteArray() {
- final Buffer buffer = buffer(true);
-
- final ByteArrayOutputStream baos = new ByteArrayOutputStream(buffer.size());
-
- try {
- buffer.writeTo(baos);
- }
- catch (final IOException ioex) {
- throw new HttpException(ioex);
- }
-
- return baos.toByteArray();
- }
-
- /**
- * Creates {@link jodd.http.Buffer buffer} ready to be consumed.
- * Buffer can, optionally, contains just headers.
- */
- protected abstract Buffer buffer(boolean full);
-
- protected void populateHeaderAndBody(final Buffer target, final Buffer formBuffer, final boolean fullRequest) {
- for (final String name : headers.names()) {
- final List values = headers.getAll(name);
-
- final String key = capitalizeHeaderKeys ? HttpUtil.prepareHeaderParameterName(name) : name;
-
- target.append(key);
- target.append(": ");
- int count = 0;
-
- for (final String value : values) {
- if (count++ > 0) {
- target.append(", ");
- }
- target.append(value);
- }
-
- target.append(CRLF);
- }
-
- if (fullRequest) {
- target.append(CRLF);
-
- if (form != null) {
- target.append(formBuffer);
- } else if (body != null) {
- target.append(body);
- }
- }
- }
-
-
- // ---------------------------------------------------------------- send
-
- protected HttpProgressListener httpProgressListener;
-
- /**
- * Sends request or response to output stream.
- */
- public void sendTo(final OutputStream out) throws IOException {
- final Buffer buffer = buffer(true);
-
- if (httpProgressListener == null) {
- buffer.writeTo(out);
- }
- else {
- buffer.writeTo(out, httpProgressListener);
- }
-
- out.flush();
- }
-
- // ---------------------------------------------------------------- parsing
-
- /**
- * Parses headers.
- */
- protected void readHeaders(final BufferedReader reader) {
- while (true) {
- final String line;
- try {
- line = reader.readLine();
- } catch (final IOException ioex) {
- throw new HttpException(ioex);
- }
-
- if (StringUtil.isBlank(line)) {
- break;
- }
-
- final int ndx = line.indexOf(':');
- if (ndx != -1) {
- header(line.substring(0, ndx), line.substring(ndx + 1));
- } else {
- throw new HttpException("Invalid header: " + line);
- }
- }
- }
-
- /**
- * Parses body.
- */
- protected void readBody(final BufferedReader reader) {
- String bodyString = null;
-
- // first determine if chunked encoding is specified
- boolean isChunked = false;
-
- final String transferEncoding = header("Transfer-Encoding");
- if (transferEncoding != null && transferEncoding.equalsIgnoreCase("chunked")) {
- isChunked = true;
- }
-
-
- // content length
- final String contentLen = contentLength();
- int contentLenValue = -1;
-
- if (contentLen != null && !isChunked) {
- contentLenValue = Integer.parseInt(contentLen);
-
- if (contentLenValue > 0) {
- final FastCharArrayWriter fastCharArrayWriter = new FastCharArrayWriter(contentLenValue);
-
- try {
- IOUtil.copy(reader, fastCharArrayWriter, contentLenValue);
- } catch (final IOException ioex) {
- throw new HttpException(ioex);
- }
-
- bodyString = fastCharArrayWriter.toString();
- }
- }
-
- // chunked encoding
- if (isChunked) {
-
- final FastCharArrayWriter fastCharArrayWriter = new FastCharArrayWriter();
-
- try {
- while (true) {
- final String line = reader.readLine();
-
- final int len;
- try {
- len = Integer.parseInt(line, 16);
- } catch (final NumberFormatException nfex) {
- throw new HttpException("Invalid chunk length: " + line);
- }
-
- if (len > 0) {
- IOUtil.copy(reader, fastCharArrayWriter, len);
- reader.readLine();
- } else {
- // end reached, read trailing headers, if there is any
- readHeaders(reader);
- break;
- }
- }
- } catch (final IOException ioex) {
- throw new HttpException(ioex);
- }
-
- bodyString = fastCharArrayWriter.toString();
- }
-
- // no body yet - special case
- if (bodyString == null && contentLenValue != 0) {
- // body ends when stream closes
- final FastCharArrayWriter fastCharArrayWriter = new FastCharArrayWriter();
- try {
- IOUtil.copy(reader, fastCharArrayWriter);
- } catch (final IOException ioex) {
- throw new HttpException(ioex);
- }
- bodyString = fastCharArrayWriter.toString();
- }
-
- // BODY READY - PARSE BODY
- String charset = this.charset;
- if (charset == null) {
- charset = StandardCharsets.ISO_8859_1.name();
- }
- body = bodyString;
-
- String mediaType = mediaType();
-
- if (mediaType == null) {
- mediaType = StringPool.EMPTY;
- } else {
- mediaType = mediaType.toLowerCase();
- }
-
- if (mediaType.equals("application/x-www-form-urlencoded")) {
- form = HttpUtil.parseQuery(bodyString, true);
- return;
- }
-
- if (mediaType.equals("multipart/form-data")) {
- form = HttpMultiMap.newCaseInsensitiveMap();
-
- final MultipartStreamParser multipartParser = new MultipartStreamParser();
-
- try {
- final byte[] bodyBytes = bodyString.getBytes(StandardCharsets.ISO_8859_1.name());
- final ByteArrayInputStream bin = new ByteArrayInputStream(bodyBytes);
- multipartParser.parseRequestStream(bin, charset);
- } catch (final IOException ioex) {
- throw new HttpException(ioex);
- }
-
- // string parameters
- for (final String paramName : multipartParser.getParameterNames()) {
- final String[] values = multipartParser.getParameterValues(paramName);
-
- for (final String value : values) {
- ((HttpMultiMap)form).add(paramName, value);
- }
- }
-
- // file parameters
- for (final String paramName : multipartParser.getFileParameterNames()) {
- final FileUpload[] uploads = multipartParser.getFiles(paramName);
-
- for (final FileUpload upload : uploads) {
- ((HttpMultiMap)form).add(paramName, upload);
- }
- }
-
- return;
- }
-
- // body is a simple content
-
- form = null;
- }
-
-}
diff --git a/jodd-http/src/main/java/jodd/http/HttpBrowser.java b/jodd-http/src/main/java/jodd/http/HttpBrowser.java
deleted file mode 100644
index 36162cb3e..000000000
--- a/jodd-http/src/main/java/jodd/http/HttpBrowser.java
+++ /dev/null
@@ -1,311 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.exception.ExceptionUtil;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Emulates HTTP Browser and persist cookies between requests.
- */
-public class HttpBrowser {
-
- protected HttpConnectionProvider httpConnectionProvider;
- protected HttpRequest httpRequest;
- protected HttpResponse httpResponse;
- protected HttpMultiMap cookies = HttpMultiMap.newCaseInsensitiveMap();
- protected HeadersMultiMap defaultHeaders = new HeadersMultiMap();
- protected boolean keepAlive;
- protected long elapsedTime;
- protected boolean catchTransportExceptions = true;
-
- public HttpBrowser() {
- httpConnectionProvider = HttpConnectionProvider.get();
- }
-
- /**
- * Returns true
if keep alive is used.
- */
- public boolean isKeepAlive() {
- return keepAlive;
- }
-
- /**
- * Defines that persistent HTTP connection should be used.
- */
- public HttpBrowser setKeepAlive(final boolean keepAlive) {
- this.keepAlive = keepAlive;
- return this;
- }
-
- /**
- * Defines if transport exceptions should be thrown.
- */
- public HttpBrowser setCatchTransportExceptions(final boolean catchTransportExceptions) {
- this.catchTransportExceptions = catchTransportExceptions;
- return this;
- }
-
- /**
- * Defines proxy for a browser.
- */
- public HttpBrowser setProxyInfo(final ProxyInfo proxyInfo) {
- httpConnectionProvider.useProxy(proxyInfo);
- return this;
- }
-
- /**
- * Defines {@link jodd.http.HttpConnectionProvider} for this browser session.
- * Resets the previous proxy definition, if set.
- */
- public HttpBrowser setHttpConnectionProvider(final HttpConnectionProvider httpConnectionProvider) {
- this.httpConnectionProvider = httpConnectionProvider;
- return this;
- }
-
- /**
- * Adds default header to all requests.
- */
- public HttpBrowser setDefaultHeader(final String name, final String value) {
- defaultHeaders.addHeader(name, value);
- return this;
- }
-
- /**
- * Returns last used request.
- */
- public HttpRequest getHttpRequest() {
- return httpRequest;
- }
-
- /**
- * Returns last received {@link HttpResponse HTTP response} object.
- */
- public HttpResponse getHttpResponse() {
- return httpResponse;
- }
-
- /**
- * Returns last response HTML page.
- */
- public String getPage() {
- if (httpResponse == null) {
- return null;
- }
- return httpResponse.bodyText();
- }
-
-
- /**
- * Sends new request as a browser. Before sending,
- * all browser cookies are added to the request.
- * After sending, the cookies are read from the response.
- * Moreover, status codes 301 and 302 are automatically
- * handled. Returns very last response.
- */
- public HttpResponse sendRequest(HttpRequest httpRequest) {
- elapsedTime = System.currentTimeMillis();
-
- // send request
-
- httpRequest.followRedirects(false);
-
- // default setting
-
- final boolean verifyHttpsHost = httpRequest.verifyHttpsHost();
- final boolean trustAllCerts = httpRequest.trustAllCertificates();
-
- while (true) {
- this.httpRequest = httpRequest;
- final HttpResponse previousResponse = this.httpResponse;
- this.httpResponse = null;
-
- addDefaultHeaders(httpRequest);
- addCookies(httpRequest);
-
- httpRequest.verifyHttpsHost(verifyHttpsHost);
- httpRequest.trustAllCerts(trustAllCerts);
-
- // send request
- if (catchTransportExceptions) {
- try {
- this.httpResponse = _sendRequest(httpRequest, previousResponse);
- }
- catch (final HttpException httpException) {
- httpResponse = new HttpResponse();
- httpResponse.assignHttpRequest(httpRequest);
- httpResponse.statusCode(503);
- httpResponse.statusPhrase("Service unavailable. " + ExceptionUtil.message(httpException));
- }
- }
- else {
- this.httpResponse =_sendRequest(httpRequest, previousResponse);
- }
-
- readCookies(httpResponse);
-
- final int statusCode = httpResponse.statusCode();
-
- // 301: moved permanently
- if (statusCode == 301) {
- final String newPath = httpResponse.location();
-
- if (newPath == null) {
- break;
- }
-
- httpRequest = HttpRequest.get(newPath);
- continue;
- }
-
- // 302: redirect, 303: see other
- if (statusCode == 302 || statusCode == 303) {
- final String newPath = httpResponse.location();
-
- if (newPath == null) {
- break;
- }
-
- httpRequest = HttpRequest.get(newPath);
- continue;
- }
-
- // 307: temporary redirect, 308: permanent redirect
- if (statusCode == 307 || statusCode == 308) {
- final String newPath = httpResponse.location();
-
- if (newPath == null) {
- break;
- }
-
- final String originalMethod = httpRequest.method();
- httpRequest = new HttpRequest()
- .method(originalMethod)
- .set(newPath);
- continue;
- }
-
- break;
- }
-
- elapsedTime = System.currentTimeMillis() - elapsedTime;
-
- return this.httpResponse;
- }
-
- /**
- * Opens connection and sends a response.
- */
- protected HttpResponse _sendRequest(final HttpRequest httpRequest, final HttpResponse previouseResponse) {
- if (!keepAlive) {
- httpRequest.open(httpConnectionProvider);
- } else {
- // keeping alive
- if (previouseResponse == null) {
- httpRequest.open(httpConnectionProvider).connectionKeepAlive(true);
- } else {
- httpRequest.keepAlive(previouseResponse, true);
- }
- }
-
- return httpRequest.send();
- }
-
- /**
- * Add default headers to the request. If request already has a header set,
- * default header will be ignored.
- */
- protected void addDefaultHeaders(final HttpRequest httpRequest) {
- for (final Map.Entry entry : defaultHeaders.entries()) {
- final String name = entry.getKey();
-
- if (!httpRequest.headers.contains(name)) {
- httpRequest.headers.add(name, entry.getValue());
- }
- }
- }
-
- /**
- * Returns elapsed time of last {@link #sendRequest(HttpRequest)} in milliseconds.
- */
- public long getElapsedTime() {
- return elapsedTime;
- }
-
- // ---------------------------------------------------------------- close
-
- /**
- * Closes browser explicitly, needed when keep-alive connection is used.
- */
- public void close() {
- if (httpResponse != null) {
- httpResponse.close();
- }
- }
-
- // ---------------------------------------------------------------- cookies
-
- /**
- * Deletes all cookies.
- */
- public void clearCookies() {
- cookies.clear();
- }
-
- /**
- * Reads cookies from response and adds to cookies list.
- */
- protected void readCookies(final HttpResponse httpResponse) {
- final Cookie[] newCookies = httpResponse.cookies();
-
- for (final Cookie cookie : newCookies) {
- cookies.set(cookie.getName(), cookie);
- }
- }
-
- /**
- * Add cookies to the request.
- */
- protected void addCookies(final HttpRequest httpRequest) {
- // prepare all cookies
- final List cookiesList = new ArrayList<>();
-
- for (final Cookie cookie : httpRequest.cookies()) {
- cookies.set(cookie.getName(),cookie);
- }
-
- if (!cookies.isEmpty()) {
- for (final Map.Entry cookieEntry : cookies) {
- cookiesList.add(cookieEntry.getValue());
- }
-
- httpRequest.cookies(cookiesList.toArray(new Cookie[0]));
- }
- }
-}
diff --git a/jodd-http/src/main/java/jodd/http/HttpConnection.java b/jodd-http/src/main/java/jodd/http/HttpConnection.java
deleted file mode 100644
index 78a7b0f65..000000000
--- a/jodd-http/src/main/java/jodd/http/HttpConnection.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * Http connection. Created by {@link HttpConnectionProvider}.
- */
-public interface HttpConnection {
-
- /**
- * Initializes http connection after socket is created.
- * Applies configurations, like {@link #setTimeout(int)}.
- */
- public void init() throws IOException;
-
- /**
- * Returns connection output stream.
- */
- public OutputStream getOutputStream() throws IOException;
-
- /**
- * Returns connection input stream.
- */
- public InputStream getInputStream() throws IOException;
-
- /**
- * Closes connection. Ignores all exceptions.
- */
- public void close();
-
- /**
- * Sets the timeout for connections, in milliseconds. With this option set to a non-zero timeout,
- * connection will block for only this amount of time. If the timeout expires, an Exception is raised.
- * The timeout must be > 0. A timeout of zero is interpreted as an infinite timeout.
- */
- void setTimeout(int milliseconds);
-
-}
\ No newline at end of file
diff --git a/jodd-http/src/main/java/jodd/http/HttpConnectionProvider.java b/jodd-http/src/main/java/jodd/http/HttpConnectionProvider.java
deleted file mode 100644
index 210f0b0ee..000000000
--- a/jodd-http/src/main/java/jodd/http/HttpConnectionProvider.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.http.net.SocketHttpConnectionProvider;
-
-import java.io.IOException;
-
-/**
- * Factory for {@link HttpConnection http connections}.
- */
-public interface HttpConnectionProvider {
-
- class Implementation {
- private static HttpConnectionProvider httpConnectionProvider = new SocketHttpConnectionProvider();
-
- public static void set(final HttpConnectionProvider httpConnectionProvider) {
- Implementation.httpConnectionProvider = httpConnectionProvider;
- }
- }
-
- /**
- * Returns default implementation of connection provider.
- */
- static HttpConnectionProvider get() {
- return Implementation.httpConnectionProvider;
- }
-
-
- /**
- * Specifies {@link ProxyInfo proxy} for provide to use.
- */
- public void useProxy(ProxyInfo proxyInfo);
-
- /**
- * Creates new {@link HttpConnection}
- * from {@link jodd.http.HttpRequest request}.
- */
- public HttpConnection createHttpConnection(HttpRequest httpRequest) throws IOException;
-
-}
\ No newline at end of file
diff --git a/jodd-http/src/main/java/jodd/http/HttpException.java b/jodd-http/src/main/java/jodd/http/HttpException.java
deleted file mode 100644
index 553599d89..000000000
--- a/jodd-http/src/main/java/jodd/http/HttpException.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.exception.UncheckedException;
-
-/**
- * HTTP exception.
- */
-public class HttpException extends UncheckedException {
-
- public HttpException(final Throwable t) {
- super(t);
- }
-
- public HttpException(final String message) {
- super(message);
- }
-
- public HttpException(final Object networkObject, final String message) {
- super(networkObject.toString() + ": " + message);
- }
-
- public HttpException(final String message, final Throwable t) {
- super(message, t);
- }
-
- public HttpException(final Object networkObject, final String message, final Throwable t) {
- super(networkObject.toString() + ": " + message, t);
- }
-}
diff --git a/jodd-http/src/main/java/jodd/http/HttpMultiMap.java b/jodd-http/src/main/java/jodd/http/HttpMultiMap.java
deleted file mode 100644
index 00480d1b9..000000000
--- a/jodd-http/src/main/java/jodd/http/HttpMultiMap.java
+++ /dev/null
@@ -1,460 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.TreeSet;
-
-/**
- * General purpose HTTP multi-map. It's optimized Linked-HashMap, designed for
- * small number of items and String
non-null keys. It stores keys
- * in case-sensitive way, but, by default, you can read them in case-insensitive
- * way.
- */
-public class HttpMultiMap implements Iterable> {
-
- private static final int BUCKET_SIZE = 31;
- private final boolean caseSensitive;
-
- @SuppressWarnings("unchecked")
- private final MapEntry[] entries = new MapEntry[BUCKET_SIZE + 1];
- private final MapEntry head = new MapEntry<>(-1, null, null);
-
- /**
- * Creates new case-insensitive multimap.
- */
- public static HttpMultiMap newCaseInsensitiveMap() {
- return new HttpMultiMap<>(false);
- }
- /**
- * Creates new case-insensitive map.
- */
- public static HttpMultiMap newCaseSensitiveMap() {
- return new HttpMultiMap<>(true);
- }
-
- protected HttpMultiMap(final boolean caseSensitive) {
- head.before = head.after = head;
- this.caseSensitive = caseSensitive;
- }
-
- /**
- * Calculates hash value of the input string.
- */
- private int hash(final String name) {
- int h = 0;
- for (int i = name.length() - 1; i >= 0; i--) {
- char c = name.charAt(i);
- if (!caseSensitive) {
- if (c >= 'A' && c <= 'Z') {
- c += 32;
- }
- }
- h = 31 * h + c;
- }
-
- if (h > 0) {
- return h;
- }
- if (h == Integer.MIN_VALUE) {
- return Integer.MAX_VALUE;
- }
- return -h;
- }
-
- /**
- * Calculates bucket index from the hash.
- */
- private static int index(final int hash) {
- return hash & BUCKET_SIZE;
- }
-
- /**
- * Returns true
if two names are the same.
- */
- private boolean eq(final String name1, final String name2) {
- int nameLen = name1.length();
- if (nameLen != name2.length()) {
- return false;
- }
-
- for (int i = nameLen - 1; i >= 0; i--) {
- char c1 = name1.charAt(i);
- char c2 = name2.charAt(i);
-
- if (c1 != c2) {
- if (caseSensitive) {
- return false;
- }
- if (c1 >= 'A' && c1 <= 'Z') {
- c1 += 32;
- }
- if (c2 >= 'A' && c2 <= 'Z') {
- c2 += 32;
- }
- if (c1 != c2) {
- return false;
- }
- }
- }
- return true;
- }
-
- // ---------------------------------------------------------------- basic
-
- /**
- * Returns the number of keys. This is not the number of all elements.
- * Not optimized.
- */
- public int size() {
- return names().size();
- }
-
- /**
- * Clears the map.
- */
- public HttpMultiMap clear() {
- for (int i = 0; i < entries.length; i++) {
- entries[i] = null;
- }
- head.before = head.after = head;
- return this;
- }
-
- /**
- * Returns true
if name exist.
- */
- public boolean contains(final String name) {
- return getEntry(name) != null;
- }
-
- /**
- * Returns true
if map is empty.
- */
- public boolean isEmpty() {
- return head == head.after;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- for (Map.Entry entry : this) {
- sb.append(entry).append('\n');
- }
- return sb.toString();
- }
-
- // ---------------------------------------------------------------- set/add
-
- private HttpMultiMap _set(final Iterable> map) {
- clear();
- for (Map.Entry entry : map) {
- add(entry.getKey(), entry.getValue());
- }
- return this;
- }
-
- public HttpMultiMap setAll(final HttpMultiMap multiMap) {
- return _set(multiMap);
- }
-
- public HttpMultiMap setAll(final Map map) {
- return _set(map.entrySet());
- }
-
- public HttpMultiMap set(final String name, final V value) {
- int h = hash(name);
- int i = index(h);
- _remove(h, i, name);
- _add(h, i, name, value);
- return this;
- }
-
- public HttpMultiMap setAll(final String name, final Iterable values) {
- int h = hash(name);
- int i = index(h);
-
- _remove(h, i, name);
- for (V v : values) {
- _add(h, i, name, v);
- }
-
- return this;
- }
-
- public HttpMultiMap add(final String name, final V value) {
- int h = hash(name);
- int i = index(h);
- _add(h, i, name, value);
- return this;
- }
-
- public HttpMultiMap addAll(final String name, final Iterable values) {
- int h = hash(name);
- int i = index(h);
- for (V value : values) {
- _add(h, i, name, value);
- }
- return this;
- }
-
- public HttpMultiMap addAll(final HttpMultiMap map) {
- for (Map.Entry entry : map.entries()) {
- add(entry.getKey(), entry.getValue());
- }
- return this;
- }
-
- public HttpMultiMap addAll(final Map map) {
- for (Map.Entry entry : map.entrySet()) {
- add(entry.getKey(), entry.getValue());
- }
- return this;
- }
-
- private void _add(final int hash, final int index, final String name, final V value) {
- // update the hash table
- MapEntry e = entries[index];
- MapEntry newEntry;
- entries[index] = newEntry = new MapEntry<>(hash, name, value);
- newEntry.next = e;
-
- // update the linked list
- newEntry.addBefore(head);
- }
-
- // ---------------------------------------------------------------- remove
-
- public HttpMultiMap remove(final String name) {
- int h = hash(name);
- int i = index(h);
- _remove(h, i, name);
- return this;
- }
-
- private void _remove(final int hash, final int index, final String name) {
- MapEntry e = entries[index];
- if (e == null) {
- return;
- }
-
- for (; ; ) {
- if (e.hash == hash && eq(name, e.key)) {
- e.remove();
- MapEntry next = e.next;
- if (next != null) {
- entries[index] = next;
- e = next;
- }
- else {
- entries[index] = null;
- return;
- }
- }
- else {
- break;
- }
- }
-
- for (; ; ) {
- MapEntry next = e.next;
- if (next == null) {
- break;
- }
- if (next.hash == hash && eq(name, next.key)) {
- e.next = next.next;
- next.remove();
- }
- else {
- e = next;
- }
- }
- }
-
- // ---------------------------------------------------------------- get
-
- /**
- * Returns the first value from the map associated with the name.
- * Returns null
if name does not exist or
- * if associated value is null
.
- */
- public V get(final String name) {
- Map.Entry entry = getEntry(name);
-
- if (entry == null) {
- return null;
- }
- return entry.getValue();
- }
-
- /**
- * Returns first entry for given name. Returns null
if entry
- * does not exist.
- */
- public Map.Entry getEntry(final String name) {
- int h = hash(name);
- int i = index(h);
- MapEntry e = entries[i];
- while (e != null) {
- if (e.hash == h && eq(name, e.key)) {
- return e;
- }
-
- e = e.next;
- }
- return null;
- }
-
- /**
- * Returns all values associated with the name.
- */
- public List getAll(final String name) {
- LinkedList values = new LinkedList<>();
-
- int h = hash(name);
- int i = index(h);
- MapEntry e = entries[i];
- while (e != null) {
- if (e.hash == h && eq(name, e.key)) {
- values.addFirst(e.getValue());
- }
- e = e.next;
- }
- return values;
- }
-
- // ---------------------------------------------------------------- iterate
-
- /**
- * Returns iterator of all entries.
- */
- @Override
- public Iterator> iterator() {
- final MapEntry[] e = {head.after};
-
- return new Iterator>() {
- @Override
- public boolean hasNext() {
- return e[0] != head;
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public Map.Entry next() {
- if (!hasNext()) {
- throw new NoSuchElementException("No next() entry in the iteration");
- }
- MapEntry next = e[0];
- e[0] = e[0].after;
- return next;
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- };
- }
-
- public Set names() {
- Set names = new TreeSet<>(caseSensitive ? null : String.CASE_INSENSITIVE_ORDER);
-
- MapEntry e = head.after;
- while (e != head) {
- names.add(e.getKey());
- e = e.after;
- }
- return names;
- }
-
- /**
- * Returns all the entries of this map. Case sensitivity does not influence
- * the returned list, it always contains all of the values.
- */
- public List> entries() {
- List> all = new LinkedList<>();
-
- MapEntry e = head.after;
- while (e != head) {
- all.add(e);
- e = e.after;
- }
- return all;
- }
-
- private static final class MapEntry implements Map.Entry {
- final int hash;
- final String key;
- V value;
- MapEntry next;
- MapEntry before, after;
-
- private MapEntry(final int hash, final String key, final V value) {
- this.hash = hash;
- this.key = key;
- this.value = value;
- }
-
- void remove() {
- before.after = after;
- after.before = before;
- }
-
- void addBefore(final MapEntry e) {
- after = e;
- before = e.before;
- before.after = this;
- after.before = this;
- }
-
- @Override
- public String getKey() {
- return key;
- }
-
- @Override
- public V getValue() {
- return value;
- }
-
- @Override
- public V setValue(final V value) {
- V oldValue = this.value;
- this.value = value;
- return oldValue;
- }
-
- @Override
- public String toString() {
- return getKey() + ": " + getValue();
- }
- }
-}
diff --git a/jodd-http/src/main/java/jodd/http/HttpProgressListener.java b/jodd-http/src/main/java/jodd/http/HttpProgressListener.java
deleted file mode 100644
index bbd0dea2c..000000000
--- a/jodd-http/src/main/java/jodd/http/HttpProgressListener.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-/**
- * Http upload progress listener.
- */
-public abstract class HttpProgressListener {
-
- /**
- * Total size to transfer.
- */
- protected int size;
-
- /**
- * Returns callback size in bytes. By default it returns
- * size of 1 percent of a total size. If returned size
- * is less then 512, it will be rounded to 512.
- * This is also the size of the chunk that is sent over network.
- */
- public int callbackSize(final int size) {
- this.size = size;
-
- int callbackSize = (size + 50) / 100;
-
- if (callbackSize < 512) {
- callbackSize = 512;
- }
-
- return callbackSize;
- }
-
- /**
- * Callback for every sent {@link #callbackSize(int) chunk}.
- * Also called before and after the transfer.
- */
- public abstract void transferred(int len);
-
-}
\ No newline at end of file
diff --git a/jodd-http/src/main/java/jodd/http/HttpRequest.java b/jodd-http/src/main/java/jodd/http/HttpRequest.java
deleted file mode 100644
index 92ddfdd4e..000000000
--- a/jodd-http/src/main/java/jodd/http/HttpRequest.java
+++ /dev/null
@@ -1,1061 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.net.HttpMethod;
-import jodd.net.MimeTypes;
-import jodd.util.Base64;
-import jodd.util.StringBand;
-import jodd.util.StringPool;
-import jodd.util.StringUtil;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.nio.charset.StandardCharsets;
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-import static jodd.util.StringPool.CRLF;
-import static jodd.util.StringPool.SPACE;
-
-/**
- * HTTP request.
- */
-public class HttpRequest extends HttpBase {
-
- protected String protocol = "http";
- protected String host = "localhost";
- protected int port = Defaults.DEFAULT_PORT;
- protected String method = "GET";
- protected String path = StringPool.SLASH;
- protected HttpMultiMap query;
-
- // ---------------------------------------------------------------- init
-
- public HttpRequest() {
- initRequest();
- }
-
- /**
- * Prepares request on creation. By default, it just
- * adds "Connection: Close" header.
- */
- protected void initRequest() {
- connectionKeepAlive(false);
- }
-
- // ---------------------------------------------------------------- properties
-
- /**
- * Returns request host name.
- */
- public String host() {
- return host;
- }
-
- /**
- * Sets request host name.
- */
- public HttpRequest host(final String host) {
- this.host = host;
- if (headers.contains(HEADER_HOST)) {
- headerOverwrite(HEADER_HOST, host);
- }
- return this;
- }
-
- /**
- * Returns used protocol. By default it's "http".
- */
- public String protocol() {
- return protocol;
- }
-
- /**
- * Defines protocol.
- */
- public HttpRequest protocol(final String protocol) {
- this.protocol = protocol;
- return this;
- }
-
- /**
- * Returns request port number. When port is not
- * explicitly defined, returns default port for
- * current protocol.
- */
- public int port() {
- if (port == Defaults.DEFAULT_PORT) {
- if (protocol == null) {
- return 80;
- }
- if (protocol.equalsIgnoreCase("https")) {
- return 443;
- }
- return 80;
- }
- return port;
- }
-
- /**
- * Sets request port number.
- */
- public HttpRequest port(final int port) {
- this.port = port;
- return this;
- }
-
- // ---------------------------------------------------------------- set
-
- /**
- * Sets the destination (method, host, port... ) at once.
- */
- public HttpRequest set(String destination) {
- destination = destination.trim();
-
- // http method, optional
-
- int ndx = destination.indexOf(' ');
-
- if (ndx != -1) {
- final String method = destination.substring(0, ndx).toUpperCase();
-
- try {
- final HttpMethod httpMethod = HttpMethod.valueOf(method);
- this.method = httpMethod.name();
- destination = destination.substring(ndx + 1);
- }
- catch (final IllegalArgumentException ignore) {
- // unknown http method
- }
- }
-
- // protocol
-
- ndx = destination.indexOf("://");
-
- if (ndx != -1) {
- protocol = destination.substring(0, ndx);
- destination = destination.substring(ndx + 3);
- }
-
- // host
-
- ndx = destination.indexOf('/');
-
- if (ndx == -1) {
- ndx = destination.length();
- }
-
- if (ndx != 0) {
-
- String hostToSet = destination.substring(0, ndx);
- destination = destination.substring(ndx);
-
- // port
-
- ndx = hostToSet.indexOf(':');
-
- if (ndx == -1) {
- port = Defaults.DEFAULT_PORT;
- } else {
- port = Integer.parseInt(hostToSet.substring(ndx + 1));
- hostToSet = hostToSet.substring(0, ndx);
- }
-
- host(hostToSet);
- }
-
- // path + query
-
- path(destination);
-
- return this;
- }
-
- // ---------------------------------------------------------------- static factories
-
- /**
- * Generic request builder, usually used when method is a variable.
- * Otherwise, use one of the other static request builder methods.
- */
- public static HttpRequest create(final String method, final String destination) {
- return new HttpRequest()
- .method(method.toUpperCase())
- .set(destination);
- }
-
- /**
- * Builds a CONNECT request.
- */
- public static HttpRequest connect(final String destination) {
- return new HttpRequest()
- .method(HttpMethod.CONNECT)
- .set(destination);
- }
- /**
- * Builds a GET request.
- */
- public static HttpRequest get(final String destination) {
- return new HttpRequest()
- .method(HttpMethod.GET)
- .set(destination);
- }
- /**
- * Builds a POST request.
- */
- public static HttpRequest post(final String destination) {
- return new HttpRequest()
- .method(HttpMethod.POST)
- .set(destination);
- }
- /**
- * Builds a PUT request.
- */
- public static HttpRequest put(final String destination) {
- return new HttpRequest()
- .method(HttpMethod.PUT)
- .set(destination);
- }
- /**
- * Builds a PATCH request.
- */
- public static HttpRequest patch(final String destination) {
- return new HttpRequest()
- .method(HttpMethod.PATCH)
- .set(destination);
- }
- /**
- * Builds a DELETE request.
- */
- public static HttpRequest delete(final String destination) {
- return new HttpRequest()
- .method(HttpMethod.DELETE)
- .set(destination);
- }
- /**
- * Builds a HEAD request.
- */
- public static HttpRequest head(final String destination) {
- return new HttpRequest()
- .method(HttpMethod.HEAD)
- .set(destination);
- }
- /**
- * Builds a TRACE request.
- */
- public static HttpRequest trace(final String destination) {
- return new HttpRequest()
- .method(HttpMethod.TRACE)
- .set(destination);
- }
- /**
- * Builds an OPTIONS request.
- */
- public static HttpRequest options(final String destination) {
- return new HttpRequest()
- .method(HttpMethod.OPTIONS)
- .set(destination);
- }
-
- // ---------------------------------------------------------------- request
-
- /**
- * Returns request method.
- */
- public String method() {
- return method;
- }
-
- /**
- * Specifies request method. It will be converted into uppercase.
- * Does not validate if method is one of the HTTP methods.
- */
- public HttpRequest method(final String method) {
- this.method = method.toUpperCase();
- return this;
- }
- public HttpRequest method(final HttpMethod httpMethod) {
- this.method = httpMethod.name();
- return this;
- }
-
- /**
- * Returns request path, without the query.
- */
- public String path() {
- return path;
- }
-
- /**
- * Sets request path. Query string is allowed.
- * Adds a slash if path doesn't start with one.
- * Query will be stripped out from the path.
- * Previous query is discarded.
- * @see #query()
- */
- public HttpRequest path(String path) {
- // this must be the only place that sets the path
-
- if (!path.startsWith(StringPool.SLASH)) {
- path = StringPool.SLASH + path;
- }
-
- final int ndx = path.indexOf('?');
-
- if (ndx != -1) {
- final String queryString = path.substring(ndx + 1);
-
- path = path.substring(0, ndx);
-
- query = HttpUtil.parseQuery(queryString, true);
- } else {
- query = HttpMultiMap.newCaseInsensitiveMap();
- }
-
- this.path = path;
-
- return this;
- }
-
- /**
- * Forces multipart requests. When set to false
,
- * it will be {@link #isFormMultipart() detected} if request
- * should be multipart. By setting this to true
- * we are forcing usage of multipart request.
- */
- public HttpRequest multipart(final boolean multipart) {
- this.multipart = multipart;
- return this;
- }
-
-
- // ---------------------------------------------------------------- cookies
-
- /**
- * Sets cookies to the request.
- */
- public HttpRequest cookies(final Cookie... cookies) {
- if (cookies.length == 0) {
- return this;
- }
-
- final StringBuilder cookieString = new StringBuilder();
-
- boolean first = true;
-
- for (final Cookie cookie : cookies) {
- final Integer maxAge = cookie.getMaxAge();
- if (maxAge != null && maxAge.intValue() == 0) {
- continue;
- }
-
- if (!first) {
- cookieString.append("; ");
- }
-
- first = false;
- cookieString.append(cookie.getName());
- cookieString.append('=');
- cookieString.append(cookie.getValue());
- }
-
- headerOverwrite("cookie", cookieString.toString());
-
- return this;
- }
-
-
- // ---------------------------------------------------------------- query
-
- /**
- * Adds query parameter.
- */
- public HttpRequest query(final String name, final String value) {
- query.add(name, value);
- return this;
- }
-
- /**
- * Adds many query parameters at once. Although it accepts objects,
- * each value will be converted to string.
- */
- public HttpRequest query(final String name1, final Object value1, final Object... parameters) {
- query(name1, value1 == null ? null : value1.toString());
-
- for (int i = 0; i < parameters.length; i += 2) {
- final String name = parameters[i].toString();
-
- final String value = parameters[i + 1].toString();
- query.add(name, value);
- }
- return this;
- }
-
- /**
- * Adds all parameters from the provided map.
- */
- public HttpRequest query(final Map queryMap) {
- for (final Map.Entry entry : queryMap.entrySet()) {
- query.add(entry.getKey(), entry.getValue());
- }
- return this;
- }
-
- /**
- * Returns backend map of query parameters.
- */
- public HttpMultiMap query() {
- return query;
- }
-
- /**
- * Clears all query parameters.
- */
- public HttpRequest clearQueries() {
- query.clear();
- return this;
- }
-
- /**
- * Removes query parameters for given name.
- */
- public HttpRequest queryRemove(final String name) {
- query.remove(name);
- return this;
- }
-
- // ---------------------------------------------------------------- queryString
-
- /**
- * @see #queryString(String, boolean)
- */
- public HttpRequest queryString(final String queryString) {
- return queryString(queryString, true);
- }
-
- /**
- * Sets query from provided query string. Previous query values
- * are discarded.
- */
- public HttpRequest queryString(final String queryString, final boolean decode) {
- this.query = HttpUtil.parseQuery(queryString, decode);
- return this;
- }
-
- /**
- * Generates query string. All values are URL encoded.
- */
- public String queryString() {
- if (query == null) {
- return StringPool.EMPTY;
- }
- return HttpUtil.buildQuery(query, queryEncoding);
- }
-
- // ---------------------------------------------------------------- query encoding
-
- protected String queryEncoding = Defaults.queryEncoding;
-
- /**
- * Defines encoding for query parameters.
- */
- public HttpRequest queryEncoding(final String encoding) {
- this.queryEncoding = encoding;
- return this;
- }
-
- // ---------------------------------------------------------------- full path
-
- /**
- * Returns full URL path.
- * Simply concatenates {@link #protocol(String) protocol}, {@link #host(String) host},
- * {@link #port(int) port}, {@link #path(String) path} and {@link #queryString(String) query string}.
- */
- public String url() {
- final StringBuilder url = new StringBuilder();
-
- url.append(hostUrl());
-
- if (path != null) {
- url.append(path);
- }
-
- final String queryString = queryString();
-
- if (StringUtil.isNotBlank(queryString)) {
- url.append('?');
- url.append(queryString);
- }
-
- return url.toString();
- }
-
- /**
- * Returns just host url, without path and query.
- */
- public String hostUrl() {
- final StringBand url = new StringBand(8);
-
- if (protocol != null) {
- url.append(protocol);
- url.append("://");
- }
-
- if (host != null) {
- url.append(host);
- }
-
- if (port != Defaults.DEFAULT_PORT) {
- url.append(':');
- url.append(port);
- }
-
- return url.toString();
- }
-
- // ---------------------------------------------------------------- auth
-
- /**
- * Enables basic authentication by adding required header.
- */
- public HttpRequest basicAuthentication(final String username, final String password) {
- if (username != null && password != null) {
- final String data = username.concat(StringPool.COLON).concat(password);
-
- final String base64 = Base64.encodeToString(data);
-
- headerOverwrite(HEADER_AUTHORIZATION, "Basic " + base64);
- }
-
- return this;
- }
-
- /**
- * Enables token-based authentication.
- */
- public HttpRequest tokenAuthentication(final String token) {
- if (token != null) {
- headerOverwrite(HEADER_AUTHORIZATION, "Bearer " + token);
- }
- return this;
- }
-
-
- // ---------------------------------------------------------------- https
-
- private boolean trustAllCertificates;
- private boolean verifyHttpsHost = true;
-
- /**
- * Trusts all certificates, use with caution.
- */
- public HttpRequest trustAllCerts(final boolean trust) {
- trustAllCertificates = trust;
- return this;
- }
-
- /**
- * Returns a flag if to trusts all certificates.
- */
- public boolean trustAllCertificates() {
- return trustAllCertificates;
- }
-
- /**
- * Verifies HTTPS hosts.
- */
- public HttpRequest verifyHttpsHost(final boolean verifyHttpsHost) {
- this.verifyHttpsHost = verifyHttpsHost;
- return this;
- }
-
- /**
- * Returns a flag if to verify https hosts.
- */
- public boolean verifyHttpsHost() {
- return verifyHttpsHost;
- }
-
- // ---------------------------------------------------------------- misc
-
- /**
- * Sets 'Host' header from current host and port.
- */
- public HttpRequest setHostHeader() {
- String hostPort = this.host;
-
- if (port != Defaults.DEFAULT_PORT) {
- hostPort += StringPool.COLON + port;
- }
-
- headerOverwrite(HEADER_HOST, hostPort);
- return this;
- }
-
- // ---------------------------------------------------------------- monitor
-
- /**
- * Registers {@link jodd.http.HttpProgressListener listener} that will
- * monitor upload progress. Be aware that the whole size of the
- * request is being monitored, not only the files content.
- */
- public HttpRequest monitor(final HttpProgressListener httpProgressListener) {
- this.httpProgressListener = httpProgressListener;
- return this;
- }
-
- // ---------------------------------------------------------------- connection properties
-
- protected int timeout = -1;
- protected int connectTimeout = -1;
- protected boolean followRedirects = false;
- protected int maxRedirects = 50;
-
- /**
- * Defines the socket timeout (SO_TIMEOUT) in milliseconds, which is the timeout for waiting for data or,
- * put differently, a maximum period inactivity between two consecutive data packets).
- * After establishing the connection, the client socket waits for response after sending
- * the request. This is the elapsed time since the client has sent request to the
- * server before server responds. Please note that this is not same as HTTP Error 408 which
- * the server sends to the client. In other words its maximum period inactivity between
- * two consecutive data packets arriving at client side after connection is established.
- * A timeout value of zero is interpreted as an infinite timeout.
- * @see jodd.http.HttpConnection#setTimeout(int)
- */
- public HttpRequest timeout(final int milliseconds) {
- this.timeout = milliseconds;
- return this;
- }
-
- /**
- * Returns read timeout (SO_TIMEOUT) in milliseconds. Negative value
- * means that default value is used.
- * @see #timeout(int)
- */
- public int timeout() {
- return timeout;
- }
-
- /**
- * Defines the socket timeout (SO_TIMEOUT) in milliseconds, which is the timeout
- * for waiting for data or, put differently, a maximum period inactivity between
- * two consecutive data packets). A timeout value of zero is interpreted as
- * an infinite timeout.
- */
- public HttpRequest connectionTimeout(final int milliseconds) {
- this.connectTimeout = milliseconds;
- return this;
- }
-
- /**
- * Returns socket connection timeout. Negative value means that default
- * value is used.
- * @see #connectionTimeout(int)
- */
- public int connectionTimeout() {
- return connectTimeout;
- }
-
- /**
- * Defines if redirects responses should be followed. NOTE: when redirection is enabled,
- * the original URL will NOT be preserved in the request!
- */
- public HttpRequest followRedirects(final boolean followRedirects) {
- this.followRedirects = followRedirects;
- return this;
- }
-
- /**
- * Returns {@code true} if redirects are followed.
- */
- public boolean isFollowRedirects() {
- return this.followRedirects;
- }
-
- /**
- * Sets the max number of redirects, used when {@link #followRedirects} is enabled.
- */
- public HttpRequest maxRedirects(final int maxRedirects) {
- this.maxRedirects = maxRedirects;
- return this;
- }
-
- /**
- * Returns max number of redirects, used when {@link #followRedirects} is enabled.
- */
- public int maxRedirects() {
- return this.maxRedirects;
- }
-
-
- // ---------------------------------------------------------------- send
-
- protected HttpConnection httpConnection;
- protected HttpConnectionProvider httpConnectionProvider;
-
- /**
- * Uses custom connection provider when {@link #open() opening} the
- * connection.
- */
- public HttpRequest withConnectionProvider(final HttpConnectionProvider httpConnectionProvider) {
- this.httpConnectionProvider = httpConnectionProvider;
- return this;
- }
-
- /**
- * Returns http connection provider that was used for creating
- * current http connection. If null
, default
- * connection provider will be used.
- */
- public HttpConnectionProvider connectionProvider() {
- return httpConnectionProvider;
- }
-
- /**
- * Returns {@link HttpConnection} that is going to be
- * used for sending this request. Value is available
- * ONLY after calling {@link #open()} and before {@link #send()}.
- */
- public HttpConnection connection() {
- return httpConnection;
- }
-
- /**
- * Opens a new {@link HttpConnection connection} using either
- * provided or {@link HttpConnectionProvider default} connection
- * provider.
- */
- public HttpRequest open() {
- if (httpConnectionProvider == null) {
- return open(HttpConnectionProvider.get());
- }
-
- return open(httpConnectionProvider);
- }
-
- /**
- * Opens a new {@link jodd.http.HttpConnection connection}
- * using given {@link jodd.http.HttpConnectionProvider}.
- */
- public HttpRequest open(final HttpConnectionProvider httpConnectionProvider) {
- if (this.httpConnection != null) {
- throw new HttpException("Connection already opened");
- }
- try {
- this.httpConnectionProvider = httpConnectionProvider;
- this.httpConnection = httpConnectionProvider.createHttpConnection(this);
- } catch (final IOException ioex) {
- throw new HttpException("Can't connect to: " + url(), ioex);
- }
-
- return this;
- }
-
- /**
- * Assignees provided {@link jodd.http.HttpConnection} for communication.
- * It does not actually opens it until the {@link #send() sending}.
- */
- public HttpRequest open(final HttpConnection httpConnection) {
- if (this.httpConnection != null) {
- throw new HttpException("Connection already opened");
- }
- this.httpConnection = httpConnection;
- this.httpConnectionProvider = null;
- return this;
- }
-
- /**
- * Continues using the same keep-alive connection.
- * Don't use any variant of open()
when
- * continuing the communication!
- * First it checks if "Connection" header exist in the response
- * and if it is equal to "Keep-Alive" value. Then it
- * checks the "Keep-Alive" headers "max" parameter.
- * If its value is positive, then the existing {@link jodd.http.HttpConnection}
- * from the request will be reused. If max value is 1,
- * connection will be sent with "Connection: Close" header, indicating
- * its the last request. When new connection is created, the
- * same {@link jodd.http.HttpConnectionProvider} that was used for
- * creating initial connection is used for opening the new connection.
- *
- * @param doContinue set it to false
to indicate the last connection
- */
- public HttpRequest keepAlive(final HttpResponse httpResponse, final boolean doContinue) {
- boolean keepAlive = httpResponse.isConnectionPersistent();
- if (keepAlive) {
- final HttpConnection previousConnection = httpResponse.getHttpRequest().httpConnection;
-
- if (previousConnection != null) {
- // keep using the connection!
- this.httpConnection = previousConnection;
- this.httpConnectionProvider = httpResponse.getHttpRequest().connectionProvider();
- }
-
- //keepAlive = true; (already set)
- } else {
- // close previous connection
- httpResponse.close();
-
- // force keep-alive on new request
- keepAlive = true;
- }
-
- // if we don't want to continue with this persistent session, mark this connection as closed
- if (!doContinue) {
- keepAlive = false;
- }
-
- connectionKeepAlive(keepAlive);
-
- // if connection is not opened, open it using previous connection provider
- if (httpConnection == null) {
- open(httpResponse.getHttpRequest().connectionProvider());
- }
- return this;
- }
-
- /**
- * {@link #open() Opens connection} if not already open, sends request,
- * reads response and closes the request. If keep-alive mode is enabled
- * connection will not be closed.
- */
- public HttpResponse send() {
- if (!followRedirects) {
- return _send();
- }
-
- int redirects = this.maxRedirects;
-
- while (redirects > 0) {
- redirects--;
-
- final HttpResponse httpResponse = _send();
-
- final int statusCode = httpResponse.statusCode();
-
- if (HttpStatus.isRedirect(statusCode)) {
- _reset();
- set(httpResponse.location());
- continue;
- }
-
- return httpResponse;
- }
-
- throw new HttpException("Max number of redirects exceeded: " + this.maxRedirects);
- }
-
- /**
- * Resets the request by resetting all additional values
- * added during the sending.
- */
- private void _reset() {
- headers.remove(HEADER_HOST);
- }
-
- private HttpResponse _send() {
- if (httpConnection == null) {
- open();
- }
-
- // sends data
- final HttpResponse httpResponse;
- try {
- final OutputStream outputStream = httpConnection.getOutputStream();
-
- sendTo(outputStream);
-
- final InputStream inputStream = httpConnection.getInputStream();
-
- httpResponse = HttpResponse.readFrom(inputStream);
-
- httpResponse.assignHttpRequest(this);
- } catch (final IOException ioex) {
- throw new HttpException(ioex);
- }
-
- final boolean keepAlive = httpResponse.isConnectionPersistent();
-
- if (!keepAlive) {
- // closes connection if keep alive is false, or if counter reached 0
- httpConnection.close();
- httpConnection = null;
- }
-
- return httpResponse;
- }
-
- // ---------------------------------------------------------------- buffer
-
- /**
- * Prepares the request buffer.
- */
- @Override
- protected Buffer buffer(final boolean fullRequest) {
- // INITIALIZATION
-
- // host port
-
- if (header(HEADER_HOST) == null) {
- setHostHeader();
- }
-
- // form
-
- final Buffer formBuffer = formBuffer();
-
- // query string
-
- final String queryString = queryString();
-
- // user-agent
-
- if (header("User-Agent") == null) {
- header("User-Agent", Defaults.userAgent);
- }
-
- // POST method requires Content-Type to be set
-
- if (method.equals("POST") && (contentLength() == null)) {
- contentLength(0);
- }
-
-
- // BUILD OUT
-
- final Buffer request = new Buffer();
-
- request.append(method)
- .append(SPACE)
- .append(path);
-
- if (query != null && !query.isEmpty()) {
- request.append('?');
- request.append(queryString);
- }
-
- request.append(SPACE)
- .append(httpVersion)
- .append(CRLF);
-
- populateHeaderAndBody(request, formBuffer, fullRequest);
-
- return request;
- }
-
- // ---------------------------------------------------------------- parse
-
- /**
- * Parses input stream and creates new HttpRequest
object.
- * Assumes input stream is in ISO_8859_1 encoding.
- */
- public static HttpRequest readFrom(final InputStream in) {
- return readFrom(in, StandardCharsets.ISO_8859_1.name());
- }
-
- public static HttpRequest readFrom(final InputStream in, final String encoding) {
- final BufferedReader reader;
- try {
- reader = new BufferedReader(new InputStreamReader(in, encoding));
- } catch (final UnsupportedEncodingException uneex) {
- return null;
- }
-
- final HttpRequest httpRequest = new HttpRequest();
- httpRequest.headers.clear();
-
- final String line;
- try {
- line = reader.readLine();
- } catch (final IOException ioex) {
- throw new HttpException(ioex);
- }
-
- if (!StringUtil.isBlank(line)) {
- final String[] s = StringUtil.splitc(line, ' ');
-
- httpRequest.method(s[0]);
- httpRequest.path(s[1]);
- httpRequest.httpVersion(s[2]);
-
- httpRequest.readHeaders(reader);
- httpRequest.readBody(reader);
- }
-
- return httpRequest;
- }
-
-
- // ---------------------------------------------------------------- shortcuts
-
- /**
- * Specifies JSON content type.
- */
- public HttpRequest contentTypeJson() {
- return contentType(MimeTypes.MIME_APPLICATION_JSON);
- }
-
- /**
- * Accepts JSON content type.
- */
- public HttpRequest acceptJson() {
- return accept(MimeTypes.MIME_APPLICATION_JSON);
- }
-
-
- // ---------------------------------------------------------------- functional/async
-
- /**
- * Sends http request asynchronously using common fork-join pool.
- * Note that this is not the right non-blocking call (not a NIO), it is just
- * a regular call that is operated in a separate thread.
- */
- public CompletableFuture sendAsync() {
- return CompletableFuture.supplyAsync(this::send);
- }
-
- /**
- * Syntax sugar.
- */
- public R sendAndReceive(final Function responseHandler) {
- return responseHandler.apply(send());
- }
-
- /**
- * Syntax sugar.
- */
- public void sendAndReceive(final Consumer responseHandler) {
- responseHandler.accept(send());
- }
-
-}
diff --git a/jodd-http/src/main/java/jodd/http/HttpResponse.java b/jodd-http/src/main/java/jodd/http/HttpResponse.java
deleted file mode 100644
index d74905dfd..000000000
--- a/jodd-http/src/main/java/jodd/http/HttpResponse.java
+++ /dev/null
@@ -1,280 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.io.IOUtil;
-import jodd.util.StringPool;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.zip.GZIPInputStream;
-
-import static jodd.util.StringPool.CRLF;
-import static jodd.util.StringPool.SPACE;
-
-/**
- * HTTP response.
- */
-public class HttpResponse extends HttpBase {
-
- protected int statusCode;
- protected String statusPhrase;
-
- /**
- * Returns response status code.
- */
- public int statusCode() {
- return statusCode;
- }
-
- /**
- * Sets response status code.
- */
- public HttpResponse statusCode(final int statusCode) {
- this.statusCode = statusCode;
- return this;
- }
-
- /**
- * Returns response status phrase.
- */
- public String statusPhrase() {
- return statusPhrase;
- }
-
- /**
- * Sets response status phrase.
- */
- public HttpResponse statusPhrase(final String statusPhrase) {
- this.statusPhrase = statusPhrase;
- return this;
- }
-
- /**
- * Parses 'location' header to return the next location or returns {@code null} if location not specified.
- * Specification (rfc2616 )
- * says that only absolute path must be provided, however, this does not
- * happens in the real world. There a proposal
- * that allows server name etc to be omitted.
- */
- public String location() {
- String location = header("location");
-
- if (location == null) {
- return null;
- }
-
- if (location.startsWith(StringPool.SLASH)) {
- location = getHttpRequest().hostUrl() + location;
- }
-
- return location;
- }
-
- // ---------------------------------------------------------------- cookie
-
- /**
- * Returns list of valid cookies sent from server.
- * If no cookie found, returns an empty array. Invalid cookies are ignored.
- */
- public Cookie[] cookies() {
- final List newCookies = headers("set-cookie");
-
- if (newCookies == null) {
- return new Cookie[0];
- }
-
- final List cookieList = new ArrayList<>(newCookies.size());
-
- for (final String cookieValue : newCookies) {
- try {
- final Cookie cookie = new Cookie(cookieValue);
-
- cookieList.add(cookie);
- }
- catch (final Exception ex) {
- // ignore
- }
- }
-
- return cookieList.toArray(new Cookie[0]);
- }
-
- // ---------------------------------------------------------------- body
-
- /**
- * Unzips GZip-ed body content, removes the content-encoding header
- * and sets the new content-length value.
- */
- public HttpResponse unzip() {
- final String contentEncoding = contentEncoding();
-
- if (contentEncoding != null && contentEncoding().equals("gzip")) {
- if (body != null) {
- headerRemove(HEADER_CONTENT_ENCODING);
- try {
- final ByteArrayInputStream in = new ByteArrayInputStream(body.getBytes(StandardCharsets.ISO_8859_1));
- final GZIPInputStream gzipInputStream = new GZIPInputStream(in);
-
- final ByteArrayOutputStream out = new ByteArrayOutputStream();
-
- IOUtil.copy(gzipInputStream, out);
-
- body(out.toString(StandardCharsets.ISO_8859_1.name()));
- } catch (final IOException ioex) {
- throw new HttpException(ioex);
- }
- }
- }
- return this;
- }
-
- // ---------------------------------------------------------------- buffer
-
-
- /**
- * Creates response {@link jodd.http.Buffer buffer}.
- */
- @Override
- protected Buffer buffer(final boolean fullResponse) {
- // form
-
- final Buffer formBuffer = formBuffer();
-
- // response
-
- final Buffer response = new Buffer();
-
- response.append(httpVersion)
- .append(SPACE)
- .append(statusCode)
- .append(SPACE)
- .append(statusPhrase)
- .append(CRLF);
-
- populateHeaderAndBody(response, formBuffer, fullResponse);
-
- return response;
- }
-
- // ---------------------------------------------------------------- read from
-
- /**
- * Reads response input stream and returns {@link HttpResponse response}.
- * Supports both streamed and chunked response.
- */
- public static HttpResponse readFrom(final InputStream in) {
- final InputStreamReader inputStreamReader = new InputStreamReader(in, StandardCharsets.ISO_8859_1);
- final BufferedReader reader = new BufferedReader(inputStreamReader);
-
- final HttpResponse httpResponse = new HttpResponse();
-
- // the first line
- String line;
- try {
- line = reader.readLine();
- } catch (final IOException ioex) {
- throw new HttpException(ioex);
- }
-
- if (line != null) {
-
- line = line.trim();
-
- int ndx = line.indexOf(' ');
- int ndx2;
-
- if (ndx > -1) {
- httpResponse.httpVersion(line.substring(0, ndx));
-
- ndx2 = line.indexOf(' ', ndx + 1);
- }
- else {
- httpResponse.httpVersion(HTTP_1_1);
- ndx2 = -1;
- ndx = 0;
- }
-
- if (ndx2 == -1) {
- ndx2 = line.length();
- }
-
- try {
- httpResponse.statusCode(Integer.parseInt(line.substring(ndx, ndx2).trim()));
- }
- catch (final NumberFormatException nfex) {
- httpResponse.statusCode(-1);
- }
-
- httpResponse.statusPhrase(line.substring(ndx2).trim());
- }
-
- httpResponse.readHeaders(reader);
- httpResponse.readBody(reader);
-
- return httpResponse;
- }
-
- // ---------------------------------------------------------------- request
-
- protected HttpRequest httpRequest;
-
- /**
- * Binds {@link jodd.http.HttpRequest} to this response.
- */
- void assignHttpRequest(final HttpRequest httpRequest) {
- this.httpRequest = httpRequest;
- }
-
- /**
- * Returns {@link jodd.http.HttpRequest} that created this response.
- */
- public HttpRequest getHttpRequest() {
- return httpRequest;
- }
-
- /**
- * Closes requests connection if it was open.
- * Should be called when using keep-alive connections.
- * Otherwise, connection will be already closed.
- */
- public HttpResponse close() {
- final HttpConnection httpConnection = httpRequest.httpConnection;
- if (httpConnection != null) {
- httpConnection.close();
- httpRequest.httpConnection = null;
- }
- return this;
- }
-
-}
diff --git a/jodd-http/src/main/java/jodd/http/HttpStatus.java b/jodd-http/src/main/java/jodd/http/HttpStatus.java
deleted file mode 100644
index 621bf200a..000000000
--- a/jodd-http/src/main/java/jodd/http/HttpStatus.java
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-package jodd.http;
-
-/**
- * HTTP status codes.
- */
-public class HttpStatus {
-
- /**
- * Returns {@code true} if status code indicates successful result.
- */
- public static boolean isSuccessful(final int statusCode) {
- return statusCode < 400;
- }
-
- /**
- * Returns {@code true} if status code indicates a redirect.
- */
- public static boolean isRedirect(final int statusCode) {
- return statusCode >=300 && statusCode < 400;
- }
-
- /**
- * Returns {@code true} if status code indicates an error.
- */
- public static boolean isError(final int statusCode) {
- return statusCode >= 500;
- }
-
- // ---------------------------------------------------------------- 1xx
-
- /**
- * HTTP Status-Code 100: Continue.
- */
- public static final int HTTP_CONTINUE = 100;
-
- // ---------------------------------------------------------------- 2xx
-
- /**
- * HTTP Status-Code 200: OK.
- */
- public static final int HTTP_OK = 200;
-
- /**
- * HTTP Status-Code 201: Created.
- */
- public static final int HTTP_CREATED = 201;
-
- /**
- * HTTP Status-Code 202: Accepted.
- */
- public static final int HTTP_ACCEPTED = 202;
-
- /**
- * HTTP Status-Code 203: Non-Authoritative Information.
- */
- public static final int HTTP_NOT_AUTHORITATIVE = 203;
-
- /**
- * HTTP Status-Code 204: No Content.
- */
- public static final int HTTP_NO_CONTENT = 204;
-
- /**
- * HTTP Status-Code 205: Reset Content.
- */
- public static final int HTTP_RESET = 205;
-
- /**
- * HTTP Status-Code 206: Partial Content.
- */
- public static final int HTTP_PARTIAL = 206;
-
- // ---------------------------------------------------------------- 3xx
-
- /**
- * HTTP Status-Code 300: Multiple Choices.
- */
- public static final int HTTP_MULTIPLE_CHOICES = 300;
-
- /**
- * HTTP Status-Code 301: Moved Permanently.
- */
- public static final int HTTP_MOVED_PERMANENTLY = 301;
-
- /**
- * HTTP Status-Code 302: Temporary Redirect.
- */
- public static final int HTTP_MOVED_TEMPORARY = 302;
-
- /**
- * HTTP Status-Code 303: See Other.
- */
- public static final int HTTP_SEE_OTHER = 303;
-
- /**
- * HTTP Status-Code 304: Not Modified.
- */
- public static final int HTTP_NOT_MODIFIED = 304;
-
- /**
- * HTTP Status-Code 305: Use Proxy.
- */
- public static final int HTTP_USE_PROXY = 305;
-
- /**
- * HTTP Status-Code 307: Temporary Redirect.
- */
- public static final int HTTP_TEMPORARY_REDIRECT = 307;
-
- // ---------------------------------------------------------------- 4xx
-
- /**
- * HTTP Status-Code 400: Bad Request.
- */
- public static final int HTTP_BAD_REQUEST = 400;
-
- /**
- * HTTP Status-Code 401: Unauthorized.
- */
- public static final int HTTP_UNAUTHORIZED = 401;
-
- /**
- * HTTP Status-Code 402: Payment Required.
- */
- public static final int HTTP_PAYMENT_REQUIRED = 402;
-
- /**
- * HTTP Status-Code 403: Forbidden.
- */
- public static final int HTTP_FORBIDDEN = 403;
-
- /**
- * HTTP Status-Code 404: Not Found.
- */
- public static final int HTTP_NOT_FOUND = 404;
-
- /**
- * HTTP Status-Code 405: Method Not Allowed.
- */
- public static final int HTTP_BAD_METHOD = 405;
-
- /**
- * HTTP Status-Code 406: Not Acceptable.
- */
- public static final int HTTP_NOT_ACCEPTABLE = 406;
-
- /**
- * HTTP Status-Code 407: Proxy Authentication Required.
- */
- public static final int HTTP_PROXY_AUTH_REQUIRED = 407;
-
- /**
- * HTTP Status-Code 408: Request Time-Out.
- */
- public static final int HTTP_CLIENT_TIMEOUT = 408;
-
- /**
- * HTTP Status-Code 409: Conflict.
- */
- public static final int HTTP_CONFLICT = 409;
-
- /**
- * HTTP Status-Code 410: Gone.
- */
- public static final int HTTP_GONE = 410;
-
- /**
- * HTTP Status-Code 411: Length Required.
- */
- public static final int HTTP_LENGTH_REQUIRED = 411;
-
- /**
- * HTTP Status-Code 412: Precondition Failed.
- */
- public static final int HTTP_PRECON_FAILED = 412;
-
- /**
- * HTTP Status-Code 413: Request Entity Too Large.
- */
- public static final int HTTP_ENTITY_TOO_LARGE = 413;
-
- /**
- * HTTP Status-Code 414: Request-URI Too Large.
- */
- public static final int HTTP_REQ_TOO_LONG = 414;
-
- /**
- * HTTP Status-Code 415: Unsupported Media Type.
- */
- public static final int HTTP_UNSUPPORTED_TYPE = 415;
-
- // ---------------------------------------------------------------- 5xx
-
- /**
- * HTTP Status-Code 500: Internal Server Error.
- */
- public static final int HTTP_INTERNAL_ERROR = 500;
-
- /**
- * HTTP Status-Code 501: Not Implemented.
- */
- public static final int HTTP_NOT_IMPLEMENTED = 501;
-
- /**
- * HTTP Status-Code 502: Bad Gateway.
- */
- public static final int HTTP_BAD_GATEWAY = 502;
-
- /**
- * HTTP Status-Code 503: Service Unavailable.
- */
- public static final int HTTP_UNAVAILABLE = 503;
-
- /**
- * HTTP Status-Code 504: Gateway Timeout.
- */
- public static final int HTTP_GATEWAY_TIMEOUT = 504;
-
- /**
- * HTTP Status-Code 505: HTTP Version Not Supported.
- */
- public static final int HTTP_VERSION_NOT_SUPPORTED = 505;
-
-}
diff --git a/jodd-http/src/main/java/jodd/http/HttpTunnel.java b/jodd-http/src/main/java/jodd/http/HttpTunnel.java
deleted file mode 100644
index bf7365e80..000000000
--- a/jodd-http/src/main/java/jodd/http/HttpTunnel.java
+++ /dev/null
@@ -1,214 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.io.IOUtil;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- * Simple HTTP tunnel base ready to be extended.
- */
-public class HttpTunnel {
-
- /**
- * The number of threads that can be executed in parallel.
- */
- protected int threadPoolSize = 10;
-
- /**
- * Number of incoming sockets connection that can be hold
- * before processing each.
- */
- protected int socketBacklog = 100;
-
- /**
- * Tunnel listening port.
- */
- protected int listenPort = 8888;
-
- /**
- * Target host.
- */
- protected String targetHost = "localhost";
-
- /**
- * Target port.
- */
- protected int targetPort = 8080;
-
- protected ExecutorService executorService;
- protected volatile boolean running;
- protected ServerSocket serverSocket;
-
- /**
- * Starts HTTP tunnel. Method ends when the tunnel is stopped.
- */
- public void start() throws IOException {
- serverSocket = new ServerSocket(listenPort, socketBacklog);
- serverSocket.setReuseAddress(true);
- executorService = Executors.newFixedThreadPool(threadPoolSize);
-
- running = true;
- while (running) {
- final Socket socket = serverSocket.accept();
- socket.setKeepAlive(false);
- executorService.execute(onSocketConnection(socket));
- }
- executorService.shutdown();
- }
-
- /**
- * Invoked on incoming connection. By default returns {@link HttpTunnelConnection}
- * to handle the connection. May be used to return custom
- * handlers.
- */
- protected Runnable onSocketConnection(final Socket socket) {
- return new HttpTunnelConnection(socket);
- }
-
- /**
- * Stops the tunnel, shutdowns the thread pool and closes server socket.
- */
- public void stop() {
- running = false;
- executorService.shutdown();
- try {
- serverSocket.close();
- } catch (final IOException ignore) {
- }
- }
-
- /**
- * Single connection handler that performs the tunneling.
- */
- public class HttpTunnelConnection implements Runnable {
-
- protected final Socket socket;
-
- public HttpTunnelConnection(final Socket socket) {
- this.socket = socket;
- }
-
- @Override
- public void run() {
- try {
- tunnel();
- } catch (final IOException ioex) {
- ioex.printStackTrace();
- }
- }
-
- /**
- * Invoked after income connection is parsed. Nothing is
- * changed in the request, except the target host and port.
- */
- protected void onRequest(final HttpRequest request) {
- }
-
- /**
- * Invoked after target response is processed. Response is now
- * ready to be sent back to the client. The following header
- * parameters are changed:
- *
- * Transfer-Encoding is removed, as body is returned at once,
- * Content-Length is added/update to body size.
- *
- */
- protected void onResponse(final HttpResponse response) {
- }
-
- /**
- * Performs the tunneling. The following steps occurs:
- *
- * read and parse clients request
- * open socket to target
- * resend request to target
- * read targets response
- * fix response and resend it to client
- *
- */
- protected void tunnel() throws IOException {
-
- // read request
- final InputStream socketInput = socket.getInputStream();
- final HttpRequest request = HttpRequest.readFrom(socketInput);
-
- // open client socket to target
- final Socket clientSocket = Sockets.connect(targetHost, targetPort);
-
- // do request
- request.host(targetHost);
- request.port(targetPort);
- request.setHostHeader();
- onRequest(request);
-
- // resend request to target
- final OutputStream out = clientSocket.getOutputStream();
- request.sendTo(out);
-
- // read target response
- final InputStream in = clientSocket.getInputStream();
- final HttpResponse response = HttpResponse.readFrom(in);
-
- // close client socket
- IOUtil.close(in);
- IOUtil.close(out);
- try {
- clientSocket.close();
- } catch (final IOException ignore) {
- }
-
- // fix response
- if (response.body() != null) {
- response.headerRemove("Transfer-Encoding");
- response.contentLength(response.body().length());
- }
-
- // do response
- onResponse(response);
-
- // send response back
- final OutputStream socketOutput = socket.getOutputStream();
- response.sendTo(socketOutput);
-
- // close socket
- IOUtil.close(socketInput);
- IOUtil.close(socketOutput);
- try {
- socket.close();
- } catch (final IOException ignore) {
- }
- }
- }
-
-}
diff --git a/jodd-http/src/main/java/jodd/http/HttpUtil.java b/jodd-http/src/main/java/jodd/http/HttpUtil.java
deleted file mode 100644
index 33a5539ae..000000000
--- a/jodd-http/src/main/java/jodd/http/HttpUtil.java
+++ /dev/null
@@ -1,255 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.net.URLCoder;
-import jodd.net.URLDecoder;
-import jodd.util.StringBand;
-import jodd.util.StringPool;
-import jodd.util.StringUtil;
-
-import java.nio.charset.Charset;
-import java.util.Map;
-
-/**
- * Few HTTP utilities.
- */
-public class HttpUtil {
-
- // ---------------------------------------------------------------- query
-
- /**
- * Builds a query string from given query map.
- */
- public static String buildQuery(final HttpMultiMap> queryMap, final String encoding) {
- if (queryMap.isEmpty()) {
- return StringPool.EMPTY;
- }
-
- final int queryMapSize = queryMap.size();
-
- final StringBand query = new StringBand(queryMapSize * 4);
-
- int count = 0;
- for (final Map.Entry entry : queryMap) {
- String key = entry.getKey();
- key = URLCoder.encodeQueryParam(key, Charset.forName(encoding));
-
- final Object value = entry.getValue();
-
- if (value == null) {
- if (count != 0) {
- query.append('&');
- }
-
- query.append(key);
- count++;
- } else {
- if (count != 0) {
- query.append('&');
- }
-
- query.append(key);
- count++;
- query.append('=');
-
- final String valueString = URLCoder.encodeQueryParam(value.toString(), Charset.forName(encoding));
- query.append(valueString);
- }
- }
-
- return query.toString();
- }
-
- /**
- * Parses query from give query string. Values are optionally decoded.
- */
- public static HttpMultiMap parseQuery(final String query, final boolean decode) {
-
- final HttpMultiMap queryMap = HttpMultiMap.newCaseInsensitiveMap();
-
- if (StringUtil.isBlank(query)) {
- return queryMap;
- }
-
- int lastNdx = 0;
- while (lastNdx < query.length()) {
- int ndx = query.indexOf('&', lastNdx);
- if (ndx == -1) {
- ndx = query.length();
- }
-
- final String paramAndValue = query.substring(lastNdx, ndx);
-
- ndx = paramAndValue.indexOf('=');
-
- if (ndx == -1) {
- queryMap.add(paramAndValue, null);
- }
- else {
- String name = paramAndValue.substring(0, ndx);
- if (decode) {
- name = URLDecoder.decodeQuery(name);
- }
-
- String value = paramAndValue.substring(ndx + 1);
-
- if (decode) {
- value = URLDecoder.decodeQuery(value);
- }
-
- queryMap.add(name, value);
- }
- lastNdx += paramAndValue.length() + 1;
- }
-
- return queryMap;
- }
-
- // ---------------------------------------------------------------- misc
-
- /**
- * Makes nice header names.
- */
- public static String prepareHeaderParameterName(final String headerName) {
-
- // special cases
-
- if (headerName.equals("etag")) {
- return HttpBase.HEADER_ETAG;
- }
-
- if (headerName.equals("www-authenticate")) {
- return "WWW-Authenticate";
- }
-
- final char[] name = headerName.toCharArray();
-
- boolean capitalize = true;
-
- for (int i = 0; i < name.length; i++) {
- final char c = name[i];
-
- if (c == '-') {
- capitalize = true;
- continue;
- }
-
- if (capitalize) {
- name[i] = Character.toUpperCase(c);
- capitalize = false;
- } else {
- name[i] = Character.toLowerCase(c);
- }
- }
-
- return new String(name);
- }
-
- // ---------------------------------------------------------------- content type
-
- /**
- * Extracts media-type from value of "Content Type" header.
- */
- public static String extractMediaType(final String contentType) {
- final int index = contentType.indexOf(';');
-
- if (index == -1) {
- return contentType;
- }
-
- return contentType.substring(0, index);
- }
-
- /**
- * @see #extractHeaderParameter(String, String, char)
- */
- public static String extractContentTypeCharset(final String contentType) {
- return extractHeaderParameter(contentType, "charset", ';');
- }
-
- // ---------------------------------------------------------------- keep-alive
-
- /**
- * Extract keep-alive timeout.
- */
- public static String extractKeepAliveTimeout(final String keepAlive) {
- return extractHeaderParameter(keepAlive, "timeout", ',');
- }
-
- public static String extractKeepAliveMax(final String keepAlive) {
- return extractHeaderParameter(keepAlive, "max", ',');
- }
-
- // ---------------------------------------------------------------- header
-
- /**
- * Extracts header parameter. Returns null
- * if parameter not found.
- */
- public static String extractHeaderParameter(final String header, final String parameter, final char separator) {
- int index = 0;
-
- while (true) {
- index = header.indexOf(separator, index);
-
- if (index == -1) {
- return null;
- }
-
- index++;
-
- // skip whitespaces
- while (index < header.length() && header.charAt(index) == ' ') {
- index++;
- }
-
- int eqNdx = header.indexOf('=', index);
-
- if (eqNdx == -1) {
- return null;
- }
-
- final String paramName = header.substring(index, eqNdx);
-
- eqNdx++;
-
- if (!paramName.equalsIgnoreCase(parameter)) {
- index = eqNdx;
- continue;
- }
-
- final int endIndex = header.indexOf(';', eqNdx);
-
- if (endIndex == -1) {
- return header.substring(eqNdx);
- } else {
- return header.substring(eqNdx, endIndex);
- }
- }
- }
-
-}
diff --git a/jodd-http/src/main/java/jodd/http/ProxyInfo.java b/jodd-http/src/main/java/jodd/http/ProxyInfo.java
deleted file mode 100644
index 7d04a05ae..000000000
--- a/jodd-http/src/main/java/jodd/http/ProxyInfo.java
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-/**
- * Proxy information.
- */
-public class ProxyInfo {
-
- /**
- * Proxy types.
- */
- public enum ProxyType {
- NONE, HTTP, SOCKS4, SOCKS5
- }
-
- private final String proxyAddress;
- private final int proxyPort;
- private final String proxyUsername;
- private final String proxyPassword;
- private final ProxyType proxyType;
-
- public ProxyInfo(final ProxyType proxyType, final String proxyHost, final int proxyPort, final String proxyUser, final String proxyPassword) {
- this.proxyType = proxyType;
- this.proxyAddress = proxyHost;
- this.proxyPort = proxyPort;
- this.proxyUsername = proxyUser;
- this.proxyPassword = proxyPassword;
- }
-
- // ---------------------------------------------------------------- factory
-
- /**
- * Creates directProxy.
- */
- public static ProxyInfo directProxy() {
- return new ProxyInfo(ProxyType.NONE, null, 0, null, null);
- }
-
- /**
- * Creates SOCKS4 proxy.
- */
- public static ProxyInfo socks4Proxy(final String proxyAddress, final int proxyPort, final String proxyUser) {
- return new ProxyInfo(ProxyType.SOCKS4, proxyAddress, proxyPort, proxyUser, null);
- }
-
- /**
- * Creates SOCKS5 proxy.
- */
- public static ProxyInfo socks5Proxy(final String proxyAddress, final int proxyPort, final String proxyUser, final String proxyPassword) {
- return new ProxyInfo(ProxyType.SOCKS5, proxyAddress, proxyPort, proxyUser, proxyPassword);
- }
-
- /**
- * Creates HTTP proxy.
- */
- public static ProxyInfo httpProxy(final String proxyAddress, final int proxyPort, final String proxyUser, final String proxyPassword) {
- return new ProxyInfo(ProxyType.HTTP, proxyAddress, proxyPort, proxyUser, proxyPassword);
- }
-
- // ---------------------------------------------------------------- getter
-
- /**
- * Returns proxy type.
- */
- public ProxyType getProxyType() {
- return proxyType;
- }
-
- /**
- * Returns proxy address.
- */
- public String getProxyAddress() {
- return proxyAddress;
- }
-
- /**
- * Returns proxy port.
- */
- public int getProxyPort() {
- return proxyPort;
- }
-
- /**
- * Returns proxy user name or null
if
- * no authentication required.
- */
- public String getProxyUsername() {
- return proxyUsername;
- }
-
- /**
- * Returns proxy password or null
.
- */
- public String getProxyPassword() {
- return proxyPassword;
- }
-
-}
\ No newline at end of file
diff --git a/jodd-http/src/main/java/jodd/http/Sockets.java b/jodd-http/src/main/java/jodd/http/Sockets.java
deleted file mode 100644
index 3834f9b1d..000000000
--- a/jodd-http/src/main/java/jodd/http/Sockets.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-
-/**
- * Sockets factory.
- */
-public class Sockets {
-
- /**
- * Creates a socket.
- */
- public static Socket connect(final String hostname, final int port) throws IOException {
- final Socket socket = new Socket();
- socket.connect(new InetSocketAddress(hostname, port));
- return socket;
- }
-
- /**
- * Creates a socket with a timeout.
- */
- public static Socket connect(final String hostname, final int port, final int connectionTimeout) throws IOException {
- final Socket socket = new Socket();
- if (connectionTimeout <= 0) {
- socket.connect(new InetSocketAddress(hostname, port));
- }
- else {
- socket.connect(new InetSocketAddress(hostname, port), connectionTimeout);
- }
- return socket;
- }
-}
diff --git a/jodd-http/src/main/java/jodd/http/net/HTTPProxySocketFactory.java b/jodd-http/src/main/java/jodd/http/net/HTTPProxySocketFactory.java
deleted file mode 100644
index 2462310ac..000000000
--- a/jodd-http/src/main/java/jodd/http/net/HTTPProxySocketFactory.java
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.net;
-
-import jodd.http.HttpException;
-import jodd.http.ProxyInfo;
-import jodd.http.Sockets;
-import jodd.util.Base64;
-
-import javax.net.SocketFactory;
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.StringReader;
-import java.net.HttpURLConnection;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Socket factory for HTTP proxy.
- */
-public class HTTPProxySocketFactory extends SocketFactory {
-
- private final ProxyInfo proxy;
- private final int connectionTimeout;
-
- public HTTPProxySocketFactory(final ProxyInfo proxy, final int connectionTimeout) {
- this.proxy = proxy;
- this.connectionTimeout = connectionTimeout;
- }
-
- @Override
- public Socket createSocket(final String host, final int port) {
- return createHttpProxySocket(host, port);
- }
-
- @Override
- public Socket createSocket(final String host, final int port, final InetAddress localHost, final int localPort) {
- return createHttpProxySocket(host, port);
- }
-
- @Override
- public Socket createSocket(final InetAddress host, final int port) {
- return createHttpProxySocket(host.getHostAddress(), port);
- }
-
- @Override
- public Socket createSocket(final InetAddress address, final int port, final InetAddress localAddress, final int localPort) {
- return createHttpProxySocket(address.getHostAddress(), port);
- }
-
- private Socket createHttpProxySocket(final String host, final int port) {
- Socket socket = null;
- final String proxyAddress = proxy.getProxyAddress();
- final int proxyPort = proxy.getProxyPort();
-
- try {
- socket = Sockets.connect(proxyAddress, proxyPort, connectionTimeout);
- String hostport = host + ":" + port;
- String proxyLine = "";
- String username = proxy.getProxyUsername();
-
- if (username != null) {
- String password = proxy.getProxyPassword();
- proxyLine =
- "Proxy-Authorization: Basic " +
- Base64.encodeToString((username + ":" + password)) + "\r\n";
- }
-
- socket.getOutputStream().write(
- ("CONNECT " + hostport + " HTTP/1.1\r\n" +
- "Host: " + hostport + "\r\n" +
- proxyLine +
- "\r\n"
- ).getBytes("UTF-8")
- );
-
- InputStream in = socket.getInputStream();
- StringBuilder recv = new StringBuilder(100);
- int nlchars = 0;
-
- do {
- int i = in.read();
- if (i == -1) {
- throw new HttpException(ProxyInfo.ProxyType.HTTP, "Invalid response");
- }
-
- char c = (char) i;
- recv.append(c);
- if (recv.length() > 1024) {
- throw new HttpException(ProxyInfo.ProxyType.HTTP, "Received header longer then 1024 chars");
- }
- if ((nlchars == 0 || nlchars == 2) && c == '\r') {
- nlchars++;
- } else if ((nlchars == 1 || nlchars == 3) && c == '\n') {
- nlchars++;
- } else {
- nlchars = 0;
- }
- } while (nlchars != 4);
-
- String recvStr = recv.toString();
-
- BufferedReader br = new BufferedReader(new StringReader(recvStr));
- String response = br.readLine();
-
- if (response == null) {
- throw new HttpException(ProxyInfo.ProxyType.HTTP, "Empty proxy response");
- }
-
- Matcher m = RESPONSE_PATTERN.matcher(response);
- if (!m.matches()) {
- throw new HttpException(ProxyInfo.ProxyType.HTTP, "Unexpected proxy response");
- }
-
- int code = Integer.parseInt(m.group(1));
-
- if (code != HttpURLConnection.HTTP_OK) {
- throw new HttpException(ProxyInfo.ProxyType.HTTP, "Invalid return status code: " + code);
- }
-
- return socket;
- } catch (RuntimeException rtex) {
- closeSocket(socket);
- throw rtex;
- } catch (Exception ex) {
- closeSocket(socket);
- throw new HttpException(ProxyInfo.ProxyType.HTTP, ex.toString(), ex);
- }
-
- }
-
- /**
- * Closes socket silently.
- */
- private void closeSocket(final Socket socket) {
- try {
- if (socket != null) {
- socket.close();
- }
- } catch (Exception ignore) {
- }
- }
-
- private static final Pattern RESPONSE_PATTERN =
- Pattern.compile("HTTP/\\S+\\s(\\d+)\\s(.*)\\s*");
-}
\ No newline at end of file
diff --git a/jodd-http/src/main/java/jodd/http/net/SSLSocketHttpConnectionProvider.java b/jodd-http/src/main/java/jodd/http/net/SSLSocketHttpConnectionProvider.java
deleted file mode 100644
index 23357cde1..000000000
--- a/jodd-http/src/main/java/jodd/http/net/SSLSocketHttpConnectionProvider.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.net;
-
-import jodd.http.ProxyInfo;
-
-import javax.net.SocketFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocketFactory;
-
-/**
- * Custom SSL socket http connection provider.
- */
-public class SSLSocketHttpConnectionProvider extends SocketHttpConnectionProvider {
-
- private final SSLSocketFactory socketFactory;
-
- public SSLSocketHttpConnectionProvider(final SSLSocketFactory sslSocketFactory) {
- this.socketFactory = sslSocketFactory;
- }
-
- public SSLSocketHttpConnectionProvider(final SSLContext sslContext) {
- this.socketFactory = sslContext.getSocketFactory();
- }
-
- @Override
- protected SocketFactory resolveSocketFactory(
- final ProxyInfo proxy,
- final boolean ssl,
- final boolean trustAllCertificates,
- final int connectionTimeout) {
- return socketFactory;
- }
-}
diff --git a/jodd-http/src/main/java/jodd/http/net/SocketHttpConnection.java b/jodd-http/src/main/java/jodd/http/net/SocketHttpConnection.java
deleted file mode 100644
index 39f347f0b..000000000
--- a/jodd-http/src/main/java/jodd/http/net/SocketHttpConnection.java
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.net;
-
-import jodd.http.HttpConnection;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-
-/**
- * Socket-based {@link jodd.http.HttpConnection}.
- * @see SocketHttpConnectionProvider
- */
-public class SocketHttpConnection implements HttpConnection {
-
- protected final Socket socket;
-
- public SocketHttpConnection(final Socket socket) {
- this.socket = socket;
- }
-
- @Override
- public void init() throws IOException {
- if (timeout >= 0) {
- socket.setSoTimeout(timeout);
- }
- }
-
- @Override
- public OutputStream getOutputStream() throws IOException {
- return socket.getOutputStream();
- }
-
- @Override
- public InputStream getInputStream() throws IOException {
- return socket.getInputStream();
- }
-
- @Override
- public void close() {
- try {
- socket.close();
- } catch (Throwable ignore) {
- }
- }
-
- @Override
- public void setTimeout(final int milliseconds) {
- this.timeout = milliseconds;
- }
-
- /**
- * Returns Socket
used by this connection.
- */
- public Socket getSocket() {
- return socket;
- }
-
- private int timeout;
-}
\ No newline at end of file
diff --git a/jodd-http/src/main/java/jodd/http/net/SocketHttpConnectionProvider.java b/jodd-http/src/main/java/jodd/http/net/SocketHttpConnectionProvider.java
deleted file mode 100644
index 1c16cd596..000000000
--- a/jodd-http/src/main/java/jodd/http/net/SocketHttpConnectionProvider.java
+++ /dev/null
@@ -1,274 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.net;
-
-import jodd.http.HttpConnection;
-import jodd.http.HttpConnectionProvider;
-import jodd.http.HttpException;
-import jodd.http.HttpRequest;
-import jodd.http.ProxyInfo;
-import jodd.http.Sockets;
-import jodd.util.StringUtil;
-
-import javax.net.SocketFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLParameters;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.SSLSocketFactory;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.security.KeyManagementException;
-import java.security.NoSuchAlgorithmException;
-
-/**
- * Socket factory for HTTP proxy.
- */
-public class SocketHttpConnectionProvider implements HttpConnectionProvider {
-
- protected ProxyInfo proxy = ProxyInfo.directProxy();
- protected String secureEnabledProtocols = System.getProperty("https.protocols");
- protected String sslProtocol = "TLSv1.1";
-
- /**
- * Defines proxy to use for created sockets.
- */
- @Override
- public void useProxy(final ProxyInfo proxyInfo) {
- proxy = proxyInfo;
- }
-
- /**
- * CSV of default enabled secured protocols. By default the value is
- * read from system property https.protocols
.
- */
- public void setSecuredProtocols(final String secureEnabledProtocols) {
- this.secureEnabledProtocols = secureEnabledProtocols;
- }
-
- /**
- * Returns current SSL protocol used.
- */
- public String getSslProtocol() {
- return sslProtocol;
- }
-
- /**
- * Sets default SSL protocol to use. One of "SSL", "TLSv1.2", "TLSv1.1", "TLSv1".
- */
- public SocketHttpConnectionProvider setSslProtocol(final String sslProtocol) {
- this.sslProtocol = sslProtocol;
- return this;
- }
-
- /**
- * Creates new connection from current {@link jodd.http.HttpRequest request}.
- *
- * @see #createSocket(String, int, int)
- */
- @Override
- public HttpConnection createHttpConnection(final HttpRequest httpRequest) throws IOException {
- final SocketHttpConnection httpConnection;
-
- final boolean https = httpRequest.protocol().equalsIgnoreCase("https");
-
- if (https) {
- SSLSocket sslSocket = createSSLSocket(
- httpRequest.host(),
- httpRequest.port(),
- httpRequest.connectionTimeout(),
- httpRequest.trustAllCertificates(),
- httpRequest.verifyHttpsHost()
- );
-
- httpConnection = new SocketHttpSecureConnection(sslSocket);
- }
- else {
- Socket socket = createSocket(httpRequest.host(), httpRequest.port(), httpRequest.connectionTimeout());
-
- httpConnection = new SocketHttpConnection(socket);
- }
-
- // prepare connection config
-
- httpConnection.setTimeout(httpRequest.timeout());
-
- try {
- // additional socket initialization
-
- httpConnection.init();
- }
- catch (Throwable throwable) { // @wjw_add
- httpConnection.close();
-
- throw new HttpException(throwable);
- }
-
- return httpConnection;
- }
-
- /**
- * Creates a socket using socket factory.
- */
- protected Socket createSocket(final String host, final int port, final int connectionTimeout) throws IOException {
- final SocketFactory socketFactory = resolveSocketFactory(proxy, false, false, connectionTimeout);
-
- if (connectionTimeout < 0) {
- return socketFactory.createSocket(host, port);
- }
- else {
- // creates unconnected socket
- Socket socket = socketFactory.createSocket();
-
- socket.connect(new InetSocketAddress(host, port), connectionTimeout);
-
- return socket;
- }
- }
-
- /**
- * Creates a SSL socket. Enables default secure enabled protocols if specified.
- */
- protected SSLSocket createSSLSocket(
- final String host, final int port, final int connectionTimeout,
- final boolean trustAll, final boolean verifyHttpsHost) throws IOException {
-
- final SocketFactory socketFactory = resolveSocketFactory(proxy, true, trustAll, connectionTimeout);
-
- final Socket socket;
-
- if (connectionTimeout < 0) {
- socket = socketFactory.createSocket(host, port);
- }
- else {
- // creates unconnected socket
- // unfortunately, this does not work always
-
-// sslSocket = (SSLSocket) socketFactory.createSocket();
-// sslSocket.connect(new InetSocketAddress(host, port), connectionTimeout);
-
- //
- // Note: SSLSocketFactory has several create() methods.
- // Those that take arguments all connect immediately
- // and have no options for specifying a connection timeout.
- //
- // So, we have to create a socket and connect it (with a
- // connection timeout), then have the SSLSocketFactory wrap
- // the already-connected socket.
- //
- socket = Sockets.connect(host, port, connectionTimeout);
- //sock.setSoTimeout(readTimeout);
- //socket.connect(new InetSocketAddress(host, port), connectionTimeout);
-
- // continue to wrap this plain socket with ssl socket...
- }
-
-
- // wrap plain socket in an SSL socket
-
- final SSLSocket sslSocket;
-
- if (socket instanceof SSLSocket) {
- sslSocket = (SSLSocket) socket;
- }
- else {
- if (socketFactory instanceof SSLSocketFactory) {
- sslSocket = (SSLSocket) ((SSLSocketFactory)socketFactory).createSocket(socket, host, port, true);
- }
- else {
- sslSocket = (SSLSocket) (getDefaultSSLSocketFactory(trustAll)).createSocket(socket, host, port, true);
- }
- }
-
- // sslSocket is now ready
-
- if (secureEnabledProtocols != null) {
- final String[] values = StringUtil.splitc(secureEnabledProtocols, ',');
-
- StringUtil.trimAll(values);
-
- sslSocket.setEnabledProtocols(values);
- }
-
- // set SSL parameters to allow host name verifier
-
- if (verifyHttpsHost) {
- final SSLParameters sslParams = new SSLParameters();
-
- sslParams.setEndpointIdentificationAlgorithm("HTTPS");
-
- sslSocket.setSSLParameters(sslParams);
- }
-
- return sslSocket;
- }
-
- /**
- * Returns default SSL socket factory allowing setting trust managers.
- */
- protected SSLSocketFactory getDefaultSSLSocketFactory(final boolean trustAllCertificates) throws IOException {
- if (trustAllCertificates) {
- try {
- SSLContext sc = SSLContext.getInstance(sslProtocol);
- sc.init(null, TrustManagers.TRUST_ALL_CERTS, new java.security.SecureRandom());
- return sc.getSocketFactory();
- }
- catch (NoSuchAlgorithmException | KeyManagementException e) {
- throw new IOException(e);
- }
- } else {
- return (SSLSocketFactory) SSLSocketFactory.getDefault();
- }
- }
-
- /**
- * Returns socket factory based on proxy type and SSL requirements.
- */
- protected SocketFactory resolveSocketFactory(
- final ProxyInfo proxy,
- final boolean ssl,
- final boolean trustAllCertificates,
- final int connectionTimeout) throws IOException {
-
- switch (proxy.getProxyType()) {
- case NONE:
- if (ssl) {
- return getDefaultSSLSocketFactory(trustAllCertificates);
- }
- else {
- return SocketFactory.getDefault();
- }
- case HTTP:
- return new HTTPProxySocketFactory(proxy, connectionTimeout);
- case SOCKS4:
- return new Socks4ProxySocketFactory(proxy, connectionTimeout);
- case SOCKS5:
- return new Socks5ProxySocketFactory(proxy, connectionTimeout);
- default:
- throw new HttpException("Invalid proxy type " + proxy.getProxyType());
- }
- }
-}
diff --git a/jodd-http/src/main/java/jodd/http/net/SocketHttpSecureConnection.java b/jodd-http/src/main/java/jodd/http/net/SocketHttpSecureConnection.java
deleted file mode 100644
index b60ba3e2e..000000000
--- a/jodd-http/src/main/java/jodd/http/net/SocketHttpSecureConnection.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.net;
-
-import javax.net.ssl.SSLSocket;
-import java.io.IOException;
-
-public class SocketHttpSecureConnection extends SocketHttpConnection {
- private final SSLSocket sslSocket;
-
- public SocketHttpSecureConnection(final SSLSocket socket) {
- super(socket);
- this.sslSocket = socket;
- }
-
- @Override
- public void init() throws IOException {
- super.init();
-
- sslSocket.startHandshake();
- }
-}
\ No newline at end of file
diff --git a/jodd-http/src/main/java/jodd/http/net/Socks4ProxySocketFactory.java b/jodd-http/src/main/java/jodd/http/net/Socks4ProxySocketFactory.java
deleted file mode 100644
index 4cd167477..000000000
--- a/jodd-http/src/main/java/jodd/http/net/Socks4ProxySocketFactory.java
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.net;
-
-import jodd.http.HttpException;
-import jodd.http.ProxyInfo;
-import jodd.http.Sockets;
-
-import javax.net.SocketFactory;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.Socket;
-
-/**
- * Socket factory for SOCKS4 proxy. This proxy does not do password authentication.
- *
- * See: http://www.openssh.com/txt/socks5.protocol for more details.
- */
-public class Socks4ProxySocketFactory extends SocketFactory {
-
- private final ProxyInfo proxy;
- private final int connectionTimeout;
-
- public Socks4ProxySocketFactory(final ProxyInfo proxy, final int connectionTimeout) {
- this.proxy = proxy;
- this.connectionTimeout = connectionTimeout;
- }
-
- @Override
- public Socket createSocket(final String host, final int port) {
- return createSocks4ProxySocket(host, port);
- }
-
- @Override
- public Socket createSocket(final String host, final int port, final InetAddress localHost, final int localPort) {
- return createSocks4ProxySocket(host, port);
- }
-
- @Override
- public Socket createSocket(final InetAddress host, final int port) {
- return createSocks4ProxySocket(host.getHostAddress(), port);
- }
-
- @Override
- public Socket createSocket(final InetAddress address, final int port, final InetAddress localAddress, final int localPort) {
- return createSocks4ProxySocket(address.getHostAddress(), port);
- }
-
- /**
- * Connects to the SOCKS4 proxy and returns proxified socket.
- */
- private Socket createSocks4ProxySocket(final String host, final int port) {
- Socket socket = null;
- final String proxyHost = proxy.getProxyAddress();
- final int proxyPort = proxy.getProxyPort();
- final String user = proxy.getProxyUsername();
-
- try {
- socket = Sockets.connect(proxyHost, proxyPort, connectionTimeout);
-
- final InputStream in = socket.getInputStream();
- final OutputStream out = socket.getOutputStream();
-
- socket.setTcpNoDelay(true);
-
- byte[] buf = new byte[1024];
-
- // 1) CONNECT
-
- int index = 0;
- buf[index++] = 4;
- buf[index++] = 1;
-
- buf[index++] = (byte) (port >>> 8);
- buf[index++] = (byte) (port & 0xff);
-
- InetAddress addr = InetAddress.getByName(host);
- byte[] byteAddress = addr.getAddress();
- for (byte byteAddres : byteAddress) {
- buf[index++] = byteAddres;
- }
-
- if (user != null) {
- System.arraycopy(user.getBytes(), 0, buf, index, user.length());
- index += user.length();
- }
- buf[index++] = 0;
- out.write(buf, 0, index);
-
- // 2) RESPONSE
-
- int len = 6;
- int s = 0;
- while (s < len) {
- int i = in.read(buf, s, len - s);
- if (i <= 0) {
- throw new HttpException(ProxyInfo.ProxyType.SOCKS4, "stream is closed");
- }
- s += i;
- }
- if (buf[0] != 0) {
- throw new HttpException(ProxyInfo.ProxyType.SOCKS4, "proxy returned VN " + buf[0]);
- }
- if (buf[1] != 90) {
- try {
- socket.close();
- } catch (Exception ignore) {
- }
- throw new HttpException(ProxyInfo.ProxyType.SOCKS4, "proxy returned CD " + buf[1]);
- }
-
- byte[] temp = new byte[2];
- in.read(temp, 0, 2);
-
- return socket;
- } catch (RuntimeException rtex) {
- closeSocket(socket);
- throw rtex;
- } catch (Exception ex) {
- closeSocket(socket);
- throw new HttpException(ProxyInfo.ProxyType.SOCKS4, ex.toString(), ex);
- }
- }
-
- /**
- * Closes socket silently.
- */
- private void closeSocket(final Socket socket) {
- try {
- if (socket != null) {
- socket.close();
- }
- } catch (Exception ignore) {
- }
- }
-}
\ No newline at end of file
diff --git a/jodd-http/src/main/java/jodd/http/net/Socks5ProxySocketFactory.java b/jodd-http/src/main/java/jodd/http/net/Socks5ProxySocketFactory.java
deleted file mode 100644
index eeb05771e..000000000
--- a/jodd-http/src/main/java/jodd/http/net/Socks5ProxySocketFactory.java
+++ /dev/null
@@ -1,231 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.net;
-
-import jodd.http.HttpException;
-import jodd.http.ProxyInfo;
-import jodd.http.Sockets;
-
-import javax.net.SocketFactory;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.Socket;
-
-/**
- * Socket factory for SOCKS5 proxy.
- *
- * See: http://www.ietf.org/rfc/rfc1928.txt
- */
-public class Socks5ProxySocketFactory extends SocketFactory {
-
- private final ProxyInfo proxy;
- private final int connectionTimeout;
-
- public Socks5ProxySocketFactory(final ProxyInfo proxy, final int connectionTimeout) {
- this.proxy = proxy;
- this.connectionTimeout = connectionTimeout;
- }
-
- @Override
- public Socket createSocket(final String host, final int port) {
- return createSocks5ProxySocket(host, port);
- }
-
- @Override
- public Socket createSocket(final String host, final int port, final InetAddress localHost, final int localPort) {
- return createSocks5ProxySocket(host, port);
- }
-
- @Override
- public Socket createSocket(final InetAddress host, final int port) {
- return createSocks5ProxySocket(host.getHostAddress(), port);
- }
-
- @Override
- public Socket createSocket(final InetAddress address, final int port, final InetAddress localAddress, final int localPort) {
- return createSocks5ProxySocket(address.getHostAddress(), port);
- }
-
- private Socket createSocks5ProxySocket(final String host, final int port) {
- Socket socket = null;
-
- String proxyAddress = proxy.getProxyAddress();
- int proxyPort = proxy.getProxyPort();
- String user = proxy.getProxyUsername();
- String passwd = proxy.getProxyPassword();
-
- try {
- socket = Sockets.connect(proxyAddress, proxyPort, connectionTimeout);
-
- final InputStream in = socket.getInputStream();
- final OutputStream out = socket.getOutputStream();
-
- socket.setTcpNoDelay(true);
-
- byte[] buf = new byte[1024];
- int index = 0;
-
- // 1) VERSION IDENT/METHOD SELECTION
-
- buf[index++] = 5;
-
- buf[index++] = 2;
- buf[index++] = 0; // NO AUTHENTICATION REQUIRED
- buf[index++] = 2; // USERNAME/PASSWORD
-
- out.write(buf, 0, index);
-
- // 2) RESPONSE
- // in.read(buf, 0, 2);
- readBytes(in, buf, 2);
-
- boolean check = false;
- switch ((buf[1]) & 0xff) {
- case 0: // NO AUTHENTICATION REQUIRED
- check = true;
- break;
- case 2: // USERNAME/PASSWORD
- if (user == null || passwd == null) {
- break;
- }
-
- // 3) USER/PASS REQUEST
-
- index = 0;
- buf[index++] = 1;
- buf[index++] = (byte) (user.length());
- System.arraycopy(user.getBytes(), 0, buf, index, user.length());
-
- index += user.length();
- buf[index++] = (byte) (passwd.length());
- System.arraycopy(passwd.getBytes(), 0, buf, index, passwd.length());
- index += passwd.length();
-
- out.write(buf, 0, index);
-
- // 4) RESPONSE, VERIFIED
- // in.read(buf, 0, 2);
- readBytes(in, buf, 2);
- if (buf[1] == 0) {
- check = true;
- }
- break;
- default:
- }
-
- if (!check) {
- try {
- socket.close();
- } catch (Exception ignore) {
- }
- throw new HttpException(ProxyInfo.ProxyType.SOCKS5, "check failed");
- }
-
- // 5) CONNECT
-
- index = 0;
- buf[index++] = 5;
- buf[index++] = 1; // CONNECT
- buf[index++] = 0;
-
- byte[] hostb = host.getBytes();
- int len = hostb.length;
- buf[index++] = 3; // DOMAINNAME
- buf[index++] = (byte) (len);
- System.arraycopy(hostb, 0, buf, index, len);
-
- index += len;
- buf[index++] = (byte) (port >>> 8);
- buf[index++] = (byte) (port & 0xff);
-
- out.write(buf, 0, index);
-
- // 6) RESPONSE
-
- // in.read(buf, 0, 4);
- readBytes(in, buf, 4);
-
- if (buf[1] != 0) {
- try {
- socket.close();
- } catch (Exception ignore) {
- }
- throw new HttpException(ProxyInfo.ProxyType.SOCKS5, "proxy returned " + buf[1]);
- }
-
- switch (buf[3] & 0xff) {
- case 1:
- // in.read(buf, 0, 6);
- readBytes(in, buf, 6);
- break;
- case 3:
- // in.read(buf, 0, 1);
- readBytes(in, buf, 1);
- // in.read(buf, 0, buf[0]+2);
- readBytes(in, buf, (buf[0] & 0xff) + 2);
- break;
- case 4:
- // in.read(buf, 0, 18);
- readBytes(in, buf, 18);
- break;
- default:
- }
- return socket;
- } catch (RuntimeException rttex) {
- closeSocket(socket);
- throw rttex;
- } catch (Exception ex) {
- closeSocket(socket);
- throw new HttpException(ProxyInfo.ProxyType.SOCKS5, ex.toString(), ex);
- }
- }
-
- private void readBytes(final InputStream in, final byte[] buf, final int len) throws IOException {
- int s = 0;
- while (s < len) {
- int i = in.read(buf, s, len - s);
- if (i <= 0) {
- throw new HttpException(ProxyInfo.ProxyType.SOCKS5, "stream is closed");
- }
- s += i;
- }
- }
-
- /**
- * Closes socket silently.
- */
- private void closeSocket(final Socket socket) {
- try {
- if (socket != null) {
- socket.close();
- }
- } catch (Exception ignore) {
- }
- }
-
-}
\ No newline at end of file
diff --git a/jodd-http/src/main/java/jodd/http/net/TrustManagers.java b/jodd-http/src/main/java/jodd/http/net/TrustManagers.java
deleted file mode 100644
index f2ef34ded..000000000
--- a/jodd-http/src/main/java/jodd/http/net/TrustManagers.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-package jodd.http.net;
-
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509ExtendedTrustManager;
-import java.net.Socket;
-import java.security.cert.X509Certificate;
-
-public class TrustManagers {
- /**
- * Array of trust managers that allow all certificates, done in Java8 proper-way.
- */
- public static TrustManager[] TRUST_ALL_CERTS = new TrustManager[]{
- new X509ExtendedTrustManager() {
- @Override
- public void checkClientTrusted(final X509Certificate[] x509Certificates, final String s) {
- }
- @Override
- public void checkServerTrusted(final X509Certificate[] x509Certificates, final String s) {
- }
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return null;
- }
- @Override
- public void checkClientTrusted(final X509Certificate[] x509Certificates, final String s, final Socket socket) {
- }
- @Override
- public void checkServerTrusted(final X509Certificate[] x509Certificates, final String s, final Socket socket) {
- }
- @Override
- public void checkClientTrusted(final X509Certificate[] x509Certificates, final String s, final SSLEngine sslEngine) {
- }
- @Override
- public void checkServerTrusted(final X509Certificate[] x509Certificates, final String s, final SSLEngine sslEngine) {
- }
- }
- };
-}
diff --git a/jodd-http/src/main/java/jodd/http/net/package-info.java b/jodd-http/src/main/java/jodd/http/net/package-info.java
deleted file mode 100644
index 0e3651940..000000000
--- a/jodd-http/src/main/java/jodd/http/net/package-info.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-/**
- * Implementations and core HTTP stuff.
- */
-package jodd.http.net;
\ No newline at end of file
diff --git a/jodd-http/src/main/java/jodd/http/package-info.java b/jodd-http/src/main/java/jodd/http/package-info.java
deleted file mode 100644
index 39fb97dc3..000000000
--- a/jodd-http/src/main/java/jodd/http/package-info.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-/**
- * Tiny, raw and simple socket-based HTTP client.
- */
-package jodd.http;
\ No newline at end of file
diff --git a/jodd-http/src/main/java/jodd/http/up/ByteArrayUploadable.java b/jodd-http/src/main/java/jodd/http/up/ByteArrayUploadable.java
deleted file mode 100644
index 8d46ce44c..000000000
--- a/jodd-http/src/main/java/jodd/http/up/ByteArrayUploadable.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.up;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-
-/**
- * Uploadable wrapper of byte array
.
- */
-public class ByteArrayUploadable implements Uploadable {
-
- protected final byte[] byteArray;
- protected final String fileName;
- protected final String mimeType;
-
- public ByteArrayUploadable(final byte[] byteArray, final String fileName) {
- this.byteArray = byteArray;
- this.fileName = fileName;
- this.mimeType = null;
- }
-
- public ByteArrayUploadable(final byte[] byteArray, final String fileName, final String mimeType) {
- this.byteArray = byteArray;
- this.fileName = fileName;
- this.mimeType = mimeType;
- }
-
- @Override
- public byte[] getContent() {
- return byteArray;
- }
-
- @Override
- public byte[] getBytes() {
- return byteArray;
- }
-
- @Override
- public String getFileName() {
- return fileName;
- }
-
- @Override
- public String getMimeType() {
- return mimeType;
- }
-
- @Override
- public int getSize() {
- return byteArray.length;
- }
-
- @Override
- public InputStream openInputStream() {
- return new ByteArrayInputStream(byteArray);
- }
-}
\ No newline at end of file
diff --git a/jodd-http/src/main/java/jodd/http/up/FileUploadable.java b/jodd-http/src/main/java/jodd/http/up/FileUploadable.java
deleted file mode 100644
index d249c3f6e..000000000
--- a/jodd-http/src/main/java/jodd/http/up/FileUploadable.java
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.up;
-
-import jodd.http.HttpException;
-import jodd.io.FileNameUtil;
-import jodd.io.FileUtil;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * File uploadable.
- */
-public class FileUploadable implements Uploadable {
-
- protected final File file;
- protected final String fileName;
- protected final String mimeType;
-
- public FileUploadable(final File file) {
- this.file = file;
- this.fileName = FileNameUtil.getName(file.getName());
- this.mimeType = null;
- }
-
- public FileUploadable(final File file, final String fileName, final String mimeType) {
- this.file = file;
- this.fileName = fileName;
- this.mimeType = mimeType;
- }
-
- @Override
- public File getContent() {
- return file;
- }
-
- @Override
- public byte[] getBytes() {
- try {
- return FileUtil.readBytes(file);
- } catch (IOException ioex) {
- throw new HttpException(ioex);
- }
- }
-
- @Override
- public String getFileName() {
- return fileName;
- }
-
- @Override
- public String getMimeType() {
- return mimeType;
- }
-
- @Override
- public int getSize() {
- return (int) file.length();
- }
-
- @Override
- public InputStream openInputStream() throws IOException {
- return new FileInputStream(file);
- }
-}
\ No newline at end of file
diff --git a/jodd-http/src/main/java/jodd/http/up/Uploadable.java b/jodd-http/src/main/java/jodd/http/up/Uploadable.java
deleted file mode 100644
index 62b4613a1..000000000
--- a/jodd-http/src/main/java/jodd/http/up/Uploadable.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.up;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Common interface of uploaded content for {@link jodd.http.HttpBase#form() form parameters}.
- * All supported objects that can be uploaded using
- * the {@link jodd.http.HttpBase#form(String, Object)} has to
- * be wrapped with this interface.
- */
-public interface Uploadable {
-
- /**
- * Returns the original content.
- */
- public T getContent();
-
- /**
- * Returns content bytes.
- */
- public byte[] getBytes();
-
- /**
- * Returns content file name.
- * If null
, the field's name will be used.
- */
- public String getFileName();
-
- /**
- * Returns MIME type. If null
,
- * MIME type will be determined from
- * {@link #getFileName() file name's} extension.
- */
- public String getMimeType();
-
- /**
- * Returns size in bytes.
- */
- public int getSize();
-
- /**
- * Opens InputStream
. User is responsible
- * for closing it.
- */
- public InputStream openInputStream() throws IOException;
-
-}
\ No newline at end of file
diff --git a/jodd-http/src/main/java/jodd/http/up/package-info.java b/jodd-http/src/main/java/jodd/http/up/package-info.java
deleted file mode 100644
index 659d7726e..000000000
--- a/jodd-http/src/main/java/jodd/http/up/package-info.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-/**
- * Uploadable content and few implementations.
- */
-package jodd.http.up;
\ No newline at end of file
diff --git a/jodd-http/src/main/resources/META-INF/LICENSE b/jodd-http/src/main/resources/META-INF/LICENSE
deleted file mode 100644
index a040a3b95..000000000
--- a/jodd-http/src/main/resources/META-INF/LICENSE
+++ /dev/null
@@ -1,24 +0,0 @@
-Copyright (c) 2003-present, Jodd Team (https://jodd.org)
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice,
-this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright
-notice, this list of conditions and the following disclaimer in the
-documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
diff --git a/jodd-http/src/test/java/jodd/http/BufferTest.java b/jodd-http/src/test/java/jodd/http/BufferTest.java
deleted file mode 100644
index 28c546afa..000000000
--- a/jodd-http/src/test/java/jodd/http/BufferTest.java
+++ /dev/null
@@ -1,300 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.http.up.Uploadable;
-import jodd.util.StringUtil;
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-
-class BufferTest {
-
- class SimpleUploadable implements Uploadable {
-
- final int size;
- final byte[] bytes;
-
- public SimpleUploadable(final int size) {
- this.size = size;
- bytes = new byte[size];
-
- Arrays.fill(bytes, (byte) '*');
- }
- public SimpleUploadable(final int size, final char c) {
- this.size = size;
- bytes = new byte[size];
-
- Arrays.fill(bytes, (byte) c);
- }
-
- public Object getContent() {
- return bytes;
- }
-
- public byte[] getBytes() {
- return bytes;
- }
-
- public String getFileName() {
- return null;
- }
-
- public String getMimeType() {
- return null;
- }
-
- public int getSize() {
- return size;
- }
-
- public InputStream openInputStream() throws IOException {
- return new ByteArrayInputStream(bytes);
- }
- }
-
- class SimpleProgressListener extends HttpProgressListener {
-
- StringBuilder sb = new StringBuilder();
-
- @Override
- public int callbackSize(final int size) {
- return 10;
- }
-
- @Override
- public void transferred(final int len) {
- if (sb.length() > 0) {
- sb.append(':');
- }
- sb.append(len);
- }
- }
-
- @Test
- void testBufferAppend() {
- final Buffer buffer = new Buffer();
-
- assertEquals(0, buffer.size());
- assertEquals(0, buffer.list.size());
- assertNull(buffer.last);
-
- buffer.append("Hey");
-
- assertEquals(3, buffer.size());
- assertNotNull(buffer.last);
-
- buffer.append('!');
- buffer.append(91);
-
- assertEquals(6, buffer.size());
- assertEquals(1, buffer.list.size());
-
- buffer.append(new SimpleUploadable(100));
-
- assertEquals(106, buffer.size());
- assertEquals(2, buffer.list.size());
- assertNull(buffer.last);
-
- buffer.append("x");
-
- assertEquals(107, buffer.size());
- assertEquals(3, buffer.list.size());
- assertNotNull(buffer.last);
-
- final Buffer buffer2 = new Buffer();
- buffer2.append(new SimpleUploadable(20));
-
- buffer.append(buffer2);
-
- assertEquals(127, buffer.size());
- assertEquals(4, buffer.list.size());
- assertNull(buffer.last);
- }
-
- @Test
- void testBufferWrite1() throws IOException {
- Buffer buffer;
- ByteArrayOutputStream baos;
- SimpleProgressListener hpl;
-
- // size < callbackSize
-
- buffer = new Buffer();
- buffer.append("12345");
-
- baos = new ByteArrayOutputStream();
- hpl = new SimpleProgressListener();
- buffer.writeTo(baos, hpl);
-
- assertEquals("12345", baos.toString(StandardCharsets.ISO_8859_1.name()));
- assertEquals("0:5", hpl.sb.toString());
-
- // size = callbackSize
-
- buffer = new Buffer();
- buffer.append("1234567890");
-
- baos = new ByteArrayOutputStream();
- hpl = new SimpleProgressListener();
- buffer.writeTo(baos, hpl);
-
- assertEquals("1234567890", baos.toString(StandardCharsets.ISO_8859_1.name()));
- assertEquals("0:10", hpl.sb.toString());
-
- // size > callbackSize
-
- buffer = new Buffer();
- buffer.append("1234567890ABC");
-
- baos = new ByteArrayOutputStream();
- hpl = new SimpleProgressListener();
- buffer.writeTo(baos, hpl);
-
- assertEquals("1234567890ABC", baos.toString(StandardCharsets.ISO_8859_1.name()));
- assertEquals("0:10:13", hpl.sb.toString());
- }
-
- @Test
- void testBufferWrite2() throws IOException {
- Buffer buffer;
- ByteArrayOutputStream baos;
- SimpleProgressListener hpl;
-
- // size > callbackSize
-
- buffer = new Buffer();
- buffer.append("12345");
- buffer.append(new SimpleUploadable(10));
- buffer.append("67");
- assertEquals(17, buffer.size());
-
- baos = new ByteArrayOutputStream();
- hpl = new SimpleProgressListener();
- buffer.writeTo(baos, hpl);
-
- assertEquals("12345**********67", baos.toString(StandardCharsets.ISO_8859_1.name()));
- assertEquals("0:10:17", hpl.sb.toString());
-
- // size = callbackSize
-
- buffer = new Buffer();
- buffer.append("12345");
- buffer.append(new SimpleUploadable(5));
- assertEquals(10, buffer.size());
-
- baos = new ByteArrayOutputStream();
- hpl = new SimpleProgressListener();
- buffer.writeTo(baos, hpl);
-
- assertEquals("12345*****", baos.toString(StandardCharsets.ISO_8859_1.name()));
- assertEquals("0:10", hpl.sb.toString());
-
- // size > callbackSize
-
- buffer = new Buffer();
- buffer.append("12345");
- buffer.append(new SimpleUploadable(21));
- buffer.append("X");
- assertEquals(27, buffer.size());
-
- baos = new ByteArrayOutputStream();
- hpl = new SimpleProgressListener();
- buffer.writeTo(baos, hpl);
-
- assertEquals("12345*********************X", baos.toString(StandardCharsets.ISO_8859_1.name()));
- assertEquals("0:10:20:27", hpl.sb.toString());
- }
-
- @Test
- void testBufferWrite3() throws IOException {
- final Buffer buffer;
- ByteArrayOutputStream baos;
- SimpleProgressListener hpl;
-
- //
-
- buffer = new Buffer();
- buffer.append(new SimpleUploadable(4, '*'));
- buffer.append(new SimpleUploadable(4, '+'));
- buffer.append(new SimpleUploadable(4, '-'));
- assertEquals(12, buffer.size());
-
- baos = new ByteArrayOutputStream();
- hpl = new SimpleProgressListener();
- buffer.writeTo(baos, hpl);
-
- assertEquals("****++++----", baos.toString(StandardCharsets.ISO_8859_1.name()));
- assertEquals("0:10:12", hpl.sb.toString());
-
- //
-
- buffer.append("12345678");
- assertEquals(20, buffer.size());
-
- baos = new ByteArrayOutputStream();
- hpl = new SimpleProgressListener();
- buffer.writeTo(baos, hpl);
-
- assertEquals("****++++----12345678", baos.toString(StandardCharsets.ISO_8859_1.name()));
- assertEquals("0:10:20", hpl.sb.toString());
-
- //
-
- buffer.append("A");
- assertEquals(21, buffer.size());
-
- baos = new ByteArrayOutputStream();
- hpl = new SimpleProgressListener();
- buffer.writeTo(baos, hpl);
-
- assertEquals("****++++----12345678A", baos.toString(StandardCharsets.ISO_8859_1.name()));
- assertEquals("0:10:20:21", hpl.sb.toString());
-
- //
-
- buffer.append(new SimpleUploadable(30, '#'));
- assertEquals(51, buffer.size());
-
- baos = new ByteArrayOutputStream();
- hpl = new SimpleProgressListener();
- buffer.writeTo(baos, hpl);
-
- assertEquals("****++++----12345678A" + StringUtil.repeat('#', 30), baos.toString(StandardCharsets.ISO_8859_1.name()));
- assertEquals("0:10:20:30:40:50:51", hpl.sb.toString());
-
- }
-
-}
diff --git a/jodd-http/src/test/java/jodd/http/CookieTest.java b/jodd-http/src/test/java/jodd/http/CookieTest.java
deleted file mode 100644
index e7ceddebd..000000000
--- a/jodd-http/src/test/java/jodd/http/CookieTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class CookieTest {
-
- @Test
- void testCookieParsing() {
- Cookie cookie = new Cookie("name=value");
-
- assertEquals("name", cookie.getName());
- assertEquals("value", cookie.getValue());
- assertEquals(null, cookie.getExpires());
-
- cookie = new Cookie("name2=value2; Expires=Wed, 09 Jun 2021 10:18:14 GMT");
-
- assertEquals("name2", cookie.getName());
- assertEquals("value2", cookie.getValue());
- assertEquals("Wed, 09 Jun 2021 10:18:14 GMT", cookie.getExpires());
-
- cookie = new Cookie("LSID=DQAAAEaem_vYg; Path=/accounts; Secure; Expires=Wed, 13 Jan 2021 22:23:01 GMT; HttpOnly");
-
- assertEquals("LSID", cookie.getName());
- assertEquals("DQAAAEaem_vYg", cookie.getValue());
- assertEquals("/accounts", cookie.getPath());
- assertTrue(cookie.isSecure());
- assertTrue(cookie.isHttpOnly());
- }
-
- @Test
- void test395() {
- Cookie cookie = new Cookie("name=value;");
-
- assertEquals("name", cookie.getName());
- assertEquals("value", cookie.getValue());
-
- cookie = new Cookie("name=value; ");
-
- assertEquals("name", cookie.getName());
- assertEquals("value", cookie.getValue());
-
- cookie = new Cookie("p_skey=UIJeeZgODkPQgiVcwHJBhq9mYrZC9JdpYF6SCZ3fNfY_; PATH=/; DOMAIN=mail.qq.com; ;");
-
- assertEquals("p_skey", cookie.getName());
- assertEquals("UIJeeZgODkPQgiVcwHJBhq9mYrZC9JdpYF6SCZ3fNfY_", cookie.getValue());
- }
-
- @Test
- void testSpecialCookieValues() {
- Cookie cookie = new Cookie("name=value");
-
- assertEquals("name", cookie.getName());
- assertEquals("value", cookie.getValue());
-
- cookie = new Cookie("name=value;");
-
- assertEquals("name", cookie.getName());
- assertEquals("value", cookie.getValue());
-
- // duplicated value
-
- cookie = new Cookie("name=value;a=b;");
-
- assertEquals("name", cookie.getName());
- assertEquals("value", cookie.getValue());
-
- // empty value
-
- cookie = new Cookie("name=");
-
- assertEquals("name", cookie.getName());
- assertEquals("", cookie.getValue());
-
- // empty name
-
- cookie = new Cookie("=value");
-
- assertEquals(null, cookie.getName());
- assertEquals(null, cookie.getValue());
- }
-
- @Test
- void testSetGetCookieFromRequest() {
- final HttpRequest request = new HttpRequest();
- request.cookies(new Cookie("one", "two"), new Cookie("one2", "two2"));
-
- final Cookie[] cookies = request.cookies();
- assertEquals(2, cookies.length);
-
- assertEquals("one", cookies[0].getName());
- assertEquals("two", cookies[0].getValue());
-
- assertEquals("one2", cookies[1].getName());
- assertEquals("two2", cookies[1].getValue());
-
- }
-}
diff --git a/jodd-http/src/test/java/jodd/http/EchoTestServer.java b/jodd-http/src/test/java/jodd/http/EchoTestServer.java
deleted file mode 100644
index 83895e437..000000000
--- a/jodd-http/src/test/java/jodd/http/EchoTestServer.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Properties;
-
-public class EchoTestServer extends NanoHTTPD {
-
- public EchoTestServer() throws IOException {
- super(8081, new File("."));
- }
-
- public String uri;
-
- public String method;
-
- public Properties header;
-
- public Properties params;
-
- public Properties files;
-
- public Response serve(String uri, String method, Properties header, Properties parms, Properties files) {
- String msg = method + " " + uri;
-
- this.uri = uri;
- this.method = method;
- this.header = header;
- this.params = parms;
- this.files = files;
-
- return new NanoHTTPD.Response(HTTP_OK, MIME_HTML, msg);
- }
-}
\ No newline at end of file
diff --git a/jodd-http/src/test/java/jodd/http/EncodingTest.java b/jodd-http/src/test/java/jodd/http/EncodingTest.java
deleted file mode 100644
index 2b1271a9b..000000000
--- a/jodd-http/src/test/java/jodd/http/EncodingTest.java
+++ /dev/null
@@ -1,269 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.http.fixture.Data;
-import jodd.http.up.ByteArrayUploadable;
-import jodd.net.MimeTypes;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class EncodingTest {
-
- static TestServer testServer;
-
- @BeforeAll
- static void startServer() throws Exception {
- testServer = new TomcatServer();
- testServer.start();
- }
-
- @AfterAll
- static void stopServer() throws Exception {
- testServer.stop();
- }
-
- @Test
- void testContentTypeHeader() {
- final HttpRequest req = HttpRequest.get("localhost/hello");
-
- assertNull(req.contentType());
-
- req.contentType("text/plain;charset=UTF-8");
-
- assertEquals("text/plain", req.mediaType());
- assertEquals("UTF-8", req.charset());
- assertEquals("text/plain;charset=UTF-8", req.contentType());
-
- req.mediaType("text/html");
- assertEquals("text/html;charset=UTF-8", req.contentType());
- req.mediaType(null);
- assertEquals("text/html;charset=UTF-8", req.contentType());
- req.charset("ASCII");
- assertEquals("text/html;charset=ASCII", req.contentType());
- req.charset(null);
- assertEquals("text/html", req.contentType());
-
- req.contentType("text/plain;charset=UTF-8;boundary=123");
- assertEquals("text/plain", req.mediaType());
- assertEquals("UTF-8", req.charset());
- assertEquals("text/plain;charset=UTF-8;boundary=123", req.contentType());
- }
-
- @Test
- void testRequestEncoding1() throws IOException {
- testRequestEncoding(1);
- }
- @Test
- void testRequestEncoding2() throws IOException {
- testRequestEncoding(2);
- }
- @Test
- void testRequestEncoding3() throws IOException {
- testRequestEncoding(3);
- }
- @Test
- void testRequestEncoding4() throws IOException {
- testRequestEncoding(4);
- }
- private void testRequestEncoding(final int i) throws IOException {
- final HttpRequest request =
- (i == 1 || i == 2) ?
- HttpRequest.get("http://localhost:8173/echo?id=12"):
- HttpRequest.post("http://localhost:8173/echo?id=12");
-
- final String utf8String = (i == 1 || i == 3) ? "Hello!" : "хелло!";
- final byte[] utf8Bytes = utf8String.getBytes(StandardCharsets.UTF_8);
- final int utf8StringRealLen = utf8Bytes.length;
-
- request.bodyText(utf8String);
-
- final String rawBody = request.body();
- assertEquals(utf8StringRealLen, rawBody.length());
- assertArrayEquals(utf8Bytes, request.bodyBytes());
-
- final HttpResponse response = request.send();
- assertEquals(200, response.statusCode());
-
- // servlet
-
- if (i < 3) {
- assertTrue(Data.ref.get);
- assertFalse(Data.ref.post);
- } else {
- assertFalse(Data.ref.get);
- assertTrue(Data.ref.post);
- }
-
- assertEquals(String.valueOf(utf8StringRealLen), Data.ref.header.get("content-length"));
- assertEquals("text/html;charset=UTF-8", Data.ref.header.get("content-type"));
- assertEquals(utf8String, Data.ref.body);
-
- // response
-
- assertEquals(String.valueOf(utf8StringRealLen), response.contentLength());
- assertEquals("text/html;charset=UTF-8", response.contentType());
- assertEquals(utf8String, response.bodyText());
- assertEquals(new String(utf8Bytes, StandardCharsets.ISO_8859_1), response.body());
- }
-
- @Test
- void testFormParams1() {
- testFormParams(1);
- }
- @Test
- void testFormParams2() {
- testFormParams(2);
- }
- @Test
- void testFormParams3() {
- testFormParams(3);
- }
- private void testFormParams(final int i) {
- final String encoding = i == 1 ? "UTF-8" : "CP1251";
-
- final HttpRequest request = HttpRequest.post("http://localhost:8173/echo3");
- request.formEncoding(encoding);
-
- if (i == 3) {
- request.charset("UTF-8");
- }
-
- final String value1 = "value";
- final String value2 = "валуе";
-
- request.form("one", value1);
- request.form("two", value2);
- if (i != 3) {
- request.form("enc", encoding);
- }
-
- final HttpResponse httpResponse = request.send();
-
- assertEquals("application/x-www-form-urlencoded", request.mediaType());
- if (i == 3) {
- assertEquals("UTF-8", request.charset());
- assertEquals("CP1251", request.formEncoding);
- } else {
- assertNull(request.charset());
- }
-
- assertFalse(Data.ref.get);
- assertTrue(Data.ref.post);
-
- assertEquals(i == 3 ? 2 : 3, Data.ref.params.size());
- assertEquals(value1, Data.ref.params.get("one"));
- assertEquals(value2, Data.ref.params.get("two"));
- }
-
- @Test
- void testQueryParams1() throws IOException {
- testQueryParams(1);
- }
-
- @Test
- @Disabled("Ignored until we figure out how to enable org.apache.catalina.STRICT_SERVLET_COMPLIANCE")
- void testQueryParams2() throws IOException {
- testQueryParams(2);
- }
- private void testQueryParams(final int i) throws IOException {
- final String encoding = i == 1 ? "UTF-8" : "CP1251";
-
- final HttpRequest request = HttpRequest.get("http://localhost:8173/echo2");
- request.queryEncoding(encoding);
-
- final String value1 = "value";
- final String value2 = "валуе";
-
- request.query("one", value1);
- request.query("two", value2);
- request.query("enc", encoding);
-
- final HttpResponse httpResponse = request.send();
-
- assertTrue(Data.ref.get);
- assertFalse(Data.ref.post);
-
- assertEquals(3, Data.ref.params.size());
- assertEquals(value1, Data.ref.params.get("one"));
- assertEquals(value2, Data.ref.params.get("two"));
- }
-
- @Test
- void testMultipart() {
- final HttpRequest request = HttpRequest.post("http://localhost:8173/echo2");
- request
- .formEncoding("UTF-8") // optional
- .multipart(true);
-
- final String value1 = "value";
- final String value2 = "валуе";
-
- request.form("one", value1);
- request.form("two", value2);
-
- final HttpResponse httpResponse = request.send();
-
- assertEquals("multipart/form-data", request.mediaType());
-
- assertFalse(Data.ref.get);
- assertTrue(Data.ref.post);
-
- assertEquals(value1, Data.ref.parts.get("one"));
- assertEquals(value2, Data.ref.parts.get("two"));
- }
-
- @Test
- void testUploadWithUploadable() throws IOException {
- final HttpResponse response = HttpRequest
- .post("http://localhost:8173/echo2")
- .multipart(true)
- .form("id", "12")
- .form("file", new ByteArrayUploadable(
- "upload тест".getBytes(StandardCharsets.UTF_8), "d ст", MimeTypes.MIME_TEXT_PLAIN))
- .send();
-
- assertEquals(200, response.statusCode());
- assertEquals("OK", response.statusPhrase());
-
- assertEquals("12", Data.ref.params.get("id"));
- assertEquals("upload тест", Data.ref.parts.get("file"));
- assertEquals("d ст", Data.ref.fileNames.get("file"));
- }
-
-
-}
diff --git a/jodd-http/src/test/java/jodd/http/GoogleMapsTest.java b/jodd-http/src/test/java/jodd/http/GoogleMapsTest.java
deleted file mode 100644
index 4d414824b..000000000
--- a/jodd-http/src/test/java/jodd/http/GoogleMapsTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.io.FileUtil;
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.net.URL;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.fail;
-
-class GoogleMapsTest {
-
- @Test
- void testNoBody() throws IOException {
- /*HttpResponse httpResponse = HttpRequest.get("http://maps.googleapis.com/maps/api/geocode/json")
- .query("address", "14621")
- .query("sensor", "false")
- .send();
- */
- URL data = RawTest.class.getResource("2-response.txt");
- byte[] fileContent = FileUtil.readBytes(data.getFile());
-
- HttpResponse httpResponse = HttpResponse.readFrom(new ByteArrayInputStream(fileContent));
-
- try {
- httpResponse.bodyText();
- } catch (Exception ex) {
- fail(ex.toString());
- }
-
- assertEquals("", httpResponse.bodyText());
- }
-
- @Test
- void testNoContentLength() throws IOException {
- URL data = RawTest.class.getResource("3-response.txt");
- byte[] fileContent = FileUtil.readBytes(data.getFile());
-
- HttpResponse httpResponse = HttpResponse.readFrom(new ByteArrayInputStream(fileContent));
-
- assertEquals("Body!", httpResponse.bodyText());
- }
-
-}
diff --git a/jodd-http/src/test/java/jodd/http/HttpBrowserOfflineTest.java b/jodd-http/src/test/java/jodd/http/HttpBrowserOfflineTest.java
deleted file mode 100644
index af532d76c..000000000
--- a/jodd-http/src/test/java/jodd/http/HttpBrowserOfflineTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-class HttpBrowserOfflineTest {
-
- @Test
- void testDefaultParameters() {
- HttpBrowser httpBrowser = new HttpBrowser();
- httpBrowser.setDefaultHeader("aaa", "123");
-
- HttpRequest request = HttpRequest.get("foo.com");
- request.header("bbb", "987");
-
- httpBrowser.addDefaultHeaders(request);
-
- assertEquals(3, request.headerNames().size());
- assertEquals("123", request.header("aaa"));
- assertEquals("987", request.header("bbb"));
- }
-
- @Test
- void testDefaultParametersOverwrite() {
- HttpBrowser httpBrowser = new HttpBrowser();
- httpBrowser.setDefaultHeader("aaa", "123");
-
- HttpRequest request = HttpRequest.get("foo.com");
- request.header("aaa", "987");
-
- httpBrowser.addDefaultHeaders(request);
-
- assertEquals(2, request.headerNames().size());
- assertEquals("987", request.header("aaa"));
- }
-}
diff --git a/jodd-http/src/test/java/jodd/http/HttpBrowserTest.java b/jodd-http/src/test/java/jodd/http/HttpBrowserTest.java
deleted file mode 100644
index 7a541cb6c..000000000
--- a/jodd-http/src/test/java/jodd/http/HttpBrowserTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-class HttpBrowserTest {
-
- static TestServer testServer;
-
- @BeforeAll
- static void startServer() throws Exception {
- testServer = new TomcatServer();
- testServer.start();
- }
-
- @AfterAll
- static void stopServer() throws Exception {
- testServer.stop();
- }
-
- @Test
- void testBrowser() {
- HttpBrowser httpBrowser = new HttpBrowser();
-
- httpBrowser.sendRequest(
- HttpRequest
- .get("localhost:8173/echo?id=17")
- .cookies(new Cookie("waffle", "jam"))
- .bodyText("hello"));
-
- HttpResponse httpResponse = httpBrowser.getHttpResponse();
-
- assertNotNull(httpResponse);
- assertEquals("hello", httpResponse.body());
-
- Cookie[] cookies = httpResponse.cookies();
- assertEquals(1, cookies.length);
-
- assertEquals("waffle", cookies[0].getName());
- assertEquals("jam!", cookies[0].getValue());
- }
-
- @Test
- void testBrowserRedirect() {
- HttpBrowser httpBrowser = new HttpBrowser();
-
- httpBrowser.sendRequest(HttpRequest.get("localhost:8173/redirect"));
-
- HttpResponse httpResponse = httpBrowser.getHttpResponse();
-
- assertEquals(200, httpResponse.statusCode());
- assertEquals("target!", httpResponse.body());
-
- }
-
-}
diff --git a/jodd-http/src/test/java/jodd/http/HttpConnectionTest.java b/jodd-http/src/test/java/jodd/http/HttpConnectionTest.java
deleted file mode 100644
index 9c164ce0f..000000000
--- a/jodd-http/src/test/java/jodd/http/HttpConnectionTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.http.up.ByteArrayUploadable;
-import jodd.io.FileUtil;
-import jodd.net.MimeTypes;
-import jodd.util.StringUtil;
-import org.junit.jupiter.api.Test;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-class HttpConnectionTest {
-
- @Test
- void testEcho() throws IOException {
- final EchoTestServer echoTestServer = new EchoTestServer();
-
- final HttpResponse response = HttpRequest.get("http://localhost:8081/hello?id=12").send();
-
- assertEquals(200, response.statusCode());
- assertEquals("OK", response.statusPhrase());
-
- assertEquals("GET", echoTestServer.method);
- assertEquals("/hello", echoTestServer.uri);
- assertEquals(1, echoTestServer.params.size());
- assertEquals("12", echoTestServer.params.get("id"));
-
- assertEquals("GET /hello", response.body());
-
- echoTestServer.stop();
- }
-
- @Test
- void testUpload() throws IOException {
- final EchoTestServer echoTestServer = new EchoTestServer();
-
- final File file = FileUtil.createTempFile();
- file.deleteOnExit();
-
- FileUtil.writeString(file, "upload тест");
- assertEquals("upload тест", FileUtil.readString(file));
-
- final HttpResponse response = HttpRequest
- .post("http://localhost:8081/hello")
- .form("id", "12")
- .form("file", file)
- .send();
-
- assertEquals(200, response.statusCode());
- assertEquals("OK", response.statusPhrase());
-
- assertEquals("POST", echoTestServer.method);
- assertEquals("12", echoTestServer.params.get("id"));
- final File uploadedFile = new File(echoTestServer.files.get("file").toString());
- assertNotNull(uploadedFile);
- assertEquals("upload тест", FileUtil.readString(uploadedFile));
-
- assertEquals("POST /hello", response.body());
-
- echoTestServer.stop();
- file.delete();
- }
-
- @Test
- void testUploadWithUploadable() throws IOException {
- final EchoTestServer echoTestServer = new EchoTestServer();
-
- final HttpResponse response = HttpRequest
- .post("http://localhost:8081/hello")
- .multipart(true)
- .form("id", "12")
- .form("file", new ByteArrayUploadable(
- "upload тест".getBytes(StandardCharsets.UTF_8), "d ст", MimeTypes.MIME_TEXT_PLAIN))
- .send();
-
- assertEquals(200, response.statusCode());
- assertEquals("OK", response.statusPhrase());
-
- assertEquals("POST", echoTestServer.method);
- assertEquals("12", echoTestServer.params.get("id"));
- final File uploadedFile = new File(echoTestServer.files.get("file").toString());
- assertNotNull(uploadedFile);
- assertEquals("upload тест", FileUtil.readString(uploadedFile));
-
- assertEquals("POST /hello", response.body());
-
- echoTestServer.stop();
- }
-
- @Test
- void testUploadWithMonitor() throws IOException {
- final EchoTestServer echoTestServer = new EchoTestServer();
-
- final File file = FileUtil.createTempFile();
- file.deleteOnExit();
-
- FileUtil.writeString(file, StringUtil.repeat('A', 1024));
-
- final StringBuilder sb = new StringBuilder();
-
- final HttpResponse response = HttpRequest
- .post("http://localhost:8081/hello")
- .form("id", "12")
- .form("file", file)
- .monitor(new HttpProgressListener() {
- @Override
- public void transferred(final int len) {
- sb.append(":" + len);
- }
- })
- .send();
-
- assertEquals(200, response.statusCode());
- assertEquals("OK", response.statusPhrase());
-
- echoTestServer.stop();
- file.delete();
-
- assertEquals(":0:512:1024:148", StringUtil.substring(sb.toString(), 0, -1));
- }
-
-}
diff --git a/jodd-http/src/test/java/jodd/http/HttpHeaderTest.java b/jodd-http/src/test/java/jodd/http/HttpHeaderTest.java
deleted file mode 100644
index 2870949d7..000000000
--- a/jodd-http/src/test/java/jodd/http/HttpHeaderTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-public class HttpHeaderTest {
-
- @Test
- void testSettingHostsHeader_changeWithSet() {
- final HttpRequest httpRequest = HttpRequest.post("jodd.site");
-
- assertEquals("jodd.site", httpRequest.host());
- // calling toString as it will set the HEADER
- assertTrue(httpRequest.toString().contains("jodd.site"));
-
- assertEquals("jodd.site", httpRequest.header(HttpRequest.HEADER_HOST));
-
- // change
- httpRequest.set("oblac.rs");
-
- // is the header changed? first check the header
- assertEquals("oblac.rs", httpRequest.host());
- assertEquals("oblac.rs", httpRequest.header(HttpRequest.HEADER_HOST));
- // the regenerated request should work
- assertTrue(httpRequest.toString().contains("oblac.rs"));
- // after the generation
- assertEquals("oblac.rs", httpRequest.header(HttpRequest.HEADER_HOST));
- }
-
- @Test
- void testSettingHostsHeader_changeWithHost() {
- final HttpRequest httpRequest = HttpRequest.post("jodd.site");
-
- assertEquals("jodd.site", httpRequest.host());
- // calling toString as it will set the HEADER
- assertTrue(httpRequest.toString().contains("jodd.site"));
-
- assertEquals("jodd.site", httpRequest.header(HttpRequest.HEADER_HOST));
-
- // change
- httpRequest.host("oblac.rs");
-
- // is the header changed? first check the header
- assertEquals("oblac.rs", httpRequest.host());
- assertEquals("oblac.rs", httpRequest.header(HttpRequest.HEADER_HOST));
- // the regenerated request should work
- assertTrue(httpRequest.toString().contains("oblac.rs"));
- // after the generation
- assertEquals("oblac.rs", httpRequest.header(HttpRequest.HEADER_HOST));
- }
-
-}
diff --git a/jodd-http/src/test/java/jodd/http/HttpMultiMapTest.java b/jodd-http/src/test/java/jodd/http/HttpMultiMapTest.java
deleted file mode 100644
index 7ba2d1993..000000000
--- a/jodd-http/src/test/java/jodd/http/HttpMultiMapTest.java
+++ /dev/null
@@ -1,194 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import org.junit.jupiter.api.Test;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
-class HttpMultiMapTest {
-
- @Test
- void testAdd() {
- HttpMultiMap mm = HttpMultiMap.newCaseInsensitiveMap();
-
- mm.add("One", "one");
- mm.add("Two", "two");
-
- assertEquals(2, mm.size());
- assertEquals("one", mm.get("one"));
- assertEquals("two", mm.get("two"));
- }
-
- @Test
- void testAddSameName() {
- HttpMultiMap mm = HttpMultiMap.newCaseInsensitiveMap();
-
- mm.add("One", "one");
- mm.add("one", "two");
-
- assertEquals(1, mm.size());
- assertEquals("two", mm.get("one"));
-
- List all = mm.getAll("one");
- assertEquals(2, all.size());
- assertEquals("one", all.get(0));
- assertEquals("two", all.get(1));
-
- mm.add("one", "three");
- all = mm.getAll("one");
- assertEquals(3, all.size());
- assertEquals("one", all.get(0));
- assertEquals("two", all.get(1));
- assertEquals("three", all.get(2));
- }
-
- @Test
- void testMissing() {
- HttpMultiMap mm = HttpMultiMap.newCaseInsensitiveMap();
-
- assertNull(mm.get("xxx"));
- }
-
- @Test
- void testIterator() {
- HttpMultiMap mm = HttpMultiMap.newCaseInsensitiveMap();
-
- mm.add("One", "one");
- mm.add("one", "two");
- mm.add("two", "2.");
- mm.add("one", "three");
-
- assertEquals(2, mm.size());
-
- Iterator> i = mm.iterator();
-
- assertTrue(i.hasNext());
- assertEquals("one", i.next().getValue());
- assertEquals("two", i.next().getValue());
- assertEquals("2.", i.next().getValue());
- assertEquals("three", i.next().getValue());
- assertFalse(i.hasNext());
-
- try {
- i.next();
- fail("error");
- } catch (Exception ignore) {}
-
- mm.clear();
- i = mm.iterator();
- assertFalse(i.hasNext());
- }
-
-
- @Test
- void testNullValues() {
- HttpMultiMap hmm = HttpMultiMap.newCaseInsensitiveMap();
-
- assertFalse(hmm.contains("one"));
-
- hmm.add("one", null);
-
- assertNull(hmm.get("one"));
- assertTrue(hmm.contains("one"));
-
- hmm.add("one", null);
-
- assertNull(hmm.get("one"));
- assertTrue(hmm.contains("one"));
-
- hmm.set("one", "1");
-
- assertEquals("1", hmm.get("one"));
- assertTrue(hmm.contains("one"));
- }
-
- @Test
- void testParametersNumber() {
- HttpMultiMap hmm = HttpMultiMap.newCaseInsensitiveMap();
-
- for (int i = 0; i < 30; i++) {
- hmm.add(String.valueOf(i), "!" + i);
- }
-
- assertEquals(30, hmm.size());
- }
-
- @Test
- void testLetterCaseInsensitive() {
- HttpMultiMap mm = HttpMultiMap.newCaseInsensitiveMap();
-
- mm.add("one", "1.1");
- mm.add("one", "1.1.1");
- mm.add("One", "1.2");
-
- assertEquals(1, mm.size());
-
- assertEquals("1.2", mm.get("one"));
- assertEquals("1.2", mm.get("ONE"));
- assertEquals("1.2", mm.get("One"));
-
- List list = mm.getAll("ONE");
-
- assertEquals(3, list.size());
-
- assertEquals(1, mm.names().size());
- assertEquals(3, mm.entries().size());
- }
-
- @Test
- void testLetterCaseSensitive() {
- HttpMultiMap mm = HttpMultiMap.newCaseSensitiveMap();
-
- mm.add("one", "1.1");
- mm.add("one", "1.1.1");
- mm.add("One", "1.2");
-
- assertEquals(2, mm.size());
-
- assertEquals("1.1.1", mm.get("one"));
- assertNull(mm.get("ONE"));
- assertEquals("1.2", mm.get("One"));
-
- List list = mm.getAll("ONE");
- assertEquals(0, list.size());
-
- list = mm.getAll("one");
- assertEquals(2, list.size());
-
- assertEquals(2, mm.names().size());
- assertEquals(3, mm.entries().size());
- }
-
-}
diff --git a/jodd-http/src/test/java/jodd/http/HttpProgressListenerTest.java b/jodd-http/src/test/java/jodd/http/HttpProgressListenerTest.java
deleted file mode 100644
index f0359dda2..000000000
--- a/jodd-http/src/test/java/jodd/http/HttpProgressListenerTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-class HttpProgressListenerTest {
-
- @Test
- void testHttpProgressListener() {
- HttpProgressListener hpl = new HttpProgressListener() {
- @Override
- public void transferred(int len) {
-
- }
- };
-
- assertEquals(512, hpl.callbackSize(0));
- assertEquals(512, hpl.callbackSize(1000));
- assertEquals(512, hpl.callbackSize(51200));
- assertEquals(512, hpl.callbackSize(51201));
-
- assertEquals(1024, hpl.callbackSize(102400));
- assertEquals(1024, hpl.callbackSize(102401));
- assertEquals(1024, hpl.callbackSize(102449));
-
- assertEquals(1025, hpl.callbackSize(102450));
- assertEquals(1025, hpl.callbackSize(102499));
- assertEquals(1025, hpl.callbackSize(102500));
- }
-}
diff --git a/jodd-http/src/test/java/jodd/http/HttpRedirectTest.java b/jodd-http/src/test/java/jodd/http/HttpRedirectTest.java
deleted file mode 100644
index 0a2f66286..000000000
--- a/jodd-http/src/test/java/jodd/http/HttpRedirectTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-class HttpRedirectTest {
-
- static TestServer testServer;
-
- @BeforeAll
- static void startServer() throws Exception {
- testServer = new TomcatServer();
- testServer.start();
- }
-
- @AfterAll
- static void stopServer() throws Exception {
- testServer.stop();
- }
-
- @Test
- void testRedirect() {
- HttpRequest httpRequest = HttpRequest.get("localhost:8173/redirect");
-
- HttpResponse httpResponse = httpRequest.send();
-
- assertEquals(302, httpResponse.statusCode);
-
- HttpBrowser httpBrowser = new HttpBrowser();
-
- httpBrowser.sendRequest(
- HttpRequest.get("localhost:8173/redirect"));
-
- httpResponse = httpBrowser.getHttpResponse();
-
- assertNotNull(httpResponse);
- assertEquals("target!", httpResponse.body());
- }
-
-}
diff --git a/jodd-http/src/test/java/jodd/http/HttpRequestTest.java b/jodd-http/src/test/java/jodd/http/HttpRequestTest.java
deleted file mode 100644
index bff548683..000000000
--- a/jodd-http/src/test/java/jodd/http/HttpRequestTest.java
+++ /dev/null
@@ -1,431 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.io.FileUtil;
-import jodd.io.IOUtil;
-import jodd.io.upload.FileUpload;
-import jodd.net.MimeTypes;
-import jodd.util.ResourcesUtil;
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.CharArrayWriter;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
-class HttpRequestTest {
-
- @Test
- void testQueryParameters() {
- final HttpRequest httpRequest = new HttpRequest();
-
- httpRequest.path("");
- assertEquals("/", httpRequest.path());
-
- httpRequest.path("jodd");
- assertEquals("/jodd", httpRequest.path());
- assertNotNull(httpRequest.query());
- assertEquals(0, httpRequest.query().size());
-
- httpRequest.queryString("one=two");
- assertEquals("/jodd", httpRequest.path());
-
- HttpMultiMap params = httpRequest.query();
- assertEquals(1, params.size());
- assertEquals("two", params.get("one"));
-
- httpRequest.queryString("one");
- assertEquals("one", httpRequest.queryString());
- params = httpRequest.query();
- assertEquals(1, params.size());
- assertNull(params.get("one"));
-
- httpRequest.queryString("one=");
- assertEquals("one=", httpRequest.queryString());
- params = httpRequest.query();
- assertEquals(1, params.size());
- assertEquals("", params.get("one"));
-
- httpRequest.queryString("one=aaa&two=bbb");
- assertEquals("one=aaa&two=bbb", httpRequest.queryString());
- params = httpRequest.query();
- assertEquals(2, params.size());
- assertEquals("aaa", params.get("one"));
- assertEquals("bbb", params.get("two"));
-
- httpRequest.queryString("one=&two=aaa");
- assertEquals("one=&two=aaa", httpRequest.queryString());
- params = httpRequest.query();
- assertEquals(2, params.size());
- assertEquals("", params.get("one"));
- assertEquals("aaa", params.get("two"));
-
- httpRequest.clearQueries();
- httpRequest.queryString("one=Супер");
- assertEquals("one=%D0%A1%D1%83%D0%BF%D0%B5%D1%80", httpRequest.queryString());
- params = httpRequest.query();
- assertEquals(1, params.size());
- assertEquals("Супер", params.get("one"));
-
- httpRequest.queryString("one=Sуp");
- assertEquals("one=S%D1%83p", httpRequest.queryString());
-
- httpRequest.queryString("one=1&one=2");
- assertEquals("one=1&one=2", httpRequest.queryString());
- params = httpRequest.query();
- assertEquals(1, params.size());
- assertEquals("1", params.getAll("one").get(0));
- assertEquals("2", params.getAll("one").get(1));
-
- httpRequest.query("one", Integer.valueOf(3));
- assertEquals("one=1&one=2&one=3", httpRequest.queryString());
- }
-
- @Test
- void testFormParamsObjects() {
- final Map params = new HashMap<>();
- params.put("state", 1);
-
- final HttpRequest httpRequest = new HttpRequest();
- httpRequest.form(params);
-
- assertEquals(1, httpRequest.form().size());
- }
-
- @Test
- void testSet() {
- HttpRequest httpRequest = new HttpRequest();
- httpRequest.set("GET http://jodd.org:173/index.html?light=true");
-
- assertEquals("GET", httpRequest.method());
- assertEquals("http", httpRequest.protocol());
- assertEquals("jodd.org", httpRequest.host());
- assertEquals(173, httpRequest.port());
- assertEquals("/index.html", httpRequest.path());
- assertEquals("true", httpRequest.query().get("light"));
-
-
- httpRequest = new HttpRequest();
- httpRequest.set("http://jodd.org:173/index.html?light=true");
-
- assertEquals("GET", httpRequest.method());
- assertEquals("http", httpRequest.protocol());
- assertEquals("jodd.org", httpRequest.host());
- assertEquals(173, httpRequest.port());
- assertEquals("/index.html", httpRequest.path());
- assertEquals("true", httpRequest.query().get("light"));
-
-
- httpRequest = new HttpRequest();
- httpRequest.set("jodd.org:173/index.html?light=true");
-
- assertEquals("GET", httpRequest.method());
- assertEquals("http", httpRequest.protocol());
- assertEquals("jodd.org", httpRequest.host());
- assertEquals(173, httpRequest.port());
- assertEquals("/index.html", httpRequest.path());
- assertEquals("true", httpRequest.query().get("light"));
-
-
- httpRequest = new HttpRequest();
- httpRequest.set("jodd.org/index.html?light=true");
-
- assertEquals("GET", httpRequest.method());
- assertEquals("http", httpRequest.protocol());
- assertEquals("jodd.org", httpRequest.host());
- assertEquals(80, httpRequest.port());
- assertEquals("/index.html", httpRequest.path());
- assertEquals("true", httpRequest.query().get("light"));
-
-
- httpRequest = new HttpRequest();
- httpRequest.set("/index.html?light=true");
-
- assertEquals("GET", httpRequest.method());
- assertEquals("http", httpRequest.protocol());
- assertEquals("localhost", httpRequest.host());
- assertEquals(80, httpRequest.port());
- assertEquals("/index.html", httpRequest.path());
- assertEquals("true", httpRequest.query().get("light"));
-
-
- httpRequest = new HttpRequest();
- httpRequest.set("http://jodd.org");
-
- assertEquals("GET", httpRequest.method());
- assertEquals("http", httpRequest.protocol());
- assertEquals("jodd.org", httpRequest.host());
- assertEquals(80, httpRequest.port());
- assertEquals("/", httpRequest.path());
- }
-
-
- @Test
- void testInOutForm() {
- final HttpRequest request = HttpRequest.get("http://jodd.org/?id=173");
- request.header("User-Agent", "Scaly");
- request.form("one", "funny");
-
- final byte[] bytes = request.toByteArray();
-
- // read
- final HttpRequest request2 = HttpRequest.readFrom(new ByteArrayInputStream(bytes));
-
- assertEquals(request.method(), request2.method());
- assertEquals(request.path(), request2.path());
- assertEquals(request.queryString(), request2.queryString());
-
- assertEquals(request.header("User-Agent"), request2.header("User-Agent"));
- assertEquals(request.header("Content-Type"), request2.header("content-type"));
- assertEquals(request.header("Content-Length"), request2.header("content-length"));
-
- final HttpMultiMap> params1 = request.form();
- final HttpMultiMap> params2 = request2.form();
- assertEquals(params1.size(), params2.size());
- assertEquals(params2.get("one"), params2.get("one"));
- }
-
- @Test
- void testNegativeContentLength() {
- HttpRequest request = HttpRequest.get("http://jodd.org/?id=173");
- request.contentLength(-123);
-
- byte[] bytes = request.toByteArray();
- try {
- final HttpRequest request2 = HttpRequest.readFrom(new ByteArrayInputStream(bytes));
- assertEquals("", request2.body());
- } catch (final Exception ex) {
- fail(ex.toString());
- }
-
- // the same test but with missing content length
-
- request = HttpRequest.get("http://jodd.org/?id=173");
-
- bytes = request.toByteArray();
- try {
- final HttpRequest request2 = HttpRequest.readFrom(new ByteArrayInputStream(bytes));
- assertEquals("", request2.body());
- } catch (final Exception ex) {
- fail(ex.toString());
- }
- }
-
- @Test
- void testFileUpload() throws IOException {
- final HttpRequest request = HttpRequest.get("http://jodd.org/?id=173");
-
- request.header("User-Agent", "Scaly").form("one", "funny");
-
- final File tempFile = FileUtil.createTempFile();
- tempFile.deleteOnExit();
- FileUtil.writeString(tempFile, "qwerty");
- request.form("two", tempFile);
-
- final byte[] bytes = request.toByteArray();
-
-
- // read
- final HttpRequest request2 = HttpRequest.readFrom(new ByteArrayInputStream(bytes));
- final HttpMultiMap> httpParams2 = request2.form();
-
- assertEquals(request.method(), request2.method());
- assertEquals(request.path(), request2.path());
- assertEquals(request.queryString(), request2.queryString());
-
- assertEquals(request.header("User-Agent"), request2.header("User-Agent"));
- assertEquals(request.header("Content-Type"), request2.header("content-type"));
- assertEquals(request.header("Content-Length"), request2.header("content-length"));
-
- final HttpMultiMap> params1 = request.form();
- final HttpMultiMap> params2 = request2.form();
- assertEquals(params1.size(), params2.size());
- assertEquals(params2.get("one"), params2.get("one"));
-
- final FileUpload fu = (FileUpload) httpParams2.get("two");
- assertEquals(6, fu.getSize());
-
- final String str = new String(fu.getFileContent());
- assertEquals("qwerty", str);
-
- tempFile.delete();
- }
-
- @Test
- void testUrl() {
- HttpRequest httpRequest = new HttpRequest();
- httpRequest.set("GET http://jodd.org:173/index.html?light=true");
-
- assertEquals("http://jodd.org:173/index.html?light=true", httpRequest.url());
- assertEquals("http://jodd.org:173", httpRequest.hostUrl());
-
- httpRequest = HttpRequest.get("foo.com/");
-
- assertEquals("http://foo.com", httpRequest.hostUrl());
- }
-
- @Test
- void testBasicAuthorizationCanBeSetToNullAndIsIgnoredSilently() {
- final HttpRequest httpRequest = new HttpRequest();
- final String[][] input = new String[][]{
- {"non-null", null},
- {null, "non-null"},
- {null, null},
- };
-
- try {
-
- for(final String[] pair :input) {
- httpRequest.basicAuthentication(pair[0], pair[1]);
- assertNull(httpRequest.headers.get("Authorization"));
- }
-
- } catch (final RuntimeException e) {
- fail("No exception should be thrown for null authorization basic header args!");
- }
- }
-
- @Test
- void test394() {
- HttpRequest request = HttpRequest.get("https://jodd.org/random link");
- assertEquals("GET", request.method());
- assertEquals("https://jodd.org/random link", request.url());
-
- request = HttpRequest.get("https://jodd.org/random link?q=1");
- assertEquals("1", request.query().get("q"));
-
- final String badUrl = "httpsjodd.org/random link?q=1:// GET";
- try {
- HttpRequest.get(badUrl).send();
- fail("error");
- }
- catch (final HttpException he) {
- assertTrue(he.getMessage().contains(badUrl));
- }
-
- }
-
- @Test
- void testCapitalizeHeaders() {
-
- // true
-
- HttpRequest request = HttpRequest.get("")
- .capitalizeHeaderKeys(true)
- .header("key-tEST2", "value2");
- assertTrue(request.toString(false).contains("Key-Test2: value2"), "Header key should have been modified");
- assertEquals("value2", request.headers("KEY-TEST2").get(0));
- assertEquals("value2", request.headers("key-test2").get(0));
-
- request.header("key-test2", "value3");
- assertTrue(request.toString(false).contains("Key-Test2: value2, value3"), "Header key should have been modified");
- assertEquals(2, request.headers("KEY-TEST2").size());
- assertEquals(2 + 2, request.headerNames().size()); // 2 default and 2 added
-
- request.headerRemove("key-test2");
- assertFalse(request.headers.contains("key-test2"));
- assertFalse(request.headers.contains("key-tEST2"));
-
-
- // false
-
- request = HttpRequest.get("")
- .capitalizeHeaderKeys(false)
- .header("KEY-TEST1", "VALUE1");
-
- assertTrue(request.toString(false).contains("KEY-TEST1: VALUE1"), "Header key should not have been modified");
- assertEquals("VALUE1", request.headers("KEY-TEST1").get(0));
- assertEquals("VALUE1", request.headers("key-test1").get(0));
-
- request.header("key-test1", "value4");
- assertTrue(request.toString(false).contains("key-test1: VALUE1, value4"), "Header key should not have been modified");
- assertEquals(2, request.headers("KEY-TEST1").size());
- assertEquals(2 + 2, request.headerNames().size()); // 2 default and 2 added
-
- request.headerRemove("key-test1");
- assertFalse(request.headers.contains("key-test1"));
- assertFalse(request.headers.contains("KEY-TEST1"));
- }
-
- @Test
- void testBigRequest() throws IOException {
- final InputStream inputStream = ResourcesUtil.getResourceAsStream("/jodd/http/answer.json");
-
- final CharArrayWriter writter = IOUtil.copy(inputStream);
- final String body = writter.toString();
-
- final HttpRequest httpRequest = HttpRequest.get("").body(body);
-
- final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- httpRequest.sendTo(outputStream);
-
- String receivedBody = outputStream.toString();
-
- final int ndx = receivedBody.indexOf("{");
- receivedBody = receivedBody.substring(ndx);
-
- assertEquals(body, receivedBody);
- }
-
- @Test
- void testHttpRequestSlash() {
- final HttpRequest request = HttpRequest.post("/");
- request.contentType("application/x-www-form-urlencoded");
- final HttpRequest request1 = HttpRequest.readFrom(new ByteArrayInputStream(request.toByteArray()));
-
- assertEquals(request.toString(), request1.toString());
- }
-
- @Test
- void testHttpRequestReRead() {
- final HttpRequest request = HttpRequest.post("http://127.0.0.1:8086/test");
- request.form("a", null);
- request.form("b", "aaa");
- final HttpRequest request1 = HttpRequest.readFrom(new ByteArrayInputStream(request.toByteArray()));
- assertEquals(request.toString(), request1.toString());
- }
-
- @Test
- void testHttpRequestContentSetOrder() {
- final HttpRequest request = HttpRequest.get("http://127.0.0.1:8086/test");
-
- request.contentTypeJson().bodyText("{}");
-
- assertEquals(MimeTypes.MIME_APPLICATION_JSON, request.mediaType());
- }
-}
diff --git a/jodd-http/src/test/java/jodd/http/HttpUploadTest.java b/jodd-http/src/test/java/jodd/http/HttpUploadTest.java
deleted file mode 100644
index 81ac99e9e..000000000
--- a/jodd-http/src/test/java/jodd/http/HttpUploadTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.io.FileUtil;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-
-import java.io.File;
-import java.io.IOException;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class HttpUploadTest {
-
- static TestServer testServer;
-
- @BeforeAll
- static void startServer() throws Exception {
- testServer = new TomcatServer();
- testServer.start();
- }
-
- @AfterAll
- static void stopServer() throws Exception {
- testServer.stop();
- }
-
-
- @Test
- void uploadTest() throws IOException {
- File temp1 = FileUtil.createTempFile();
- FileUtil.writeString(temp1, "Temp1 content");
- File temp2 = FileUtil.createTempFile();
- FileUtil.writeString(temp2, "Temp2 content");
-
- temp1.deleteOnExit();
- temp2.deleteOnExit();
-
- HttpRequest httpRequest = HttpRequest.post("localhost:8173/echo")
- .form(
- "title", "test",
- "description", "Upload test",
- "file1", temp1,
- "file2", temp2
- );
-
-
- HttpResponse httpResponse = httpRequest.send();
- String body = httpResponse.bodyText();
-
- assertTrue(body.contains("Temp1 content"));
- assertTrue(body.contains("Temp2 content"));
- }
-}
diff --git a/jodd-http/src/test/java/jodd/http/HttpUtilTest.java b/jodd-http/src/test/java/jodd/http/HttpUtilTest.java
deleted file mode 100644
index 878621fb5..000000000
--- a/jodd-http/src/test/java/jodd/http/HttpUtilTest.java
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import org.junit.jupiter.api.Test;
-
-import java.nio.charset.StandardCharsets;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
-
-class HttpUtilTest {
-
- @Test
- void testNiceHeaderNames() {
- assertEquals("Content-Type", HttpUtil.prepareHeaderParameterName("conTent-tyPe"));
- assertEquals("ETag", HttpUtil.prepareHeaderParameterName("etag"));
- }
-
- @Test
- void testMediaTypeAndParameters() {
- String contentType = "text/html";
-
- assertEquals("text/html", HttpUtil.extractMediaType(contentType));
- assertEquals(null, HttpUtil.extractHeaderParameter(contentType, "charset", ';'));
-
- contentType = "text/html;"; // special case, see #588
-
- assertEquals("text/html", HttpUtil.extractMediaType(contentType));
- assertEquals(null, HttpUtil.extractHeaderParameter(contentType, "charset", ';'));
-
- contentType = "text/html; charset=ISO-8859-4";
-
- assertEquals("text/html", HttpUtil.extractMediaType(contentType));
- assertEquals("ISO-8859-4", HttpUtil.extractHeaderParameter(contentType, "charset", ';'));
-
-
- contentType = "text/html;charset=ISO-8859-4";
-
- assertEquals("text/html", HttpUtil.extractMediaType(contentType));
- assertEquals("ISO-8859-4", HttpUtil.extractHeaderParameter(contentType, "charset", ';'));
-
-
- contentType = "text/html; pre=foo; charset=ISO-8859-4";
-
- assertEquals("text/html", HttpUtil.extractMediaType(contentType));
- assertEquals("ISO-8859-4", HttpUtil.extractHeaderParameter(contentType, "charset", ';'));
-
-
- contentType = "text/html; pre=foo; charset=ISO-8859-4; post=bar";
-
- assertEquals("text/html", HttpUtil.extractMediaType(contentType));
- assertEquals("ISO-8859-4", HttpUtil.extractHeaderParameter(contentType, "charset", ';'));
- assertEquals("foo", HttpUtil.extractHeaderParameter(contentType, "pre", ';'));
- assertEquals(null, HttpUtil.extractHeaderParameter(contentType, "na", ';'));
- }
-
- @Test
- void testDefaultPort() {
- HttpRequest request;
-
- request = HttpRequest.get("jodd.org");
- assertEquals("http", request.protocol());
- assertEquals(80, request.port());
-
- request = HttpRequest.get("jodd.org:80");
- assertEquals("http", request.protocol());
- assertEquals(80, request.port());
-
- request = HttpRequest.get("jodd.org:801");
- assertEquals("http", request.protocol());
- assertEquals(801, request.port());
-
- request = HttpRequest.get("http://jodd.org");
- assertEquals("http", request.protocol());
- assertEquals(80, request.port());
-
- request = HttpRequest.get("https://jodd.org");
- assertEquals("https", request.protocol());
- assertEquals(443, request.port());
-
- request = HttpRequest.get("https://jodd.org:8443");
- assertEquals("https", request.protocol());
- assertEquals(8443, request.port());
- }
-
- @Test
- void testBuildQuery() {
- final HttpMultiMap map = HttpMultiMap.newCaseInsensitiveMap();
-
- assertEquals("", HttpUtil.buildQuery(map, StandardCharsets.UTF_8.name()));
-
- map.add("aaa", "one");
- assertEquals("aaa=one", HttpUtil.buildQuery(map, StandardCharsets.UTF_8.name()));
-
- map.add("bbb", "two");
- assertEquals("aaa=one&bbb=two", HttpUtil.buildQuery(map, StandardCharsets.UTF_8.name()));
-
- map.clear().add("ccc", null);
- assertEquals("ccc", HttpUtil.buildQuery(map, StandardCharsets.UTF_8.name()));
-
- map.add("ddd", "four");
- assertEquals("ccc&ddd=four", HttpUtil.buildQuery(map, StandardCharsets.UTF_8.name()));
- }
-
- @Test
- void testParseQuery() {
- HttpMultiMap map = HttpUtil.parseQuery("a=b", false);
-
- assertEquals(1, map.size());
- assertEquals("b", map.get("a"));
-
-
- map = HttpUtil.parseQuery("a=b&c=d", false);
-
- assertEquals(2, map.size());
- assertEquals("b", map.get("a"));
- assertEquals("d", map.get("c"));
- }
-
- @Test
- void testParseQuery_specialCase() {
- HttpMultiMap map = HttpUtil.parseQuery("a&b", false);
-
- assertEquals(2, map.size());
- assertNull(map.get("a"));
- assertNull(map.get("b"));
-
-
- map = HttpUtil.parseQuery("a&c=d", false);
-
- assertEquals(2, map.size());
- assertNull(map.get("a"));
- assertEquals("d", map.get("c"));
- }
-
-}
diff --git a/jodd-http/src/test/java/jodd/http/HttpsFactoryTest.java b/jodd-http/src/test/java/jodd/http/HttpsFactoryTest.java
deleted file mode 100644
index e3c84fadc..000000000
--- a/jodd-http/src/test/java/jodd/http/HttpsFactoryTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.http.net.SSLSocketHttpConnectionProvider;
-import org.junit.jupiter.api.Test;
-
-import javax.net.ssl.SSLSocketFactory;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.UnknownHostException;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class HttpsFactoryTest {
-
- @Test
- void testCustomSSLSocketHttpConnectionProvider() {
- final AtomicBoolean atomicBoolean = new AtomicBoolean();
-
- final SSLSocketFactory sslSocketFactory = new SSLSocketFactory() {
- @Override
- public String[] getDefaultCipherSuites() {
- return new String[0];
- }
-
- @Override
- public String[] getSupportedCipherSuites() {
- return new String[0];
- }
-
- @Override
- public Socket createSocket(Socket socket, String s, int i, boolean b) throws IOException {
- atomicBoolean.set(true);
- return null;
- }
-
- @Override
- public Socket createSocket(String s, int i) throws IOException, UnknownHostException {
- atomicBoolean.set(true);
- return null;
- }
-
- @Override
- public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) throws IOException, UnknownHostException {
- atomicBoolean.set(true);
- return null;
- }
-
- @Override
- public Socket createSocket(InetAddress inetAddress, int i) throws IOException {
- atomicBoolean.set(true);
- return null;
- }
-
- @Override
- public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) throws IOException {
- atomicBoolean.set(true);
- return null;
- }
- };
-
- try {
- HttpRequest.get("https://google.com")
- .withConnectionProvider(new SSLSocketHttpConnectionProvider(sslSocketFactory))
- .open();
- }
- catch (NullPointerException npe) {
- }
-
- assertTrue(atomicBoolean.get());
- }
-}
diff --git a/jodd-http/src/test/java/jodd/http/KeepAliveTest.java b/jodd-http/src/test/java/jodd/http/KeepAliveTest.java
deleted file mode 100644
index cc330e106..000000000
--- a/jodd-http/src/test/java/jodd/http/KeepAliveTest.java
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertSame;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class KeepAliveTest {
-
- private static final String[] RESPONSES = new String[] {
- "HTTP/1.1 200 OK\r\n" +
- "Content-Type: text/html; charset=utf-8\r\n" +
- "Content-Length: 13\r\n" +
- "Connection: Keep-Alive\r\n" +
- "Keep-Alive: timeout=100, max=2\r\n" +
- "\r\n" +
- "",
-
- "HTTP/1.1 200 OK\r\n" +
- "Content-Type: text/html; charset=utf-8\r\n" +
- "Content-Length: 13\r\n" +
- "Connection: Keep-Alive\r\n" +
- "Keep-Alive: timeout=100, max=1\r\n" +
- "\r\n" +
- "",
-
- "HTTP/1.1 200 OK\r\n" +
- "Content-Type: text/html; charset=utf-8\r\n" +
- "Content-Length: 13\r\n" +
- "Connection: Close\r\n" +
- "\r\n" +
- ""
- };
-
- private static int currentResponse;
-
- HttpConnectionProvider httpConnectionProvider = new HttpConnectionProvider() {
- public void useProxy(ProxyInfo proxyInfo) {
- }
-
- public HttpConnection createHttpConnection(HttpRequest httpRequest) throws IOException {
- return new HttpConnection() {
- @Override
- public void init() throws IOException {
- // ignore
- }
-
- public OutputStream getOutputStream() throws IOException {
- return new ByteArrayOutputStream();
- }
-
- public InputStream getInputStream() throws IOException {
- return new ByteArrayInputStream(RESPONSES[currentResponse].getBytes());
- }
-
- public void setTimeout(int milliseconds) {
- // ignore
- }
-
- public void close() {
- }
- };
- }
- };
-
- @Test
- void testKeepAlive() {
- currentResponse = 0;
-
- // ->
- HttpRequest request = HttpRequest.get("http://jodd.org");
- assertEquals("Close", request.header("Connection"));
- request.connectionKeepAlive(true);
- assertTrue(request.isConnectionPersistent());
-
- // <-
- HttpResponse response = request.open(httpConnectionProvider).send();
- HttpConnection connection = request.connection();
-
- assertTrue(request.isConnectionPersistent());
- assertTrue(response.isConnectionPersistent());
- assertNotNull(request.connection());
-
- currentResponse = 1;
-
- // ->
- request = HttpRequest.get("http://jodd.org");
- response = request.keepAlive(response, true).send();
-
- // <-
- assertSame(connection, request.connection());
- assertTrue(request.isConnectionPersistent());
- assertTrue(response.isConnectionPersistent());
- assertNotNull(request.connection());
-
- currentResponse = 2;
-
- // -> LAST request
- request = HttpRequest.get("http://jodd.org");
- response = request.keepAlive(response, true).send();
-
- // <-
- assertNull(request.connection()); // connection is closed
- assertTrue(request.isConnectionPersistent());
- assertFalse(response.isConnectionPersistent());
-
- currentResponse = 0;
-
- // -> AFTER THE LAST, STARTS EVERYTHING AGAIN
-
- request = HttpRequest.get("http://jodd.org");
- response = request.keepAlive(response, true).send(); // should be false for the last connection, but ok.
-
- // <-
- assertTrue(request.isConnectionPersistent());
- assertTrue(response.isConnectionPersistent());
- assertNotNull(request.connection());
-
- // CLOSE
-
- response.close();
- assertNull(request.connection()); // connection closed
- }
-
- @Test
- void testKeepAliveBrowser() {
- HttpBrowser browser = new HttpBrowser();
- browser.setKeepAlive(true);
- browser.setHttpConnectionProvider(httpConnectionProvider);
-
- currentResponse = 0;
-
- // ->
- HttpRequest request = HttpRequest.get("http://jodd.org");
- browser.sendRequest(request);
-
- // <-
- HttpResponse response = browser.getHttpResponse();
- HttpConnection connection = request.connection();
-
- assertTrue(request.isConnectionPersistent());
- assertTrue(response.isConnectionPersistent());
- assertNotNull(request.connection());
-
- currentResponse = 1;
-
- // ->
- request = HttpRequest.get("http://jodd.org");
- response = browser.sendRequest(request);
-
- // <-
- assertSame(connection, request.connection());
- assertTrue(request.isConnectionPersistent());
- assertTrue(response.isConnectionPersistent());
- assertNotNull(request.connection());
-
- currentResponse = 2;
-
- // -> LAST request
- request = HttpRequest.get("http://jodd.org");
- response = browser.sendRequest(request);
-
- // <-
- assertNull(request.connection()); // connection is closed
- assertTrue(request.isConnectionPersistent());
- assertFalse(response.isConnectionPersistent());
-
- currentResponse = 0;
-
- // -> AFTER THE LAST, STARTS EVERYTHING AGAIN
-
- request = HttpRequest.get("http://jodd.org");
- response = browser.sendRequest(request);
-
- // <-
- assertTrue(request.isConnectionPersistent());
- assertTrue(response.isConnectionPersistent());
- assertNotNull(request.connection());
-
- // CLOSE
-
- browser.close();
- assertNull(request.connection()); // connection closed
- }
-}
diff --git a/jodd-http/src/test/java/jodd/http/NanoHTTPD.java b/jodd-http/src/test/java/jodd/http/NanoHTTPD.java
deleted file mode 100644
index baca8ccf1..000000000
--- a/jodd-http/src/test/java/jodd/http/NanoHTTPD.java
+++ /dev/null
@@ -1,1178 +0,0 @@
-/*
- * #%L
- * NanoHttpd-Core
- * %%
- * Copyright (C) 2012 - 2016 nanohttpd
- * %%
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the nanohttpd nor the names of its contributors
- * may be used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- * #L%
- */
-
-package jodd.http;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.io.PrintWriter;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.URLEncoder;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Locale;
-import java.util.Properties;
-import java.util.StringTokenizer;
-import java.util.TimeZone;
-import java.util.Vector;
-
-/**
- * A simple, tiny, nicely embeddable HTTP server in Java
- *
- *
- * NanoHTTPD
- *
- * Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen,
- * 2010 by Konstantinos Togias
- *
- *
- *
- * Features + limitations:
- *
- *
- * Only one Java file
- * Java 5 compatible
- * Released as open source, Modified BSD licence
- * No fixed config files, logging, authorization etc. (Implement yourself if
- * you need them.)
- * Supports parameter parsing of GET and POST methods (+ rudimentary PUT
- * support in 1.25)
- * Supports both dynamic content and file serving
- * Supports file upload (since version 1.2, 2010)
- * Supports partial content (streaming)
- * Supports ETags
- * Never caches anything
- * Doesn't limit bandwidth, request time or simultaneous connections
- * Default code serves files and shows all HTTP parameters and headers
- * File server supports directory listing, index.html and index.htm
- * File server supports partial content (streaming)
- * File server supports ETags
- * File server does the 301 redirection trick for directories without '/'
- * File server supports simple skipping for files (continue download)
- * File server serves also very long files without memory overhead
- * Contains a built-in list of most common MIME types
- * All header names are converted to lower case so they don't vary between
- * browsers/clients
- *
- *
- *
- *
- * How to use:
- *
- *
- * Subclass and implement serve() and embed to your own program
- *
- *
- *
- * See the separate "LICENSE.md" file for the distribution license (Modified BSD
- * licence)
- */
-public class NanoHTTPD
-{
- // ==================================================
- // API parts
- // ==================================================
-
- /**
- * Override this to customize the server.
- *
- * (By default, this delegates to serveFile() and allows directory listing.)
- *
- * @param uri Percent-decoded URI without parameters, for example "/index.cgi"
- * @param method "GET", "POST" etc.
- * @param parms Parsed, percent decoded parameters from URI and, in case of POST, data.
- * @param header Header entries, percent decoded
- * @return HTTP response, see class Response for details
- */
- public Response serve( String uri, String method, Properties header, Properties parms, Properties files )
- {
- myOut.println( method + " '" + uri + "' " );
-
- Enumeration e = header.propertyNames();
- while ( e.hasMoreElements())
- {
- String value = (String)e.nextElement();
- myOut.println( " HDR: '" + value + "' = '" +
- header.getProperty( value ) + "'" );
- }
- e = parms.propertyNames();
- while ( e.hasMoreElements())
- {
- String value = (String)e.nextElement();
- myOut.println( " PRM: '" + value + "' = '" +
- parms.getProperty( value ) + "'" );
- }
- e = files.propertyNames();
- while ( e.hasMoreElements())
- {
- String value = (String)e.nextElement();
- myOut.println( " UPLOADED: '" + value + "' = '" +
- files.getProperty( value ) + "'" );
- }
-
- return serveFile( uri, header, myRootDir, true );
- }
-
- /**
- * HTTP response.
- * Return one of these from serve().
- */
- public class Response
- {
- /**
- * Default constructor: response = HTTP_OK, data = mime = 'null'
- */
- public Response()
- {
- this.status = HTTP_OK;
- }
-
- /**
- * Basic constructor.
- */
- public Response( String status, String mimeType, InputStream data )
- {
- this.status = status;
- this.mimeType = mimeType;
- this.data = data;
- }
-
- /**
- * Convenience method that makes an InputStream out of
- * given text.
- */
- public Response( String status, String mimeType, String txt )
- {
- this.status = status;
- this.mimeType = mimeType;
- try
- {
- this.data = new ByteArrayInputStream( txt.getBytes("UTF-8"));
- }
- catch ( java.io.UnsupportedEncodingException uee )
- {
- uee.printStackTrace();
- }
- }
-
- /**
- * Adds given line to the header.
- */
- public void addHeader( String name, String value )
- {
- header.put( name, value );
- }
-
- /**
- * HTTP status code after processing, e.g. "200 OK", HTTP_OK
- */
- public String status;
-
- /**
- * MIME type of content, e.g. "text/html"
- */
- public String mimeType;
-
- /**
- * Data of the response, may be null.
- */
- public InputStream data;
-
- /**
- * Headers for the HTTP response. Use addHeader()
- * to add lines.
- */
- public Properties header = new Properties();
- }
-
- /**
- * Some HTTP response status codes
- */
- public static final String
- HTTP_OK = "200 OK",
- HTTP_PARTIALCONTENT = "206 Partial Content",
- HTTP_RANGE_NOT_SATISFIABLE = "416 Requested Range Not Satisfiable",
- HTTP_REDIRECT = "301 Moved Permanently",
- HTTP_NOTMODIFIED = "304 Not Modified",
- HTTP_FORBIDDEN = "403 Forbidden",
- HTTP_NOTFOUND = "404 Not Found",
- HTTP_BADREQUEST = "400 Bad Request",
- HTTP_INTERNALERROR = "500 Internal Server Error",
- HTTP_NOTIMPLEMENTED = "501 Not Implemented";
-
- /**
- * Common mime types for dynamic content
- */
- public static final String
- MIME_PLAINTEXT = "text/plain",
- MIME_HTML = "text/html",
- MIME_DEFAULT_BINARY = "application/octet-stream",
- MIME_XML = "text/xml";
-
- // ==================================================
- // Socket & server code
- // ==================================================
-
- /**
- * Starts a HTTP server to given port.
- * Throws an IOException if the socket is already in use
- */
- public NanoHTTPD( int port, File wwwroot ) throws IOException
- {
- myTcpPort = port;
- this.myRootDir = wwwroot;
- myServerSocket = new ServerSocket( myTcpPort );
- myThread = new Thread( new Runnable()
- {
- @Override
- public void run()
- {
- try
- {
- while( true )
- new HTTPSession( myServerSocket.accept());
- }
- catch ( IOException ioe )
- {}
- }
- });
- myThread.setDaemon( true );
- myThread.start();
- }
-
- /**
- * Stops the server.
- */
- public void stop()
- {
- try
- {
- myServerSocket.close();
- myThread.join();
- }
- catch ( IOException ioe ) {}
- catch ( InterruptedException e ) {}
- }
-
-
- /**
- * Starts as a standalone file server and waits for Enter.
- */
- public static void main( String[] args )
- {
- myOut.println( "NanoHTTPD 1.25 (C) 2001,2005-2011 Jarno Elonen and (C) 2010 Konstantinos Togias\n" +
- "(Command line options: [-p port] [-d root-dir] [--licence])\n" );
-
- // Defaults
- int port = 80;
- File wwwroot = new File(".").getAbsoluteFile();
-
- // Show licence if requested
- for ( int i=0; i 0)
- {
- rlen += read;
- splitbyte = findHeaderEnd(buf, rlen);
- if (splitbyte > 0)
- break;
- read = is.read(buf, rlen, bufsize - rlen);
- }
- }
-
- // Create a BufferedReader for parsing the header.
- ByteArrayInputStream hbis = new ByteArrayInputStream(buf, 0, rlen);
- BufferedReader hin = new BufferedReader( new InputStreamReader( hbis ));
- Properties pre = new Properties();
- Properties parms = new Properties();
- Properties header = new Properties();
- Properties files = new Properties();
-
- // Decode the header into parms and header java properties
- decodeHeader(hin, pre, parms, header);
- String method = pre.getProperty("method");
- String uri = pre.getProperty("uri");
-
- long size = 0x7FFFFFFFFFFFFFFFl;
- String contentLength = header.getProperty("content-length");
- if (contentLength != null)
- {
- try { size = Integer.parseInt(contentLength); }
- catch (NumberFormatException ex) {}
- }
-
- // Write the part of body already read to ByteArrayOutputStream f
- ByteArrayOutputStream f = new ByteArrayOutputStream();
- if (splitbyte < rlen)
- f.write(buf, splitbyte, rlen-splitbyte);
-
- // While Firefox sends on the first read all the data fitting
- // our buffer, Chrome and Opera send only the headers even if
- // there is data for the body. We do some magic here to find
- // out whether we have already consumed part of body, if we
- // have reached the end of the data to be sent or we should
- // expect the first byte of the body at the next read.
- if (splitbyte < rlen)
- size -= rlen-splitbyte+1;
- else if (splitbyte==0 || size == 0x7FFFFFFFFFFFFFFFl)
- size = 0;
-
- // Now read all the body and write it to f
- buf = new byte[512];
- while ( rlen >= 0 && size > 0 )
- {
- rlen = is.read(buf, 0, 512);
- size -= rlen;
- if (rlen > 0)
- f.write(buf, 0, rlen);
- }
-
- // Get the raw body as a byte []
- byte [] fbuf = f.toByteArray();
-
- // Create a BufferedReader for easily reading it as string.
- ByteArrayInputStream bin = new ByteArrayInputStream(fbuf);
- BufferedReader in = new BufferedReader( new InputStreamReader(bin));
-
- // If the method is POST, there may be parameters
- // in data section, too, read it:
- if ( method.equalsIgnoreCase( "POST" ))
- {
- String contentType = "";
- String contentTypeHeader = header.getProperty("content-type");
- StringTokenizer st = new StringTokenizer( contentTypeHeader , "; " );
- if ( st.hasMoreTokens()) {
- contentType = st.nextToken();
- }
-
- if (contentType.equalsIgnoreCase("multipart/form-data"))
- {
- // Handle multipart/form-data
- if ( !st.hasMoreTokens())
- sendError( HTTP_BADREQUEST, "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html" );
- String boundaryExp = st.nextToken();
- st = new StringTokenizer( boundaryExp , "=" );
- if (st.countTokens() != 2)
- sendError( HTTP_BADREQUEST, "BAD REQUEST: Content type is multipart/form-data but boundary syntax error. Usage: GET /example/file.html" );
- st.nextToken();
- String boundary = st.nextToken();
-
- decodeMultipartData(boundary, fbuf, in, parms, files);
- }
- else
- {
- // Handle application/x-www-form-urlencoded
- String postLine = "";
- char[] pbuf = new char[512];
- int read = in.read(pbuf);
- while ( read >= 0 && !postLine.endsWith("\r\n") )
- {
- postLine += String.valueOf(pbuf, 0, read);
- read = in.read(pbuf);
- }
- postLine = postLine.trim();
- decodeParms( postLine, parms );
- }
- }
-
- if ( method.equalsIgnoreCase( "PUT" ))
- files.put("content", saveTmpFile( fbuf, 0, f.size()));
-
- // Ok, now do the serve()
- Response r = serve( uri, method, header, parms, files );
- if ( r == null )
- sendError( HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: Serve() returned a null response." );
- else
- sendResponse( r.status, r.mimeType, r.header, r.data );
-
- in.close();
- is.close();
- }
- catch ( IOException ioe )
- {
- try
- {
- sendError( HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
- }
- catch ( Throwable t ) {}
- }
- catch ( InterruptedException ie )
- {
- // Thrown by sendError, ignore and exit the thread.
- }
- }
-
- /**
- * Decodes the sent headers and loads the data into
- * java Properties' key - value pairs
- **/
- private void decodeHeader(BufferedReader in, Properties pre, Properties parms, Properties header)
- throws InterruptedException
- {
- try {
- // Read the request line
- String inLine = in.readLine();
- if (inLine == null) return;
- StringTokenizer st = new StringTokenizer( inLine );
- if ( !st.hasMoreTokens())
- sendError( HTTP_BADREQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html" );
-
- String method = st.nextToken();
- pre.put("method", method);
-
- if ( !st.hasMoreTokens())
- sendError( HTTP_BADREQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html" );
-
- String uri = st.nextToken();
-
- // Decode parameters from the URI
- int qmi = uri.indexOf( '?' );
- if ( qmi >= 0 )
- {
- decodeParms( uri.substring( qmi+1 ), parms );
- uri = decodePercent( uri.substring( 0, qmi ));
- }
- else uri = decodePercent(uri);
-
- // If there's another token, it's protocol version,
- // followed by HTTP headers. Ignore version but parse headers.
- // NOTE: this now forces header names lowercase since they are
- // case insensitive and vary by client.
- if ( st.hasMoreTokens())
- {
- String line = in.readLine();
- while ( line != null && line.trim().length() > 0 )
- {
- int p = line.indexOf( ':' );
- if ( p >= 0 )
- header.put( line.substring(0,p).trim().toLowerCase(), line.substring(p+1).trim());
- line = in.readLine();
- }
- }
-
- pre.put("uri", uri);
- }
- catch ( IOException ioe )
- {
- sendError( HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
- }
- }
-
- /**
- * Decodes the Multipart Body data and put it
- * into java Properties' key - value pairs.
- **/
- private void decodeMultipartData(String boundary, byte[] fbuf, BufferedReader in, Properties parms, Properties files)
- throws InterruptedException
- {
- try
- {
- int[] bpositions = getBoundaryPositions(fbuf,boundary.getBytes());
- int boundarycount = 1;
- String mpline = in.readLine();
- while ( mpline != null )
- {
- if (mpline.indexOf(boundary) == -1)
- sendError( HTTP_BADREQUEST, "BAD REQUEST: Content type is multipart/form-data but next chunk does not start with boundary. Usage: GET /example/file.html" );
- boundarycount++;
- Properties item = new Properties();
- mpline = in.readLine();
- while (mpline != null && mpline.trim().length() > 0)
- {
- int p = mpline.indexOf( ':' );
- if (p != -1)
- item.put( mpline.substring(0,p).trim().toLowerCase(), mpline.substring(p+1).trim());
- mpline = in.readLine();
- }
- if (mpline != null)
- {
- String contentDisposition = item.getProperty("content-disposition");
- if (contentDisposition == null)
- {
- sendError( HTTP_BADREQUEST, "BAD REQUEST: Content type is multipart/form-data but no content-disposition info found. Usage: GET /example/file.html" );
- }
- StringTokenizer st = new StringTokenizer( contentDisposition , "; " );
- Properties disposition = new Properties();
- while ( st.hasMoreTokens())
- {
- String token = st.nextToken();
- int p = token.indexOf( '=' );
- if (p!=-1)
- disposition.put( token.substring(0,p).trim().toLowerCase(), token.substring(p+1).trim());
- }
- String pname = disposition.getProperty("name");
- pname = pname.substring(1,pname.length()-1);
-
- String value = "";
- if (item.getProperty("content-type") == null) {
- while (mpline != null && mpline.indexOf(boundary) == -1)
- {
- mpline = in.readLine();
- if ( mpline != null)
- {
- int d = mpline.indexOf(boundary);
- if (d == -1)
- value+=mpline;
- else
- value+=mpline.substring(0,d-2);
- }
- }
- }
- else
- {
- if (boundarycount> bpositions.length)
- sendError( HTTP_INTERNALERROR, "Error processing request" );
- int offset = stripMultipartHeaders(fbuf, bpositions[boundarycount-2]);
- String path = saveTmpFile(fbuf, offset, bpositions[boundarycount-1]-offset-4);
- files.put(pname, path);
- value = disposition.getProperty("filename");
- value = value.substring(1,value.length()-1);
- do {
- mpline = in.readLine();
- } while (mpline != null && mpline.indexOf(boundary) == -1);
- }
- parms.put(pname, value);
- }
- }
- }
- catch ( IOException ioe )
- {
- sendError( HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
- }
- }
-
- /**
- * Find byte index separating header from body.
- * It must be the last byte of the first two sequential new lines.
- **/
- private int findHeaderEnd(final byte[] buf, int rlen)
- {
- int splitbyte = 0;
- while (splitbyte + 3 < rlen)
- {
- if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n')
- return splitbyte + 4;
- splitbyte++;
- }
- return 0;
- }
-
- /**
- * Find the byte positions where multipart boundaries start.
- **/
- public int[] getBoundaryPositions(byte[] b, byte[] boundary)
- {
- int matchcount = 0;
- int matchbyte = -1;
- Vector matchbytes = new Vector();
- for (int i=0; i 0)
- {
- String tmpdir = System.getProperty("java.io.tmpdir");
- try {
- File temp = File.createTempFile("NanoHTTPD", "", new File(tmpdir));
- OutputStream fstream = new FileOutputStream(temp);
- fstream.write(b, offset, len);
- fstream.close();
- path = temp.getAbsolutePath();
- } catch (Exception e) { // Catch exception if any
- myErr.println("Error: " + e.getMessage());
- }
- }
- return path;
- }
-
-
- /**
- * It returns the offset separating multipart file headers
- * from the file's data.
- **/
- private int stripMultipartHeaders(byte[] b, int offset)
- {
- int i = 0;
- for (i=offset; i "an example string"}
- */
- private String decodePercent( String str ) throws InterruptedException
- {
- try
- {
- StringBuffer sb = new StringBuffer();
- for( int i=0; i= 0 )
- p.put( decodePercent( e.substring( 0, sep )).trim(),
- decodePercent( e.substring( sep+1 )));
- }
- }
-
- /**
- * Returns an error message as a HTTP response and
- * throws InterruptedException to stop further request processing.
- */
- private void sendError( String status, String msg ) throws InterruptedException
- {
- sendResponse( status, MIME_PLAINTEXT, null, new ByteArrayInputStream( msg.getBytes()));
- throw new InterruptedException();
- }
-
- /**
- * Sends given response to the socket.
- */
- private void sendResponse( String status, String mime, Properties header, InputStream data )
- {
- try
- {
- if ( status == null )
- throw new Error( "sendResponse(): Status can't be null." );
-
- OutputStream out = mySocket.getOutputStream();
- PrintWriter pw = new PrintWriter( out );
- pw.print("HTTP/1.0 " + status + " \r\n");
-
- if ( mime != null )
- pw.print("Content-Type: " + mime + "\r\n");
-
- if ( header == null || header.getProperty( "Date" ) == null )
- pw.print( "Date: " + gmtFrmt.format( new Date()) + "\r\n");
-
- if ( header != null )
- {
- Enumeration e = header.keys();
- while ( e.hasMoreElements())
- {
- String key = (String)e.nextElement();
- String value = header.getProperty( key );
- pw.print( key + ": " + value + "\r\n");
- }
- }
-
- pw.print("\r\n");
- pw.flush();
-
- if ( data != null )
- {
- int pending = data.available(); // This is to support partial sends, see serveFile()
- byte[] buff = new byte[theBufferSize];
- while (pending>0)
- {
- int read = data.read( buff, 0, ( (pending>theBufferSize) ? theBufferSize : pending ));
- if (read <= 0) break;
- out.write( buff, 0, read );
- pending -= read;
- }
- }
- out.flush();
- out.close();
- if ( data != null )
- data.close();
- }
- catch( IOException ioe )
- {
- // Couldn't write? No can do.
- try { mySocket.close(); } catch( Throwable t ) {}
- }
- }
-
- private Socket mySocket;
- }
-
- /**
- * URL-encodes everything between "/"-characters.
- * Encodes spaces as '%20' instead of '+'.
- */
- private String encodeUri( String uri )
- {
- String newUri = "";
- StringTokenizer st = new StringTokenizer( uri, "/ ", true );
- while ( st.hasMoreTokens())
- {
- String tok = st.nextToken();
- if ( tok.equals( "/" ))
- newUri += "/";
- else if ( tok.equals( " " ))
- newUri += "%20";
- else
- {
- newUri += URLEncoder.encode( tok );
- // For Java 1.4 you'll want to use this instead:
- // try { newUri += URLEncoder.encode( tok, "UTF-8" ); } catch ( java.io.UnsupportedEncodingException uee ) {}
- }
- }
- return newUri;
- }
-
- private int myTcpPort;
- private final ServerSocket myServerSocket;
- private Thread myThread;
- private File myRootDir;
-
- // ==================================================
- // File server code
- // ==================================================
-
- /**
- * Serves file from homeDir and its' subdirectories (only).
- * Uses only URI, ignores all headers and HTTP parameters.
- */
- public Response serveFile( String uri, Properties header, File homeDir,
- boolean allowDirectoryListing )
- {
- Response res = null;
-
- // Make sure we won't die of an exception later
- if ( !homeDir.isDirectory())
- res = new Response( HTTP_INTERNALERROR, MIME_PLAINTEXT,
- "INTERNAL ERRROR: serveFile(): given homeDir is not a directory." );
-
- if ( res == null )
- {
- // Remove URL arguments
- uri = uri.trim().replace( File.separatorChar, '/' );
- if ( uri.indexOf( '?' ) >= 0 )
- uri = uri.substring(0, uri.indexOf( '?' ));
-
- // Prohibit getting out of current directory
- if ( uri.startsWith( ".." ) || uri.endsWith( ".." ) || uri.indexOf( "../" ) >= 0 )
- res = new Response( HTTP_FORBIDDEN, MIME_PLAINTEXT,
- "FORBIDDEN: Won't serve ../ for security reasons." );
- }
-
- File f = new File( homeDir, uri );
- if ( res == null && !f.exists())
- res = new Response( HTTP_NOTFOUND, MIME_PLAINTEXT,
- "Error 404, file not found." );
-
- // List the directory, if necessary
- if ( res == null && f.isDirectory())
- {
- // Browsers get confused without '/' after the
- // directory, send a redirect.
- if ( !uri.endsWith( "/" ))
- {
- uri += "/";
- res = new Response( HTTP_REDIRECT, MIME_HTML,
- "Redirected: " +
- uri + " ");
- res.addHeader( "Location", uri );
- }
-
- if ( res == null )
- {
- // First try index.html and index.htm
- if ( new File( f, "index.html" ).exists())
- f = new File( homeDir, uri + "/index.html" );
- else if ( new File( f, "index.htm" ).exists())
- f = new File( homeDir, uri + "/index.htm" );
- // No index file, list the directory if it is readable
- else if ( allowDirectoryListing && f.canRead() )
- {
- String[] files = f.list();
- String msg = "Directory " + uri + " ";
-
- if ( uri.length() > 1 )
- {
- String u = uri.substring( 0, uri.length()-1 );
- int slash = u.lastIndexOf( '/' );
- if ( slash >= 0 && slash < u.length())
- msg += ".. ";
- }
-
- if (files!=null)
- {
- for ( int i=0; i";
- files[i] += "/";
- }
-
- msg += "" +
- files[i] + " ";
-
- // Show file size
- if ( curFile.isFile())
- {
- long len = curFile.length();
- msg += " (";
- if ( len < 1024 )
- msg += len + " bytes";
- else if ( len < 1024 * 1024 )
- msg += len/1024 + "." + (len%1024/10%100) + " KB";
- else
- msg += len/(1024*1024) + "." + len%(1024*1024)/10%100 + " MB";
-
- msg += ") ";
- }
- msg += " ";
- if ( dir ) msg += "";
- }
- }
- msg += "";
- res = new Response( HTTP_OK, MIME_HTML, msg );
- }
- else
- {
- res = new Response( HTTP_FORBIDDEN, MIME_PLAINTEXT,
- "FORBIDDEN: No directory listing." );
- }
- }
- }
-
- try
- {
- if ( res == null )
- {
- // Get MIME type from file name extension, if possible
- String mime = null;
- int dot = f.getCanonicalPath().lastIndexOf( '.' );
- if ( dot >= 0 )
- mime = (String)theMimeTypes.get( f.getCanonicalPath().substring( dot + 1 ).toLowerCase());
- if ( mime == null )
- mime = MIME_DEFAULT_BINARY;
-
- // Calculate etag
- String etag = Integer.toHexString((f.getAbsolutePath() + f.lastModified() + "" + f.length()).hashCode());
-
- // Support (simple) skipping:
- long startFrom = 0;
- long endAt = -1;
- String range = header.getProperty( "range" );
- if ( range != null )
- {
- if ( range.startsWith( "bytes=" ))
- {
- range = range.substring( "bytes=".length());
- int minus = range.indexOf( '-' );
- try {
- if ( minus > 0 )
- {
- startFrom = Long.parseLong( range.substring( 0, minus ));
- endAt = Long.parseLong( range.substring( minus+1 ));
- }
- }
- catch ( NumberFormatException nfe ) {}
- }
- }
-
- // Change return code and add Content-Range header when skipping is requested
- long fileLen = f.length();
- if (range != null && startFrom >= 0)
- {
- if ( startFrom >= fileLen)
- {
- res = new Response( HTTP_RANGE_NOT_SATISFIABLE, MIME_PLAINTEXT, "" );
- res.addHeader( "Content-Range", "bytes 0-0/" + fileLen);
- res.addHeader( "ETag", etag);
- }
- else
- {
- if ( endAt < 0 )
- endAt = fileLen-1;
- long newLen = endAt - startFrom + 1;
- if ( newLen < 0 ) newLen = 0;
-
- final long dataLen = newLen;
- FileInputStream fis = new FileInputStream( f ) {
- @Override
- public int available() throws IOException { return (int)dataLen; }
- };
- fis.skip( startFrom );
-
- res = new Response( HTTP_PARTIALCONTENT, mime, fis );
- res.addHeader( "Content-Length", "" + dataLen);
- res.addHeader( "Content-Range", "bytes " + startFrom + "-" + endAt + "/" + fileLen);
- res.addHeader( "ETag", etag);
- }
- }
- else
- {
- if (etag.equals(header.getProperty("if-none-match")))
- res = new Response( HTTP_NOTMODIFIED, mime, "");
- else
- {
- res = new Response( HTTP_OK, mime, new FileInputStream( f ));
- res.addHeader( "Content-Length", "" + fileLen);
- res.addHeader( "ETag", etag);
- }
- }
- }
- }
- catch( IOException ioe )
- {
- res = new Response( HTTP_FORBIDDEN, MIME_PLAINTEXT, "FORBIDDEN: Reading file failed." );
- }
-
- res.addHeader( "Accept-Ranges", "bytes"); // Announce that the file server accepts partial content requestes
- return res;
- }
-
- /**
- * Hashtable mapping (String)FILENAME_EXTENSION -> (String)MIME_TYPE
- */
- private static Hashtable theMimeTypes = new Hashtable();
- static
- {
- StringTokenizer st = new StringTokenizer(
- "css text/css "+
- "htm text/html "+
- "html text/html "+
- "xml text/xml "+
- "txt text/plain "+
- "asc text/plain "+
- "gif image/gif "+
- "jpg image/jpeg "+
- "jpeg image/jpeg "+
- "png image/png "+
- "mp3 audio/mpeg "+
- "m3u audio/mpeg-url " +
- "mp4 video/mp4 " +
- "ogv video/ogg " +
- "flv video/x-flv " +
- "mov video/quicktime " +
- "swf application/x-shockwave-flash " +
- "js application/javascript "+
- "pdf application/pdf "+
- "doc application/msword "+
- "ogg application/x-ogg "+
- "zip application/octet-stream "+
- "exe application/octet-stream "+
- "class application/octet-stream " );
- while ( st.hasMoreTokens())
- theMimeTypes.put( st.nextToken(), st.nextToken());
- }
-
- private static int theBufferSize = 16 * 1024;
-
- // Change these if you want to log to somewhere else than stdout
- protected static PrintStream myOut = System.out;
- protected static PrintStream myErr = System.err;
-
- /**
- * GMT date formatter
- */
- private static java.text.SimpleDateFormat gmtFrmt;
- static
- {
- gmtFrmt = new java.text.SimpleDateFormat( "E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
- gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));
- }
-
- /**
- * The distribution licence
- */
- private static final String LICENCE =
- "Copyright (C) 2001,2005-2011 by Jarno Elonen \n"+
- "and Copyright (C) 2010 by Konstantinos Togias \n"+
- "\n"+
- "Redistribution and use in source and binary forms, with or without\n"+
- "modification, are permitted provided that the following conditions\n"+
- "are met:\n"+
- "\n"+
- "Redistributions of source code must retain the above copyright notice,\n"+
- "this list of conditions and the following disclaimer. Redistributions in\n"+
- "binary form must reproduce the above copyright notice, this list of\n"+
- "conditions and the following disclaimer in the documentation and/or other\n"+
- "materials provided with the distribution. The name of the author may not\n"+
- "be used to endorse or promote products derived from this software without\n"+
- "specific prior written permission. \n"+
- " \n"+
- "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"+
- "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"+
- "OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"+
- "IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"+
- "INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n"+
- "NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"+
- "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"+
- "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"+
- "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"+
- "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.";
-}
\ No newline at end of file
diff --git a/jodd-http/src/test/java/jodd/http/ProxyTest.java b/jodd-http/src/test/java/jodd/http/ProxyTest.java
deleted file mode 100644
index 9c818ca10..000000000
--- a/jodd-http/src/test/java/jodd/http/ProxyTest.java
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import io.netty.handler.codec.http.HttpHeaders;
-import jodd.http.net.SocketHttpConnectionProvider;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.mockserver.integration.ClientAndProxy;
-import org.mockserver.integration.ClientAndServer;
-import org.mockserver.model.Header;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockserver.integration.ClientAndProxy.startClientAndProxy;
-import static org.mockserver.integration.ClientAndServer.startClientAndServer;
-import static org.mockserver.model.HttpRequest.request;
-import static org.mockserver.model.HttpResponse.response;
-import static org.mockserver.verify.VerificationTimes.exactly;
-
-class ProxyTest {
-
- private ClientAndProxy proxy;
- private ClientAndServer mockServer;
-
- @BeforeEach
- void startProxy() {
- mockServer = startClientAndServer(1080);
- proxy = startClientAndProxy(1090);
- setupMockServer();
- }
-
- @AfterEach
- void stopProxy() {
- proxy.stop();
- mockServer.stop();
- }
-
- @Test
- void testDirect() {
- HttpResponse response = HttpRequest.get("http://localhost:1080/get_books").send();
- assertEquals(200, response.statusCode());
- assertTrue(response.body().contains("Tatum"));
- proxy.verify(request().withPath("/get_books"), exactly(0));
- }
-
- @Test
- void testDirectHttps() {
- HttpResponse response = HttpRequest.get("https://localhost:1080/get_books").trustAllCerts(true).send();
- assertEquals(200, response.statusCode());
- assertTrue(response.body().contains("Tatum"));
- proxy.verify(request().withPath("/get_books"), exactly(0));
- }
-
- @Test
- @Disabled
- void testHttpProxy() {
- SocketHttpConnectionProvider s = new SocketHttpConnectionProvider();
- s.useProxy(ProxyInfo.httpProxy("localhost", 1090, null, null));
-
- HttpResponse response = HttpRequest.get("http://localhost:1080/get_books")
- .withConnectionProvider(s)
- .send();
- assertEquals(200, response.statusCode());
- assertTrue(response.body().contains("Tatum"));
- }
-
- @Test
- void testSocks5Proxy() {
- SocketHttpConnectionProvider s = new SocketHttpConnectionProvider();
- s.useProxy(ProxyInfo.socks5Proxy("localhost", 1090, null, null));
-
- HttpResponse response = HttpRequest.get("http://localhost:1080/get_books")
- .withConnectionProvider(s)
- .send();
- assertEquals(200, response.statusCode());
- assertTrue(response.body().contains("Tatum"));
- proxy.verify(request().withPath("/get_books"), exactly(1));
- }
-
- @Test
- void testSocks5ProxyWithHttps() {
- SocketHttpConnectionProvider s = new SocketHttpConnectionProvider();
- s.useProxy(ProxyInfo.socks5Proxy("localhost", 1090, null, null));
-
- HttpResponse response = HttpRequest.get("https://localhost:1080/get_books")
- .withConnectionProvider(s)
- .trustAllCerts(true)
- .send();
- assertEquals(200, response.statusCode());
- assertTrue(response.body().contains("Tatum"));
- proxy.verify(request().withPath("/get_books"), exactly(1));
- }
-
- private void setupMockServer() {
- mockServer
- .when(
- request()
- .withPath("/get_books")
- )
- .respond(
- response()
- .withHeaders(
- new Header(HttpHeaders.Names.CONTENT_TYPE,"application/json")
- )
- .withBody("" +
- "[\n" +
- " {\n" +
- " \"id\": \"1\",\n" +
- " \"title\": \"Xenophon's imperial fiction : on the education of Cyrus\",\n" +
- " \"author\": \"James Tatum\",\n" +
- " \"isbn\": \"0691067570\",\n" +
- " \"publicationDate\": \"1989\"\n" +
- " },\n" +
- " {\n" +
- " \"id\": \"2\",\n" +
- " \"title\": \"You are here : personal geographies and other maps of the imagination\",\n" +
- " \"author\": \"Katharine A. Harmon\",\n" +
- " \"isbn\": \"1568984308\",\n" +
- " \"publicationDate\": \"2004\"\n" +
- " },\n" +
- " {\n" +
- " \"id\": \"3\",\n" +
- " \"title\": \"You just don't understand : women and men in conversation\",\n" +
- " \"author\": \"Deborah Tannen\",\n" +
- " \"isbn\": \"0345372050\",\n" +
- " \"publicationDate\": \"1990\"\n" +
- " }" +
- "]")
- );
- }
-}
diff --git a/jodd-http/src/test/java/jodd/http/RawTest.java b/jodd-http/src/test/java/jodd/http/RawTest.java
deleted file mode 100644
index 81e79e0d7..000000000
--- a/jodd-http/src/test/java/jodd/http/RawTest.java
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.io.FileUtil;
-import jodd.util.StringUtil;
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.net.URL;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class RawTest {
-
- @Test
- void testRawResponse1() throws IOException {
- URL data = RawTest.class.getResource("1-response.txt");
-
- String fileContent = FileUtil.readString(data.getFile());
-
- fileContent = StringUtil.replace(fileContent, "\r\n", "\n");
-
- HttpResponse response = HttpResponse.readFrom(new ByteArrayInputStream(fileContent.getBytes("UTF-8")));
-
- HttpMultiMap headers = response.headers;
- assertEquals(7, headers.size());
-
- assertEquals("no-cache", headers.get("pragma"));
- assertEquals("Sat, 23 Mar 2013 23:34:18 GMT", headers.get("date"));
- assertEquals("max-age=0, must-revalidate, no-cache, no-store, private, post-check=0, pre-check=0",
- headers.get("cache-control"));
- assertEquals("no-cache", headers.get("pragma"));
- assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", headers.get("expires"));
- assertEquals("text/html;charset=UTF-8", headers.get("content-type"));
- assertEquals("close", headers.get("connection"));
- assertEquals("102", headers.get("content-length"));
-
- assertEquals("no-cache", response.header("Pragma"));
- assertEquals("text/html;charset=UTF-8" , response.contentType());
- assertEquals("text/html" , response.mediaType());
- assertEquals("UTF-8" , response.charset());
-
- assertNotNull(response.contentLength());
-
- String rawBody = response.body();
- String textBody = response.bodyText();
-
- assertTrue(rawBody.startsWith(""));
- assertTrue(rawBody.endsWith(""));
-
- assertTrue(rawBody.contains("This is UTF8 Encoding."));
- assertFalse(rawBody.contains("Тхис ис УТФ8 Енцодинг."));
- assertTrue(textBody.contains("Тхис ис УТФ8 Енцодинг."));
-
- assertEquals(77, textBody.length());
-
- int len = textBody.getBytes("UTF-8").length;
-
- assertEquals(94, rawBody.length());
- assertEquals(len, rawBody.length());
- }
-
- @Test
- void testRawResponse4() throws IOException {
- URL data = RawTest.class.getResource("4-response.txt");
-
- String fileContent = FileUtil.readString(data.getFile());
-
- fileContent = StringUtil.replace(fileContent, "\n", "\r\n");
- fileContent = StringUtil.replace(fileContent, "\r\r\n", "\r\n");
-
- HttpResponse response = HttpResponse.readFrom(new ByteArrayInputStream(fileContent.getBytes("UTF-8")));
-
- String body = response.bodyText();
-
- assertEquals(
- "Wikipedia in\n" +
- "\n" +
- "chunks.", body.replace("\r\n", "\n"));
- }
-
-
- @Test
- void testRawResponse5() throws IOException {
- URL data = RawTest.class.getResource("5-response.txt");
-
- String fileContent = FileUtil.readString(data.getFile());
-
- fileContent = StringUtil.replace(fileContent, "\n", "\r\n");
- fileContent = StringUtil.replace(fileContent, "\r\r\n", "\r\n");
-
- HttpResponse response = HttpResponse.readFrom(new ByteArrayInputStream(fileContent.getBytes("UTF-8")));
-
- String body = response.bodyText();
-
- assertEquals(
- "Wikipedia in\n" +
- "\n" +
- "chunks.", body.replace("\r\n", "\n"));
-
- assertEquals("TheData", response.header("SomeAfterHeader"));
- }
-
- @Test
- void testRawResponse6() throws IOException {
- URL data = RawTest.class.getResource("6-response.txt");
-
- String fileContent = FileUtil.readString(data.getFile());
-
- fileContent = StringUtil.replace(fileContent, "\n", "\r\n");
- fileContent = StringUtil.replace(fileContent, "\r\r\n", "\r\n");
-
- HttpResponse response = HttpResponse.readFrom(new ByteArrayInputStream(fileContent.getBytes("UTF-8")));
-
- assertEquals(200, response.statusCode());
- assertEquals("", response.statusPhrase);
-
- String body = response.bodyText();
-
- assertEquals(
- "Wikipedia in\n" +
- "\n" +
- "chunks.", body.replace("\r\n", "\n"));
-
- assertEquals("TheData", response.header("SomeAfterHeader"));
- }
-
-
-}
diff --git a/jodd-http/src/test/java/jodd/http/TestServer.java b/jodd-http/src/test/java/jodd/http/TestServer.java
deleted file mode 100644
index e9bc74f2d..000000000
--- a/jodd-http/src/test/java/jodd/http/TestServer.java
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import jodd.io.FileUtil;
-
-import java.io.File;
-import java.net.URL;
-
-
-/**
- * Common server content.
- */
-public abstract class TestServer {
-
- protected File webRoot;
-
- public void start() throws Exception {
- webRoot = FileUtil.createTempDirectory("jodd-http", "test");
- webRoot.deleteOnExit();
-
- // web-inf
-
- File webInfFolder = new File(webRoot, "WEB-INF");
- webInfFolder.mkdir();
-
- // web.xml
-
- URL webXmlUrl = TestServer.class.getResource("web.xml");
- File webXmlFile = FileUtil.toFile(webXmlUrl);
-
- FileUtil.copy(webXmlFile, webInfFolder);
-
- // lib folder
-
- File libFolder = new File(webInfFolder, "lib");
- libFolder.mkdir();
-
- // classes
-
- File classes = new File(webInfFolder, "classes/jodd/http/fixture");
- classes.mkdirs();
-
- URL echoServletUrl = TestServer.class.getResource("fixture/EchoServlet.class");
- File echoServletFile = FileUtil.toFile(echoServletUrl);
- FileUtil.copyFileToDir(echoServletFile, classes);
-
- echoServletUrl = TestServer.class.getResource("fixture/Echo2Servlet.class");
- echoServletFile = FileUtil.toFile(echoServletUrl);
- FileUtil.copyFileToDir(echoServletFile, classes);
-
- echoServletUrl = TestServer.class.getResource("fixture/Echo3Servlet.class");
- echoServletFile = FileUtil.toFile(echoServletUrl);
- FileUtil.copyFileToDir(echoServletFile, classes);
-
- URL redirectServletUrl = TestServer.class.getResource("fixture/RedirectServlet.class");
- File redirectServletFile = FileUtil.toFile(redirectServletUrl);
- FileUtil.copyFileToDir(redirectServletFile, classes);
-
- URL targetServletUrl = TestServer.class.getResource("fixture/TargetServlet.class");
- File targetServletFile = FileUtil.toFile(targetServletUrl);
- FileUtil.copyFileToDir(targetServletFile, classes);
- }
-
- public void stop() throws Exception {
- webRoot.delete();
- }
-}
\ No newline at end of file
diff --git a/jodd-http/src/test/java/jodd/http/TimeoutTest.java b/jodd-http/src/test/java/jodd/http/TimeoutTest.java
deleted file mode 100644
index ff3df7f92..000000000
--- a/jodd-http/src/test/java/jodd/http/TimeoutTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.fail;
-
-class TimeoutTest {
-
- static TestServer testServer;
-
- @BeforeAll
- static void startServer() throws Exception {
- testServer = new TomcatServer();
- testServer.start();
- }
-
- @AfterAll
- static void stopServer() throws Exception {
- testServer.stop();
- }
-
- @Test
- void testTimeout() {
- HttpRequest httpRequest = HttpRequest.get("localhost:8173/slow");
- httpRequest.timeout(1000);
-
- try {
- httpRequest.send();
- fail("error");
- }
- catch(HttpException ignore) {
- }
-
- httpRequest = HttpRequest.get("localhost:8173/slow");
- httpRequest.timeout(6000);
-
- int status = httpRequest.send().statusCode();
-
- assertEquals(200, status);
- }
-}
diff --git a/jodd-http/src/test/java/jodd/http/TomcatServer.java b/jodd-http/src/test/java/jodd/http/TomcatServer.java
deleted file mode 100644
index 74f964cb7..000000000
--- a/jodd-http/src/test/java/jodd/http/TomcatServer.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http;
-
-import org.apache.catalina.startup.Tomcat;
-
-/**
- * Tomcat Server.
- */
-public class TomcatServer extends TestServer {
-
- protected Tomcat tomcat;
-
- @Override
- public void start() throws Exception {
- super.start();
-
- String workingDir = System.getProperty("java.io.tmpdir");
-
- tomcat = new Tomcat();
- tomcat.setPort(8173);
- tomcat.setBaseDir(workingDir);
- tomcat.addWebapp("", webRoot.getAbsolutePath());
-
- tomcat.start();
- }
-
- @Override
- public void stop() throws Exception {
- tomcat.stop();
- tomcat.destroy();
- super.stop();
- }
-}
\ No newline at end of file
diff --git a/jodd-http/src/test/java/jodd/http/fixture/Data.java b/jodd-http/src/test/java/jodd/http/fixture/Data.java
deleted file mode 100644
index 42d4f18c8..000000000
--- a/jodd-http/src/test/java/jodd/http/fixture/Data.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.fixture;
-
-import javax.servlet.http.Cookie;
-import java.util.Map;
-
-public class Data {
-
- public static Data ref;
-
- public boolean get;
- public boolean post;
- public String queryString;
- public String body;
- public Map header;
- public Map params;
- public Map parts;
- public Map fileNames;
- public Cookie[] cookies;
-}
diff --git a/jodd-http/src/test/java/jodd/http/fixture/Echo2Servlet.java b/jodd-http/src/test/java/jodd/http/fixture/Echo2Servlet.java
deleted file mode 100644
index 1d64cc33f..000000000
--- a/jodd-http/src/test/java/jodd/http/fixture/Echo2Servlet.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.fixture;
-
-import javax.servlet.annotation.MultipartConfig;
-import javax.servlet.http.HttpServletRequest;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-@MultipartConfig
-public class Echo2Servlet extends EchoServlet {
-
- @Override
- protected void readAll(final HttpServletRequest req) throws IOException {
- Data.ref.queryString = req.getQueryString();
- Data.ref.header = copyHeaders(req);
- Data.ref.params = copyParams(req, StandardCharsets.UTF_8.name());
- Data.ref.parts = copyParts(req);
- Data.ref.fileNames = copyFileName(req);
- }
-
-}
diff --git a/jodd-http/src/test/java/jodd/http/fixture/Echo3Servlet.java b/jodd-http/src/test/java/jodd/http/fixture/Echo3Servlet.java
deleted file mode 100644
index 559d5ee11..000000000
--- a/jodd-http/src/test/java/jodd/http/fixture/Echo3Servlet.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.fixture;
-
-import javax.servlet.annotation.MultipartConfig;
-import javax.servlet.http.HttpServletRequest;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-@MultipartConfig
-public class Echo3Servlet extends EchoServlet {
-
- @Override
- protected void readAll(final HttpServletRequest req) throws IOException {
- Data.ref.queryString = req.getQueryString();
- Data.ref.header = copyHeaders(req);
- Data.ref.params = copyParams(req, StandardCharsets.ISO_8859_1.name());
- Data.ref.parts = copyParts(req);
- Data.ref.fileNames = copyFileName(req);
- }
-
-}
diff --git a/jodd-http/src/test/java/jodd/http/fixture/EchoServlet.java b/jodd-http/src/test/java/jodd/http/fixture/EchoServlet.java
deleted file mode 100644
index 372c84129..000000000
--- a/jodd-http/src/test/java/jodd/http/fixture/EchoServlet.java
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.fixture;
-
-import jodd.io.IOUtil;
-import jodd.util.StringUtil;
-import org.apache.catalina.core.ApplicationPart;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.Part;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-
-public class EchoServlet extends HttpServlet {
-
- @Override
- protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
- Data.ref = new Data();
- Data.ref.get = true;
- Data.ref.post = false;
- readAll(req);
-
- if (Data.ref.cookies != null) {
- for (final Cookie cookie : Data.ref.cookies) {
- cookie.setValue(cookie.getValue() + "!");
- resp.addCookie(cookie);
- }
- }
-
- write(resp, Data.ref.body);
- }
-
- @Override
- protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
- Data.ref = new Data();
- Data.ref.post = true;
- Data.ref.get = false;
- readAll(req);
- write(resp, Data.ref.body);
- }
-
- // ---------------------------------------------------------------- write
-
- protected void write(final HttpServletResponse resp, final String text) throws IOException {
- if (text != null) {
- resp.setContentLength(text.getBytes(StandardCharsets.UTF_8).length);
- resp.setContentType("text/html;charset=UTF-8");
- resp.getWriter().write(text);
- resp.flushBuffer();
- }
- }
-
- // ---------------------------------------------------------------- read all
-
- protected void readAll(final HttpServletRequest req) throws IOException {
- Data.ref.body = readRequestBody(req);
- Data.ref.queryString = req.getQueryString();
- Data.ref.header = copyHeaders(req);
- Data.ref.cookies = req.getCookies();
- }
-
- protected String readRequestBody(final HttpServletRequest request) throws IOException {
- final BufferedReader buff = request.getReader();
- final StringWriter out = new StringWriter();
- IOUtil.copy(buff, out);
- return out.toString();
- }
-
- protected Map copyHeaders(final HttpServletRequest req) {
- final Enumeration enumeration = req.getHeaderNames();
- final Map header = new HashMap<>();
-
- while (enumeration.hasMoreElements()) {
- final String name = enumeration.nextElement().toString();
- final String value = req.getHeader(name);
- header.put(name, value);
- }
-
- return header;
- }
-
- protected Map copyParams(final HttpServletRequest req, final String fromEncoding) {
- final String charset = req.getParameter("enc");
-
- final Enumeration enumeration = req.getParameterNames();
- final Map params = new HashMap<>();
-
- while (enumeration.hasMoreElements()) {
- final String name = enumeration.nextElement().toString();
- String value = req.getParameter(name);
- if (charset != null) {
- value = StringUtil.convertCharset(value, Charset.forName(fromEncoding), Charset.forName(charset));
- }
- params.put(name, value);
- }
-
- return params;
- }
-
- protected Map copyParts(final HttpServletRequest req) {
- final Map parts = new HashMap<>();
- if (req.getContentType() == null) {
- return parts;
- }
- if (req.getContentType() != null && !req.getContentType().toLowerCase().contains("multipart/form-data")) {
- return parts;
- }
-
- final String enc = "UTF-8";
-
- try {
- final Collection prs = req.getParts();
-
- for (final Part p : prs) {
- parts.put(p.getName(), new String(IOUtil.readBytes(p.getInputStream()), enc));
- }
- }
- catch (final IOException | ServletException e) {
- e.printStackTrace();
- }
-
- return parts;
- }
-
- protected Map copyFileName(final HttpServletRequest req) {
- final Map parts = new HashMap<>();
- if (req.getContentType() == null) {
- return parts;
- }
- if (req.getContentType() != null && !req.getContentType().toLowerCase().contains("multipart/form-data")) {
- return parts;
- }
-
- try {
- final Collection prs = req.getParts();
-
- for (final Part p : prs) {
- if (p instanceof ApplicationPart) {
- final ApplicationPart ap = (ApplicationPart) p;
- parts.put(p.getName(), ap.getSubmittedFileName());
- }
- }
- }
- catch (final IOException | ServletException e) {
- e.printStackTrace();
- }
-
- return parts;
- }
-
-}
diff --git a/jodd-http/src/test/java/jodd/http/fixture/RedirectServlet.java b/jodd-http/src/test/java/jodd/http/fixture/RedirectServlet.java
deleted file mode 100644
index 62dabd6da..000000000
--- a/jodd-http/src/test/java/jodd/http/fixture/RedirectServlet.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.fixture;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-
-/**
- * Simply redirects to '/target'.
- */
-public class RedirectServlet extends HttpServlet {
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
-
- resp.sendRedirect("/target");
- }
-}
\ No newline at end of file
diff --git a/jodd-http/src/test/java/jodd/http/fixture/SlowServlet.java b/jodd-http/src/test/java/jodd/http/fixture/SlowServlet.java
deleted file mode 100644
index e9f6e9fb9..000000000
--- a/jodd-http/src/test/java/jodd/http/fixture/SlowServlet.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.fixture;
-
-import jodd.util.ThreadUtil;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-public class SlowServlet extends HttpServlet {
-
- @Override
- protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
- ThreadUtil.sleep(5000);
- write(resp, "OK");
- }
-
- protected void write(final HttpServletResponse resp, final String text) throws IOException {
- if (text != null) {
- resp.setContentLength(text.getBytes(StandardCharsets.UTF_8.name()).length);
- resp.setContentType("text/html;charset=UTF-8");
- resp.getWriter().write(text);
- resp.flushBuffer();
- }
- }
-
-}
diff --git a/jodd-http/src/test/java/jodd/http/fixture/TargetServlet.java b/jodd-http/src/test/java/jodd/http/fixture/TargetServlet.java
deleted file mode 100644
index 69e0d1c94..000000000
--- a/jodd-http/src/test/java/jodd/http/fixture/TargetServlet.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.http.fixture;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-
-public class TargetServlet extends HttpServlet {
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
-
- resp.getWriter().write("target!");
- }
-
-
-}
\ No newline at end of file
diff --git a/jodd-http/src/test/resources/jodd/http/1-response.txt b/jodd-http/src/test/resources/jodd/http/1-response.txt
deleted file mode 100644
index 14b948a27..000000000
--- a/jodd-http/src/test/resources/jodd/http/1-response.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-HTTP/1.1 200 OK
-Date: Sat, 23 Mar 2013 23:34:18 GMT
-Cache-Control: max-age=0, must-revalidate, no-cache, no-store, private, post-check=0, pre-check=0
-Pragma: no-cache
-Expires: Thu, 01 Jan 1970 00:00:00 GMT
-Content-Type: text/html;charset=UTF-8
-Connection: close
-Content-Length: 102
-
-
-
-
-This is UTF8 Encoding.
-Тхис ис УТФ8 Енцодинг.
-
-
-
\ No newline at end of file
diff --git a/jodd-http/src/test/resources/jodd/http/2-response.txt b/jodd-http/src/test/resources/jodd/http/2-response.txt
deleted file mode 100644
index 5fc7e7b26..000000000
--- a/jodd-http/src/test/resources/jodd/http/2-response.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-HTTP/1.1 200 OK
-Content-Type: application/json; charset=UTF-8
-Date: Thu, 27 Feb 2014 21:10:55 GMT
-Expires: Fri, 28 Feb 2014 21:10:55 GMT
-Cache-Control: public, max-age=86400
-Vary: Accept-Language
-Access-Control-Allow-Origin: *
-Server: mafe
-X-Xss-Protection: 1; mode=block
-X-Frame-Options: SAMEORIGIN
-Alternate-Protocol: 80:quic
-Connection: close
\ No newline at end of file
diff --git a/jodd-http/src/test/resources/jodd/http/3-response.txt b/jodd-http/src/test/resources/jodd/http/3-response.txt
deleted file mode 100644
index 6b3694306..000000000
--- a/jodd-http/src/test/resources/jodd/http/3-response.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-HTTP/1.1 200 OK
-Content-Type: application/json; charset=UTF-8
-Date: Thu, 27 Feb 2014 21:10:55 GMT
-Expires: Fri, 28 Feb 2014 21:10:55 GMT
-Cache-Control: public, max-age=86400
-Vary: Accept-Language
-Access-Control-Allow-Origin: *
-Server: mafe
-X-Xss-Protection: 1; mode=block
-X-Frame-Options: SAMEORIGIN
-Alternate-Protocol: 80:quic
-Connection: close
-
-Body!
\ No newline at end of file
diff --git a/jodd-http/src/test/resources/jodd/http/4-response.txt b/jodd-http/src/test/resources/jodd/http/4-response.txt
deleted file mode 100644
index 5e0b208df..000000000
--- a/jodd-http/src/test/resources/jodd/http/4-response.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-HTTP/1.1 200 OK
-content-type:text/html; charset=utf-8
-Transfer-Encoding: chunked
-Connection: close
-
-4
-Wiki
-5
-pedia
-e
- in
-
-chunks.
-0
diff --git a/jodd-http/src/test/resources/jodd/http/5-response.txt b/jodd-http/src/test/resources/jodd/http/5-response.txt
deleted file mode 100644
index 9865db525..000000000
--- a/jodd-http/src/test/resources/jodd/http/5-response.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-HTTP/1.1 200 OK
-content-type:text/html; charset=utf-8
-Transfer-Encoding: chunked
-Connection: close
-
-4
-Wiki
-5
-pedia
-e
- in
-
-chunks.
-0
-SomeAfterHeader: TheData
diff --git a/jodd-http/src/test/resources/jodd/http/6-response.txt b/jodd-http/src/test/resources/jodd/http/6-response.txt
deleted file mode 100644
index c9077d96f..000000000
--- a/jodd-http/src/test/resources/jodd/http/6-response.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-HTTP/1.1 200
-content-type:text/html; charset=utf-8
-Content-Length: 102
-Transfer-Encoding: chunked
-Connection: close
-
-4
-Wiki
-5
-pedia
-e
- in
-
-chunks.
-0
-SomeAfterHeader: TheData
diff --git a/jodd-http/src/test/resources/jodd/http/answer.json b/jodd-http/src/test/resources/jodd/http/answer.json
deleted file mode 100644
index 40057d576..000000000
--- a/jodd-http/src/test/resources/jodd/http/answer.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "Code": "rne",
- "Question": {
- "Choices": [
- {
- "FullName": "Abc Def",
- "Code": "rfr"
- },
- {
- "FullName": "Crocodil Dunddy",
- "Code": "mios"
- },
- {
- "FullName": "Super Duper",
- "Code": "rne"
- }
- ],
- "Solution": "nYZmfwV/lZKk2K7Aai30qOtNhQk=",
- "UserThumb": "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"
- }
-}
\ No newline at end of file
diff --git a/jodd-http/src/test/resources/jodd/http/web.xml b/jodd-http/src/test/resources/jodd/http/web.xml
deleted file mode 100644
index 2d5d23f52..000000000
--- a/jodd-http/src/test/resources/jodd/http/web.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
-
-
-
- EchoServlet
- EchoServlet
- jodd.http.fixture.EchoServlet
-
-
-
- EchoServlet
- /echo
-
-
-
- Echo2Servlet
- Echo2Servlet
- jodd.http.fixture.Echo2Servlet
-
-
-
- Echo2Servlet
- /echo2
-
-
-
- Echo3Servlet
- Echo3Servlet
- jodd.http.fixture.Echo3Servlet
-
-
-
- Echo3Servlet
- /echo3
-
-
-
- TargetServlet
- TargetServlet
- jodd.http.fixture.TargetServlet
-
-
-
- TargetServlet
- /target
-
-
-
- RedirectServlet
- RedirectServlet
- jodd.http.fixture.RedirectServlet
-
-
-
- RedirectServlet
- /redirect
-
-
-
- SlowServlet
- SlowServlet
- jodd.http.fixture.SlowServlet
-
-
-
- SlowServlet
- /slow
-
-
-
\ No newline at end of file
diff --git a/jodd-joy/build.gradle b/jodd-joy/build.gradle
index 57e9728ff..152ad8899 100644
--- a/jodd-joy/build.gradle
+++ b/jodd-joy/build.gradle
@@ -4,7 +4,7 @@ ext.moduleDescription = 'Jodd Joy is set of Jodd extensions that makes developme
dependencies {
api project(':jodd-core')
- api project(':jodd-props')
+
api project(':jodd-petite')
api project(':jodd-madvoc')
api project(':jodd-vtor')
@@ -13,17 +13,17 @@ dependencies {
api project(':jodd-proxetta')
api 'org.slf4j:slf4j-api:1.7.30'
api project(':jodd-decora')
- api project(':jodd-http')
api project(':jodd-servlet')
- api 'org.jodd:jodd-mail:6.0.1'
+ api 'org.jodd:jodd-mail:6.0.2'
+ api 'org.jodd:jodd-props:6.0.1'
+ api 'org.jodd:jodd-http:6.0.2'
api 'org.mindrot:jbcrypt:0.4'
provided lib.servlet
provided lib.jsp
- testImplementation project(':jodd-http')
// testCompile project(':jodd-db').sourceSets.test.output
testImplementation lib.junit5
testImplementation lib.hsqldb
diff --git a/jodd-joy/src/main/java/jodd/joy/JoyProps.java b/jodd-joy/src/main/java/jodd/joy/JoyProps.java
index 5c68f4080..cea53a37c 100644
--- a/jodd-joy/src/main/java/jodd/joy/JoyProps.java
+++ b/jodd-joy/src/main/java/jodd/joy/JoyProps.java
@@ -25,8 +25,13 @@
package jodd.joy;
+import jodd.core.JoddCore;
+import jodd.exception.UncheckedException;
+import jodd.io.findfile.ClassScanner;
import jodd.props.Props;
+import jodd.util.StringUtil;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -55,8 +60,8 @@ public Props getProps() {
// ---------------------------------------------------------------- config
- private List propsNamePatterns = new ArrayList<>();
- private List propsProfiles = new ArrayList<>();
+ private final List propsNamePatterns = new ArrayList<>();
+ private final List propsProfiles = new ArrayList<>();
/**
* Adds props files or patterns.
@@ -115,7 +120,7 @@ public void start() {
final long startTime = System.currentTimeMillis();
- props.loadFromClasspath(patterns);
+ loadFromClasspath(props, patterns);
log.debug("Props scanning completed in " + (System.currentTimeMillis() - startTime) + "ms.");
@@ -124,6 +129,27 @@ public void start() {
log.info("PROPS OK!");
}
+ private void loadFromClasspath(final Props props, final String... patterns) {
+ ClassScanner.create()
+ .registerEntryConsumer(entryData -> {
+ String usedEncoding = JoddCore.encoding;
+ if (StringUtil.endsWithIgnoreCase(entryData.name(), ".properties")) {
+ usedEncoding = StandardCharsets.ISO_8859_1.name();
+ }
+
+ final String encoding = usedEncoding;
+ UncheckedException.runAndWrapException(() -> props.load(entryData.openInputStream(), encoding));
+ })
+ .includeResources(true)
+ .ignoreException(true)
+ .excludeCommonJars()
+ .excludeAllEntries(true)
+ .includeEntries(patterns)
+ .scanDefaultClasspath()
+ .start();
+ }
+
+
/**
* Creates new {@link Props} with default configuration.
* Empty props will be ignored, and missing macros will be
diff --git a/jodd-madvoc/build.gradle b/jodd-madvoc/build.gradle
index db391d104..3aa9c9337 100644
--- a/jodd-madvoc/build.gradle
+++ b/jodd-madvoc/build.gradle
@@ -4,17 +4,17 @@ ext.moduleDescription = 'Jodd Madvoc is elegant web MVC framework that uses CoC
dependencies {
api project(':jodd-core')
- api project(':jodd-props')
api project(':jodd-servlet')
api project(':jodd-petite')
api project(':jodd-proxetta')
- api 'org.jodd:jodd-json:6.0.1'
+ api 'org.jodd:jodd-json:6.0.2'
+ api 'org.jodd:jodd-props:6.0.1'
api 'org.slf4j:slf4j-api:1.7.30'
provided lib.servlet
provided lib.jsp
- testImplementation project(':jodd-http')
+ testImplementation 'org.jodd:jodd-http:6.0.2'
testImplementation project(':jodd-core').sourceSets.test.output
testImplementation lib.junit5
testImplementation lib.tomcat_embed
diff --git a/jodd-madvoc/src/main/java/jodd/madvoc/Madvoc.java b/jodd-madvoc/src/main/java/jodd/madvoc/Madvoc.java
index a0c275d38..1b83bc41e 100644
--- a/jodd-madvoc/src/main/java/jodd/madvoc/Madvoc.java
+++ b/jodd-madvoc/src/main/java/jodd/madvoc/Madvoc.java
@@ -25,14 +25,19 @@
package jodd.madvoc;
+import jodd.core.JoddCore;
+import jodd.exception.UncheckedException;
+import jodd.io.findfile.ClassScanner;
import jodd.props.Props;
import jodd.typeconverter.Converter;
import jodd.util.ClassLoaderUtil;
import jodd.util.ClassUtil;
+import jodd.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletContext;
+import java.nio.charset.StandardCharsets;
/**
* Maintain the lifecycle of a Madvoc {@link WebApp}.
@@ -245,13 +250,36 @@ protected Props loadMadvocParams(final String[] patterns) {
log.info("Loading Madvoc parameters from: " + Converter.get().toString(patterns));
}
try {
- return new Props().loadFromClasspath(patterns);
+ final Props props = new Props();
+ loadFromClasspath(props, patterns);
+ return props;
} catch (final Exception ex) {
throw new MadvocException("Unable to load Madvoc parameters from: " +
Converter.get().toString(patterns) + ".properties': " + ex.toString(), ex);
}
}
+ private void loadFromClasspath(final Props props, final String... patterns) {
+ ClassScanner.create()
+ .registerEntryConsumer(entryData -> {
+ String usedEncoding = JoddCore.encoding;
+ if (StringUtil.endsWithIgnoreCase(entryData.name(), ".properties")) {
+ usedEncoding = StandardCharsets.ISO_8859_1.name();
+ }
+
+ final String encoding = usedEncoding;
+ UncheckedException.runAndWrapException(() -> props.load(entryData.openInputStream(), encoding));
+ })
+ .includeResources(true)
+ .ignoreException(true)
+ .excludeCommonJars()
+ .excludeAllEntries(true)
+ .includeEntries(patterns)
+ .scanDefaultClasspath()
+ .start();
+ }
+
+
/**
* Loads Madvoc component that will be used for configuring the user actions.
diff --git a/jodd-madvoc/src/main/java/jodd/madvoc/component/FileUploader.java b/jodd-madvoc/src/main/java/jodd/madvoc/component/FileUploader.java
index cdb1a5d8a..cc823790a 100644
--- a/jodd-madvoc/src/main/java/jodd/madvoc/component/FileUploader.java
+++ b/jodd-madvoc/src/main/java/jodd/madvoc/component/FileUploader.java
@@ -25,8 +25,9 @@
package jodd.madvoc.component;
-import jodd.io.upload.FileUploadFactory;
-import jodd.io.upload.impl.AdaptiveFileUploadFactory;
+
+import jodd.http.upload.FileUploadFactory;
+import jodd.http.upload.impl.AdaptiveFileUploadFactory;
import java.util.function.Supplier;
diff --git a/jodd-madvoc/src/main/java/jodd/madvoc/scope/RequestScope.java b/jodd-madvoc/src/main/java/jodd/madvoc/scope/RequestScope.java
index bd69f05e3..4b660eeac 100644
--- a/jodd-madvoc/src/main/java/jodd/madvoc/scope/RequestScope.java
+++ b/jodd-madvoc/src/main/java/jodd/madvoc/scope/RequestScope.java
@@ -25,7 +25,7 @@
package jodd.madvoc.scope;
-import jodd.io.upload.FileUpload;
+import jodd.http.upload.FileUpload;
import jodd.madvoc.ActionRequest;
import jodd.madvoc.component.MadvocEncoding;
import jodd.madvoc.config.Targets;
diff --git a/jodd-madvoc/src/test/java/jodd/madvoc/action/mv/UploadAction.java b/jodd-madvoc/src/test/java/jodd/madvoc/action/mv/UploadAction.java
index 08d80d865..f18ebf4ea 100644
--- a/jodd-madvoc/src/test/java/jodd/madvoc/action/mv/UploadAction.java
+++ b/jodd-madvoc/src/test/java/jodd/madvoc/action/mv/UploadAction.java
@@ -25,7 +25,7 @@
package jodd.madvoc.action.mv;
-import jodd.io.upload.FileUpload;
+import jodd.http.upload.FileUpload;
import jodd.madvoc.meta.Action;
import jodd.madvoc.meta.In;
import jodd.madvoc.meta.MadvocAction;
@@ -45,4 +45,4 @@ public String execute() {
return "move:/mv/user.importList.html" ;
}
-}
\ No newline at end of file
+}
diff --git a/jodd-madvoc/src/test/java/jodd/madvoc/action/mv/UserAction.java b/jodd-madvoc/src/test/java/jodd/madvoc/action/mv/UserAction.java
index 9817ef5d3..6389a0642 100644
--- a/jodd-madvoc/src/test/java/jodd/madvoc/action/mv/UserAction.java
+++ b/jodd-madvoc/src/test/java/jodd/madvoc/action/mv/UserAction.java
@@ -25,7 +25,7 @@
package jodd.madvoc.action.mv;
-import jodd.io.upload.FileUpload;
+import jodd.http.upload.FileUpload;
import jodd.madvoc.meta.Action;
import jodd.madvoc.meta.In;
import jodd.madvoc.meta.MadvocAction;
@@ -48,18 +48,18 @@ public class UserAction {
public String importList() throws IOException {
stuff = "";
- for (FileUpload uploadFile : uploadFiles) {
+ for (final FileUpload uploadFile : uploadFiles) {
stuff += uploadFile.getFileContent().length;
stuff += uploadFile.getSize();
stuff += uploadFile.getHeader().getFileName();
stuff += " ";
}
- for (String uploadFileName : uploadFileNames) {
+ for (final String uploadFileName : uploadFileNames) {
stuff += uploadFileName;
stuff += " ";
}
return "ok";
}
-}
\ No newline at end of file
+}
diff --git a/jodd-petite/build.gradle b/jodd-petite/build.gradle
index ec0353d1f..b0dfd5e08 100644
--- a/jodd-petite/build.gradle
+++ b/jodd-petite/build.gradle
@@ -4,11 +4,11 @@ ext.moduleDescription = 'Jodd Petite is slick and lightweight DI container that
dependencies {
api project(':jodd-core')
- api project(':jodd-props')
api project(':jodd-proxetta')
api project(':jodd-servlet'), optional
api 'org.slf4j:slf4j-api:1.7.30'
+ api 'org.jodd:jodd-props:6.0.1'
provided lib.servlet, optional
diff --git a/jodd-props b/jodd-props
new file mode 160000
index 000000000..7f34caeb0
--- /dev/null
+++ b/jodd-props
@@ -0,0 +1 @@
+Subproject commit 7f34caeb0bb2573da01edf5193813484c6729c0c
diff --git a/jodd-props/build.gradle b/jodd-props/build.gradle
deleted file mode 100644
index 45c9ce667..000000000
--- a/jodd-props/build.gradle
+++ /dev/null
@@ -1,10 +0,0 @@
-
-ext.moduleName = 'Jodd Props'
-ext.moduleDescription = 'Jodd Props is super properties replacement, featuring: UTF8, sections, profiles, macros and more.'
-
-dependencies {
- api project(':jodd-core')
-
- testImplementation project(':jodd-core').sourceSets.test.output
- testImplementation lib.junit5
-}
diff --git a/jodd-props/src/main/java/jodd/props/PropertiesToProps.java b/jodd-props/src/main/java/jodd/props/PropertiesToProps.java
deleted file mode 100644
index 5abf24944..000000000
--- a/jodd-props/src/main/java/jodd/props/PropertiesToProps.java
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.props;
-
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.Map;
-import java.util.Properties;
-
-/**
- * Converter for Java Properties to Jodd Props format.
- */
-class PropertiesToProps {
-
- /**
- * Convert Java Properties to Jodd Props format
- *
- * @param writer Writer to write Props formatted file content to
- * @param properties Properties to convert to Props format
- * @param profiles Properties per profile to convert and add to the Props format
- * @throws IOException On any I/O error when writing to the writer
- */
- void convertToWriter(final Writer writer, final Properties properties, final Map profiles)
- throws IOException {
- final BufferedWriter bw = getBufferedWriter(writer);
- writeBaseAndProfileProperties(bw, properties, profiles);
- writeProfilePropertiesThatAreNotInTheBase(bw, properties, profiles);
- bw.flush();
- }
-
- private void writeProfilePropertiesThatAreNotInTheBase(final BufferedWriter bw, final Properties baseProperties,
- final Map profiles) throws IOException {
-
- for (final Map.Entry entry : profiles.entrySet()) {
- final String profileName = entry.getKey();
- final Properties profileProperties = entry.getValue();
-
- for (final Object key : profileProperties.keySet()) {
- if (baseProperties.containsKey(key)) {
- continue;
- }
-
- final String keyString = key.toString();
- writeProfileProperty(bw, profileName, keyString, profileProperties.getProperty(keyString));
- }
- }
- }
-
- private BufferedWriter getBufferedWriter(final Writer writer) {
- final BufferedWriter bw;
- if (writer instanceof BufferedWriter) {
- bw = (BufferedWriter) writer;
- } else {
- bw = new BufferedWriter(writer);
- }
- return bw;
- }
-
- private void writeBaseAndProfileProperties(final BufferedWriter bw, final Properties baseProperties,
- final Map profiles) throws IOException {
- for (final Object key : baseProperties.keySet()) {
- final String keyString = key.toString();
-
- final String value = baseProperties.getProperty(keyString);
- writeBaseProperty(bw, keyString, value);
- writeProfilePropertiesOfKey(bw, keyString, profiles);
- }
- }
-
- private void writeProfilePropertiesOfKey(final BufferedWriter bw, final String key,
- final Map profiles) throws IOException {
- for (final Map.Entry entry : profiles.entrySet()) {
- final Properties profileProperties = entry.getValue();
- if (!profileProperties.containsKey(key)) {
- continue;
- }
- final String profileName = entry.getKey();
- writeProfileProperty(bw, profileName, key, profileProperties.getProperty(key));
- }
- }
-
- private void writeProfileProperty(final BufferedWriter bw, final String profileName,
- final String key, final String value)
- throws IOException {
- bw.write(key + '<' + profileName + '>' + '=' + value);
- bw.newLine();
- }
-
- private void writeBaseProperty(final BufferedWriter bw, final String key, final String value)
- throws IOException {
- bw.write(key + '=' + value);
- bw.newLine();
- }
-}
\ No newline at end of file
diff --git a/jodd-props/src/main/java/jodd/props/Props.java b/jodd-props/src/main/java/jodd/props/Props.java
deleted file mode 100644
index c3d4c1ca1..000000000
--- a/jodd-props/src/main/java/jodd/props/Props.java
+++ /dev/null
@@ -1,754 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.props;
-
-import jodd.core.JoddCore;
-import jodd.exception.UncheckedException;
-import jodd.io.FastCharArrayWriter;
-import jodd.io.FileNameUtil;
-import jodd.io.FileUtil;
-import jodd.io.IOUtil;
-import jodd.io.findfile.ClassScanner;
-import jodd.util.StringPool;
-import jodd.util.StringUtil;
-import jodd.util.Wildcard;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Writer;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Properties;
-
-
-/**
- * Super properties: fast, configurable, supports (ini) sections, profiles.
- *
- * Basic parsing rules:
- *
- * By default, props files are UTF8 encoded.
- * Leading and trailing spaces will be trimmed from section names and property names.
- * Leading and/or trailing spaces may be trimmed from property values.
- * You can use either equal sign (=) or colon (:) to assign property values
- * Comments begin with either a semicolon (;), or a sharp sign (#) and extend to the end of line. It doesn't have to be the first character.
- * A backslash (\) escapes the next character (e.g., \# is a literal #, \\ is a literal \).
- * If the last character of a line is backslash (\), the value is continued on the next line with new line character included.
- * \\uXXXX is encoded as character
- * \t, \r and \f are encoded as characters
- *
- *
- * Sections rules:
- *
- * Section names are enclosed between [ and ].
- * Properties following a section header belong to that section. Section name is added as a prefix to section properties.
- * Section ends with empty section definition [] or with new section start
- *
- *
- * Profiles rules:
- *
- * Profile names are enclosed between < and > in property key.
- * Each property key may contain zero, one or more profile definitions.
- *
- *
- * Macro rules:
- *
- * Profile values may contain references to other properties using ${ and }
- * Inner references are supported
- * References are resolved first in the profile context and then in the base props context.
- *
- */
-public class Props implements Cloneable {
-
- private static final String DEFAULT_PROFILES_PROP = "@profiles";
-
- protected final PropsParser parser;
-
- protected final PropsData data;
-
- protected String activeProfilesProp = DEFAULT_PROFILES_PROP;
-
- protected String[] activeProfiles;
-
- protected volatile boolean initialized;
-
- /**
- * Statis ctor.
- */
- public static Props create() {
- return new Props();
- }
-
-
- /**
- * Creates new props.
- */
- public Props() {
- this(new PropsParser());
- }
-
- protected Props(final PropsParser parser) {
- this.parser = parser;
- this.data = parser.getPropsData();
- }
-
- /**
- * Clones props by creating new instance and copying current configuration.
- */
- @Override
- protected Props clone() {
- final PropsParser parser = this.parser.clone();
- final Props p = new Props(parser);
-
- p.activeProfilesProp = activeProfilesProp;
- return p;
- }
-
- /**
- * Returns active profiles or null
if none defined.
- */
- public String[] getActiveProfiles() {
- initialize();
- return activeProfiles;
- }
-
- // ---------------------------------------------------------------- configuration
-
- /**
- * Sets new active profiles and overrides existing ones.
- * By setting null
, no active profile will be set.
- *
- * Note that if some props are loaded after
- * this method call, they might override active profiles
- * by using special property for active profiles (@profiles
).
- */
- public Props setActiveProfiles(final String... activeProfiles) {
- initialized = false;
- this.activeProfiles = activeProfiles;
- return this;
- }
-
- /**
- * Specifies the new line string when EOL is escaped.
- * Default value is an empty string.
- */
- public Props setEscapeNewLineValue(final String escapeNewLineValue) {
- parser.escapeNewLineValue = escapeNewLineValue;
- return this;
- }
-
- /**
- * Specifies should the values be trimmed from the left.
- * Default is true
.
- */
- public Props setValueTrimLeft(final boolean valueTrimLeft) {
- parser.valueTrimLeft = valueTrimLeft;
- return this;
- }
-
- /**
- * Specifies should the values be trimmed from the right.
- * Default is true
.
- */
- public Props setValueTrimRight(final boolean valueTrimRight) {
- parser.valueTrimRight = valueTrimRight;
- return this;
- }
-
- /**
- * Defines if the prefix whitespaces should be ignored when value is split into the lines.
- */
- public Props setIgnorePrefixWhitespacesOnNewLine(final boolean ignorePrefixWhitespacesOnNewLine) {
- parser.ignorePrefixWhitespacesOnNewLine = ignorePrefixWhitespacesOnNewLine;
- return this;
- }
-
- /**
- * Skips empty properties as they don't exist.
- */
- public Props setSkipEmptyProps(final boolean skipEmptyProps) {
- parser.skipEmptyProps = skipEmptyProps;
- data.skipEmptyProps = skipEmptyProps;
- return this;
- }
-
- /**
- * Appends duplicate props.
- */
- public Props setAppendDuplicateProps(final boolean appendDuplicateProps) {
- data.appendDuplicateProps = appendDuplicateProps;
- return this;
- }
-
- /**
- * Ignore missing macros by replacing them with an empty string.
- */
- public Props setIgnoreMissingMacros(final boolean ignoreMissingMacros) {
- data.ignoreMissingMacros = ignoreMissingMacros;
- return this;
- }
-
- /**
- * Enables multiline values.
- */
- public Props setMultilineValues(final boolean multilineValues) {
- parser.multilineValues = multilineValues;
- return this;
- }
-
- /**
- * Parses input string and loads provided properties map.
- */
- protected synchronized void parse(final String data) {
- initialized = false;
- parser.parse(data);
- }
-
- // ---------------------------------------------------------------- load
-
- /**
- * Loads props from the string.
- */
- public Props load(final String data) {
- parse(data);
- return this;
- }
-
- /**
- * Loads props from the file. Assumes UTF8 encoding unless
- * the file ends with '.properties', than it uses ISO 8859-1.
- */
- public Props load(final File file) throws IOException {
- final String extension = FileNameUtil.getExtension(file.getAbsolutePath());
- final String data;
- if (extension.equalsIgnoreCase("properties")) {
- data = FileUtil.readString(file, StandardCharsets.ISO_8859_1);
- } else {
- data = FileUtil.readString(file);
- }
- parse(data);
- return this;
- }
-
- /**
- * Loads properties from the file in provided encoding.
- */
- public Props load(final File file, final String encoding) throws IOException {
- parse(FileUtil.readString(file, Charset.forName(encoding)));
- return this;
- }
-
- /**
- * Loads properties from input stream. Stream is not closed at the end.
- */
- public Props load(final InputStream in) throws IOException {
- final Writer out = new FastCharArrayWriter();
- IOUtil.copy(in, out);
- parse(out.toString());
- return this;
- }
-
- /**
- * Loads properties from input stream and provided encoding.
- * Stream is not closed at the end.
- */
- public Props load(final InputStream in, final String encoding) throws IOException {
- final Writer out = new FastCharArrayWriter();
- IOUtil.copy(in, out, Charset.forName(encoding));
- parse(out.toString());
- return this;
- }
-
- /**
- * Loads base properties from the provided java properties.
- * Null values are ignored.
- */
- public Props load(final Map, ?> p) {
- for (final Map.Entry, ?> entry : p.entrySet()) {
- final String name = entry.getKey().toString();
- final Object value = entry.getValue();
- if (value == null) {
- continue;
- }
- data.putBaseProperty(name, value.toString(), false);
- }
- return this;
- }
-
- /**
- * Loads base properties from java Map using provided prefix.
- * Null values are ignored.
- */
- @SuppressWarnings("unchecked")
- public Props load(final Map, ?> map, final String prefix) {
- String realPrefix = prefix;
- realPrefix += '.';
- for (final Map.Entry entry : map.entrySet()) {
- final String name = entry.getKey().toString();
- final Object value = entry.getValue();
- if (value == null) {
- continue;
- }
- data.putBaseProperty(realPrefix + name, value.toString(), false);
- }
- return this;
- }
-
- /**
- * Loads system properties with given prefix.
- * If prefix is null
it will not be ignored.
- */
- public Props loadSystemProperties(final String prefix) {
- final Properties environmentProperties = System.getProperties();
- load(environmentProperties, prefix);
- return this;
- }
-
- /**
- * Loads environment properties with given prefix.
- * If prefix is null
it will not be used.
- */
- public Props loadEnvironment(final String prefix) {
- final Map environmentMap = System.getenv();
- load(environmentMap, prefix);
- return this;
- }
-
- /**
- * Loads props and properties from the classpath.
- */
- public Props loadFromClasspath(final String... patterns) {
- ClassScanner.create()
- .registerEntryConsumer(entryData -> {
- String usedEncoding = JoddCore.encoding;
- if (StringUtil.endsWithIgnoreCase(entryData.name(), ".properties")) {
- usedEncoding = StandardCharsets.ISO_8859_1.name();
- }
-
- final String encoding = usedEncoding;
- UncheckedException.runAndWrapException(() -> load(entryData.openInputStream(), encoding));
- })
- .includeResources(true)
- .ignoreException(true)
- .excludeCommonJars()
- .excludeAllEntries(true)
- .includeEntries(patterns)
- .scanDefaultClasspath()
- .start();
- return this;
- }
-
-
- // ---------------------------------------------------------------- props
-
- /**
- * Counts the total number of properties, including all profiles.
- * This operation performs calculation each time and it might be
- * more time consuming then expected.
- */
- public int countTotalProperties() {
- return data.countBaseProperties() + data.countProfileProperties();
- }
-
- /**
- * Returns string
value of base property.
- * Returns null
if property doesn't exist.
- */
- @SuppressWarnings({"NullArgumentToVariableArgMethod"})
- public String getBaseValue(final String key) {
- return getValue(key, StringPool.EMPTY_ARRAY);
- }
-
- /**
- * Returns value of property, using active profiles, or {@code null} if property not found.
- */
- public String getValue(final String key) {
- initialize();
- return data.lookupValue(key, activeProfiles);
- }
-
- /**
- * Returns value of property, using active profiles or default value if not found.
- */
- public String getValueOrDefault(final String key, final String defaultValue) {
- initialize();
- final String value = data.lookupValue(key, activeProfiles);
- if (value == null) {
- return defaultValue;
- }
- return value;
- }
-
- /**
- * Returns integer value of given property or {@code null} if property not found.
- */
- public Integer getIntegerValue(final String key) {
- final String value = getValue(key);
- if (value == null) {
- return null;
- }
- return Integer.valueOf(value);
- }
-
- /**
- * Returns integer value or default one if property not defined.
- */
- public Integer getIntegerValue(final String key, final Integer defaultValue) {
- final String value = getValue(key);
- if (value == null) {
- return defaultValue;
- }
- return Integer.valueOf(value);
- }
-
- /**
- * Returns long value of given property or {@code null} if property not found.
- */
- public Long getLongValue(final String key) {
- final String value = getValue(key);
- if (value == null) {
- return null;
- }
- return Long.valueOf(value);
- }
-
- /**
- * Returns long value or default one if property not defined.
- */
- public Long getLongValue(final String key, final Long defaultValue) {
- final String value = getValue(key);
- if (value == null) {
- return defaultValue;
- }
- return Long.valueOf(value);
- }
-
- /**
- * Returns double value of given property or {@code null} if property not found.
- */
- public Double getDoubleValue(final String key) {
- final String value = getValue(key);
- if (value == null) {
- return null;
- }
- return Double.valueOf(value);
- }
-
- /**
- * Returns double value or default one if property not defined.
- */
- public Double getDoubleValue(final String key, final Double defaultValue) {
- final String value = getValue(key);
- if (value == null) {
- return defaultValue;
- }
- return Double.valueOf(value);
- }
-
- /**
- * Returns boolean value of given property or {@code null} if property not found.
- */
- public Boolean getBooleanValue(final String key) {
- final String value = getValue(key);
- if (value == null) {
- return null;
- }
- return Boolean.valueOf(value);
- }
-
- /**
- * Returns boolean value or default one if property not defined.
- */
- public Boolean getBooleanValue(final String key, final Boolean defaultValue) {
- final String value = getValue(key);
- if (value == null) {
- return defaultValue;
- }
- return Boolean.valueOf(value);
- }
-
- /**
- * Returns string
value of given profiles. If key is not
- * found under listed profiles, base properties will be searched.
- * Returns null
if property doesn't exist.
- */
- public String getValue(final String key, final String... profiles) {
- initialize();
- return data.lookupValue(key, profiles);
- }
-
- public Integer getIntegerValue(final String key, final String... profiles) {
- final String value = getValue(key, profiles);
- if (value == null) {
- return null;
- }
- return Integer.valueOf(value);
- }
- public Integer getIntegerValue(final String key, final Integer defaultValue, final String... profiles) {
- final String value = getValue(key, profiles);
- if (value == null) {
- return defaultValue;
- }
- return Integer.valueOf(value);
- }
- public Long getLongValue(final String key, final String... profiles) {
- final String value = getValue(key, profiles);
- if (value == null) {
- return null;
- }
- return Long.valueOf(value);
- }
- public Long getLongValue(final String key, final Long defaultValue, final String... profiles) {
- final String value = getValue(key, profiles);
- if (value == null) {
- return defaultValue;
- }
- return Long.valueOf(value);
- }
- public Double getDoubleValue(final String key, final String... profiles) {
- final String value = getValue(key, profiles);
- if (value == null) {
- return null;
- }
- return Double.valueOf(value);
- }
- public Double getDoubleValue(final String key, final Double defaultValue, final String... profiles) {
- final String value = getValue(key, profiles);
- if (value == null) {
- return defaultValue;
- }
- return Double.valueOf(value);
- }
- public Boolean getBooleanValue(final String key, final String... profiles) {
- final String value = getValue(key, profiles);
- if (value == null) {
- return null;
- }
- return Boolean.valueOf(value);
- }
- public Boolean getBooleanValue(final String key, final Boolean defaultValue, final String... profiles) {
- final String value = getValue(key, profiles);
- if (value == null) {
- return defaultValue;
- }
- return Boolean.valueOf(value);
- }
-
-
- /**
- * Sets default value.
- */
- public void setValue(final String key, final String value) {
- setValue(key, value, null);
- }
-
- /**
- * Sets value on some profile.
- */
- public void setValue(final String key, final String value, final String profile) {
- if (profile == null) {
- data.putBaseProperty(key, value, false);
- } else {
- data.putProfileProperty(key, value, profile, false);
- }
- initialized = false;
- }
-
- // ---------------------------------------------------------------- extract
-
- /**
- * Extracts props belonging to active profiles.
- */
- public void extractProps(final Map target) {
- initialize();
- data.extract(target, activeProfiles, null, null);
- }
-
- /**
- * Extract props of given profiles.
- */
- public void extractProps(final Map target, final String... profiles) {
- initialize();
- data.extract(target, profiles, null, null);
- }
-
- /**
- * Extracts subset of properties that matches given wildcards.
- */
- public void extractSubProps(final Map target, final String... wildcardPatterns) {
- initialize();
- data.extract(target, activeProfiles, wildcardPatterns, null);
- }
-
- /**
- * Extracts subset of properties that matches given wildcards.
- */
- public void extractSubProps(final Map target, final String[] profiles, final String[] wildcardPatterns) {
- initialize();
- data.extract(target, profiles, wildcardPatterns, null);
- }
-
- // ---------------------------------------------------------------- childMap
-
- /**
- * Returns inner map from the props with given prefix. Keys in returned map
- * will not have the prefix.
- */
- @SuppressWarnings("unchecked")
- public Map innerMap(final String prefix) {
- initialize();
- return data.extract(null, activeProfiles, null, prefix);
- }
-
- /**
- * Adds child map to the props on given prefix.
- */
- public void addInnerMap(final String prefix, final Map, ?> map) {
- addInnerMap(prefix, map, null);
- }
-
- /**
- * Adds child map to the props on given prefix.
- */
- public void addInnerMap(String prefix, final Map, ?> map, final String profile) {
- if (!StringUtil.endsWithChar(prefix, '.')) {
- prefix += StringPool.DOT;
- }
-
- for (final Map.Entry, ?> entry : map.entrySet()) {
- String key = entry.getKey().toString();
-
- key = prefix + key;
-
- setValue(key, entry.getValue().toString(), profile);
- }
- }
-
- // ---------------------------------------------------------------- initialize
-
- /**
- * Initializes props. By default it only resolves active profiles.
- */
- protected void initialize() {
- if (!initialized) {
- synchronized (this) {
- if (!initialized) {
-
- resolveActiveProfiles();
-
- initialized = true;
- }
- }
- }
- }
-
- /**
- * Resolves active profiles from special property.
- * This property can be only a base property!
- * If default active property is not defined, nothing happens.
- * Otherwise, it will replace currently active profiles.
- */
- protected void resolveActiveProfiles() {
- if (activeProfilesProp == null) {
- activeProfiles = null;
- return;
- }
-
- final PropsEntry pv = data.getBaseProperty(activeProfilesProp);
- if (pv == null) {
- // no active profile set as the property, exit
- return;
- }
-
- final String value = pv.getValue();
- if (StringUtil.isBlank(value)) {
- activeProfiles = null;
- return;
- }
-
- activeProfiles = StringUtil.splitc(value, ',');
- StringUtil.trimAll(activeProfiles);
- }
-
- // ---------------------------------------------------------------- iterator
-
- /**
- * Returns all profiles names.
- */
- public String[] getAllProfiles() {
- final String[] profiles = new String[data.profileProperties.size()];
-
- int index = 0;
- for (final String profileName : data.profileProperties.keySet()) {
- profiles[index] = profileName;
- index++;
- }
- return profiles;
- }
-
- /**
- * Returns all the profiles that define certain prop's key name.
- * Key name is given as a wildcard, or it can be matched fully.
- */
- public String[] getProfilesFor(final String propKeyNameWildcard) {
- final HashSet profiles = new HashSet<>();
-
- profile:
- for (final Map.Entry> entries : data.profileProperties.entrySet()) {
- final String profileName = entries.getKey();
-
- final Map value = entries.getValue();
-
- for (final String propKeyName : value.keySet()) {
- if (Wildcard.equalsOrMatch(propKeyName, propKeyNameWildcard)) {
- profiles.add(profileName);
- continue profile;
- }
- }
- }
-
- return profiles.toArray(new String[0]);
- }
-
- /**
- * Returns {@link PropsEntries builder} for entries {@link #iterator() itertor}.
- */
- public PropsEntries entries() {
- initialize();
- return new PropsEntries(this);
- }
-
- /**
- * Returns iterator for active profiles.
- */
- public Iterator iterator() {
- return entries().activeProfiles().iterator();
- }
-
-}
diff --git a/jodd-props/src/main/java/jodd/props/PropsConverter.java b/jodd-props/src/main/java/jodd/props/PropsConverter.java
deleted file mode 100644
index 769ec8edd..000000000
--- a/jodd-props/src/main/java/jodd/props/PropsConverter.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.props;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Properties;
-
-/**
- * Converter of Java Properties to Jodd Props format.
- */
-public class PropsConverter {
-
- /**
- * Convert Java Properties to Jodd Props format.
- *
- * @param writer Writer to write Props formatted file content to
- * @param properties Properties to convert to Props format
- * @throws IOException On any I/O error when writing to the writer
- */
- public static void convert(final Writer writer, final Properties properties) throws IOException {
- convert(writer, properties, Collections.emptyMap());
- }
-
- /**
- * Convert Java Properties to Jodd Props format.
- *
- * @param writer Writer to write Props formatted file content to
- * @param properties Properties to convert to Props format
- * @param profiles Properties per profile to convert and add to the Props format
- * @throws IOException On any I/O error when writing to the writer
- */
- public static void convert(final Writer writer, final Properties properties, final Map profiles)
- throws IOException {
-
- final PropertiesToProps toProps = new PropertiesToProps();
- toProps.convertToWriter(writer, properties, profiles);
- }
-
-}
diff --git a/jodd-props/src/main/java/jodd/props/PropsData.java b/jodd-props/src/main/java/jodd/props/PropsData.java
deleted file mode 100644
index 0385e90bf..000000000
--- a/jodd-props/src/main/java/jodd/props/PropsData.java
+++ /dev/null
@@ -1,356 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.props;
-
-import jodd.util.StringPool;
-import jodd.util.StringTemplateParser;
-import jodd.util.StringUtil;
-import jodd.util.Wildcard;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.function.Function;
-
-/**
- * Props data storage for base and profile properties.
- * Properties can be lookuped and modified only through this
- * class.
- */
-public class PropsData implements Cloneable {
-
- private static final int MAX_INNER_MACROS = 100;
- private static final String APPEND_SEPARATOR = ",";
-
- protected final HashMap baseProperties;
- protected final HashMap> profileProperties;
-
- protected PropsEntry first;
- protected PropsEntry last;
-
- /**
- * If set, duplicate props will be appended to the end, separated by comma.
- */
- protected boolean appendDuplicateProps;
-
- /**
- * When set, missing macros will be replaces with an empty string.
- */
- protected boolean ignoreMissingMacros;
-
- /**
- * When set, empty properties will be skipped.
- */
- protected boolean skipEmptyProps = true;
-
- public PropsData() {
- this(new HashMap<>(), new HashMap<>());
- }
-
- protected PropsData(final HashMap properties, final HashMap> profiles) {
- this.baseProperties = properties;
- this.profileProperties = profiles;
- }
-
- @Override
- public PropsData clone() {
- final HashMap> newProfiles = new HashMap<>();
-
- final HashMap newBase = new HashMap<>(baseProperties);
-
- for (final Map.Entry> entry : profileProperties.entrySet()) {
- final Map map = new HashMap<>(entry.getValue().size());
- map.putAll(entry.getValue());
- newProfiles.put(entry.getKey(), map);
- }
-
- final PropsData pd = new PropsData(newBase, newProfiles);
- pd.appendDuplicateProps = appendDuplicateProps;
- pd.ignoreMissingMacros = ignoreMissingMacros;
- pd.skipEmptyProps = skipEmptyProps;
- return pd;
- }
-
- // ---------------------------------------------------------------- misc
-
- /**
- * Puts key-value pair into the map, with respect of appending duplicate properties
- */
- protected void put(final String profile, final Map map, final String key, final String value, final boolean append) {
- String realValue = value;
- if (append || appendDuplicateProps) {
- final PropsEntry pv = map.get(key);
- if (pv != null) {
- realValue = pv.value + APPEND_SEPARATOR + realValue;
- }
- }
- final PropsEntry propsEntry = new PropsEntry(key, realValue, profile, this);
-
- // update position pointers
- if (first == null) {
- first = propsEntry;
- } else {
- last.next = propsEntry;
- }
- last = propsEntry;
-
- // add to the map
- map.put(key, propsEntry);
- }
-
- // ---------------------------------------------------------------- properties
-
- /**
- * Counts base properties.
- */
- public int countBaseProperties() {
- return baseProperties.size();
- }
-
- /**
- * Adds base property.
- */
- public void putBaseProperty(final String key, final String value, final boolean append) {
- put(null, baseProperties, key, value, append);
- }
-
- /**
- * Returns base property or null
if it doesn't exist.
- */
- public PropsEntry getBaseProperty(final String key) {
- return baseProperties.get(key);
- }
-
- // ---------------------------------------------------------------- profiles
-
- /**
- * Counts profile properties. Note: this method is not
- * that easy on execution.
- */
- public int countProfileProperties() {
- final HashSet profileKeys = new HashSet<>();
-
- for (final Map map : profileProperties.values()) {
- for (final String key : map.keySet()) {
- if (!baseProperties.containsKey(key)) {
- profileKeys.add(key);
- }
- }
- }
- return profileKeys.size();
- }
-
- /**
- * Adds profile property.
- */
- public void putProfileProperty(final String key, final String value, final String profile, final boolean append) {
- final Map map = profileProperties.computeIfAbsent(profile, k -> new HashMap<>());
- put(profile, map, key, value, append);
- }
-
- /**
- * Returns profile property.
- */
- public PropsEntry getProfileProperty(final String profile, final String key) {
- final Map profileMap = profileProperties.get(profile);
- if (profileMap == null) {
- return null;
- }
- return profileMap.get(key);
- }
-
-
- // ---------------------------------------------------------------- lookup
-
- /**
- * Lookup props value through profiles and base properties.
- * Returns {@code null} if value not found.
- */
- protected String lookupValue(final String key, final String... profiles) {
- if (profiles != null) {
- for (String profile : profiles) {
- if (profile == null) {
- continue;
- }
- while (true) {
- final Map profileMap = this.profileProperties.get(profile);
- if (profileMap != null) {
- final PropsEntry value = profileMap.get(key);
-
- if (value != null) {
- return value.getValue(profiles);
- }
- }
-
- // go back with profile
- final int ndx = profile.lastIndexOf('.');
- if (ndx == -1) {
- break;
- }
- profile = profile.substring(0, ndx);
- }
- }
- }
- final PropsEntry value = getBaseProperty(key);
-
- if (value == null) {
- return null;
- }
-
- return value.getValue(profiles);
- }
-
- // ---------------------------------------------------------------- resolve
-
- /**
- * Resolves all macros in this props set. Called on property lookup.
- */
- public String resolveMacros(String value, final String... profiles) {
- // create string template parser that will be used internally
- final Function macroResolver = macroName -> {
- String[] lookupProfiles = profiles;
-
- final int leftIndex = macroName.indexOf('<');
- if (leftIndex != -1) {
- final int rightIndex = macroName.indexOf('>');
-
- final String profiles1 = macroName.substring(leftIndex + 1, rightIndex);
- macroName = macroName.substring(0, leftIndex).concat(macroName.substring(rightIndex + 1));
-
- lookupProfiles = StringUtil.splitc(profiles1, ',');
-
- StringUtil.trimAll(lookupProfiles);
- }
-
- return lookupValue(macroName, lookupProfiles);
- };
-
-
- final StringTemplateParser stringTemplateParser = new StringTemplateParser(macroResolver);
- stringTemplateParser.setResolveEscapes(false);
-
- if (!ignoreMissingMacros) {
- stringTemplateParser.setReplaceMissingKey(false);
- } else {
- stringTemplateParser.setReplaceMissingKey(true);
- stringTemplateParser.setMissingKeyReplacement(StringPool.EMPTY);
- }
-
- // start parsing
- int loopCount = 0;
-
- while (loopCount++ < MAX_INNER_MACROS) {
- final String newValue = stringTemplateParser.apply(value);
-
- if (newValue.equals(value)) {
- break;
- }
-
- if (skipEmptyProps) {
- if (newValue.length() == 0) {
- return null;
- }
- }
-
- value = newValue;
- }
-
- return value;
- }
-
- // ---------------------------------------------------------------- extract
-
- /**
- * Extracts props to target map. This is all-in-one method, that does many things at once.
- */
- public Map extract(Map target, final String[] profiles, final String[] wildcardPatterns, String prefix) {
- if (target == null) {
- target = new HashMap();
- }
-
- // make sure prefix ends with a dot
- if (prefix != null) {
- if (!StringUtil.endsWithChar(prefix, '.')) {
- prefix += StringPool.DOT;
- }
- }
-
- if (profiles != null) {
- for (String profile : profiles) {
- while (true) {
- final Map map = this.profileProperties.get(profile);
- if (map != null) {
- extractMap(target, map, profiles, wildcardPatterns, prefix);
- }
-
- final int ndx = profile.lastIndexOf('.');
- if (ndx == -1) {
- break;
- }
- profile = profile.substring(0, ndx);
- }
- }
- }
-
- extractMap(target, this.baseProperties, profiles, wildcardPatterns, prefix);
-
- return target;
- }
-
- @SuppressWarnings("unchecked")
- protected void extractMap(
- final Map target,
- final Map map,
- final String[] profiles,
- final String[] wildcardPatterns,
- final String prefix
- ) {
-
- for (final Map.Entry entry : map.entrySet()) {
- String key = entry.getKey();
-
- if (wildcardPatterns != null) {
- if (Wildcard.matchOne(key, wildcardPatterns) == -1) {
- continue;
- }
- }
-
- // shorten the key
- if (prefix != null) {
- if (!key.startsWith(prefix)) {
- continue;
- }
- key = key.substring(prefix.length());
- }
-
- // only append if target DOES NOT contain the key
- if (!target.containsKey(key)) {
- target.put(key, entry.getValue().getValue(profiles));
- }
- }
- }
-
-}
diff --git a/jodd-props/src/main/java/jodd/props/PropsEntries.java b/jodd-props/src/main/java/jodd/props/PropsEntries.java
deleted file mode 100644
index 66c7af086..000000000
--- a/jodd-props/src/main/java/jodd/props/PropsEntries.java
+++ /dev/null
@@ -1,287 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.props;
-
-import jodd.util.CollectionUtil;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.function.Consumer;
-
-/**
- * Props iterator builder. Should be used with: {@link jodd.props.Props#entries()}.
- */
-public final class PropsEntries {
-
- private final PropsIterator propsIterator;
- private final Props props;
-
- public PropsEntries(final Props props) {
- this.props = props;
- this.propsIterator = new PropsIterator();
- }
-
- /**
- * Enables profile to iterate.
- */
- public PropsEntries profile(final String profile) {
- addProfiles(profile);
- return this;
- }
- /**
- * Enables profiles to iterate.
- */
- public PropsEntries profile(final String... profiles) {
- if (profiles == null) {
- return this;
- }
- for (final String profile : profiles) {
- addProfiles(profile);
- }
- return this;
- }
-
- /**
- * Enables active profiles to iterate over.
- */
- public PropsEntries activeProfiles() {
- profile(props.activeProfiles);
- return this;
- }
-
- private void addProfiles(final String profile) {
- if (propsIterator.profiles == null) {
- propsIterator.profiles = new ArrayList<>();
- }
- propsIterator.profiles.add(profile);
- }
-
- /**
- * Enables section to iterate.
- */
- public PropsEntries section(final String section) {
- addSection(section);
- return this;
- }
- /**
- * Enables sections to iterate.
- */
- public PropsEntries section(final String... section) {
- for (final String s : section) {
- addSection(s);
- }
- return this;
- }
-
- private void addSection(final String section) {
- if (propsIterator.sections == null) {
- propsIterator.sections = new ArrayList<>();
- }
- propsIterator.sections.add(section + '.');
- }
-
- /**
- * Skips duplicate keys (defined in different profiles) which value is not
- * used for setting current key value.
- */
- public PropsEntries skipDuplicatesByValue() {
- propsIterator.skipDuplicatesByValue = true;
- propsIterator.skipDuplicatesByPosition = false;
- return this;
- }
-
- /**
- * Skips all keys after first definition, even if value is set later.
- */
- public PropsEntries skipDuplicatesByPosition() {
- propsIterator.skipDuplicatesByPosition = true;
- propsIterator.skipDuplicatesByValue = false;
- return this;
- }
-
- /**
- * Returns populated iterator.
- */
- public Iterator iterator() {
- return propsIterator;
- }
-
- /**
- * Consumer all properties.
- */
- public void forEach(final Consumer propsDataConsumer) {
- CollectionUtil.streamOf(propsIterator).forEach(propsDataConsumer);
- }
-
- // ---------------------------------------------------------------- iterator
-
- /**
- * Props iterator.
- */
- private class PropsIterator implements Iterator {
- private PropsEntry next = props.data.first;
- private boolean firstTime = true;
- private List profiles;
- private List sections;
- private boolean skipDuplicatesByValue;
- private boolean skipDuplicatesByPosition;
- private Set keys;
-
- @Override
- public boolean hasNext() {
- if (firstTime) {
- start();
- }
- return next != null;
- }
-
- /**
- * Starts with the iterator.
- */
- private void start() {
- firstTime = false;
-
- while (!accept(next)) {
- if (next == null) {
- break;
- }
- next = next.next; // funny :)))
- }
- }
-
- /**
- * Accepts an entry and returns true
- * if entry should appear in this iteration.
- */
- private boolean accept(final PropsEntry entry) {
- if (entry == null) {
- return false;
- }
- if (profiles != null) {
- if (entry.getProfile() != null) {
- boolean found = false;
- for (final String profile : profiles) {
- if (entry.getProfile().equals(profile)) {
- found = true;
- break;
- }
- }
- if (!found) {
- return false;
- }
- }
- } else {
- // ignore all profile keys if only a base profile is active
- if (entry.getProfile() != null) {
- return false;
- }
- }
-
- if (sections != null) {
- boolean found = false;
- for (final String section : sections) {
- if (entry.getKey().startsWith(section)) {
- found = true;
- break;
- }
- }
- if (!found) {
- return false;
- }
- }
-
- if (profiles != null) {
- if (skipDuplicatesByValue) {
- final String thisProfile = entry.getProfile();
-
- // iterate all profiles before this one
- for (final String profile : profiles) {
- if (profile.equals(thisProfile)) {
- // if we came to this point, there is no
- // property defined in higher profile
- // therefore this one is the most important
- return true;
- }
-
- // check if key exist in higher profile
- final Map profileMap = props.data.profileProperties.get(profile);
- if (profileMap == null) {
- continue;
- }
- if (profileMap.containsKey(entry.getKey())) {
- // duplicate key exist in higher profile, therefore this one is less important
- return false;
- }
- }
- }
- if (skipDuplicatesByPosition) {
- if (keys == null) {
- keys = new HashSet<>();
- }
- if (!keys.add(entry.getKey())) {
- return false; // the key was already there
- }
- }
- }
-
- return true;
- }
-
- @Override
- public PropsEntry next() {
- if (firstTime) {
- start();
- }
-
- if (next == null) {
- throw new NoSuchElementException();
- }
-
- final PropsEntry returnValue = next;
-
- while (next != null) {
- next = next.next;
-
- if (accept(next)) {
- break;
- }
- }
-
- return returnValue;
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- }
-
-}
diff --git a/jodd-props/src/main/java/jodd/props/PropsEntry.java b/jodd-props/src/main/java/jodd/props/PropsEntry.java
deleted file mode 100644
index bdc0fd651..000000000
--- a/jodd-props/src/main/java/jodd/props/PropsEntry.java
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.props;
-
-/**
- * Holds props value.
- */
-public class PropsEntry {
-
- /**
- * Original value.
- */
- protected final String value;
-
- protected PropsEntry next;
-
- protected final String key;
-
- protected final String profile;
-
- protected final boolean hasMacro;
-
- protected final PropsData propsData;
-
- public PropsEntry(final String key, final String value, final String profile, final PropsData propsData) {
- this.value = value;
- this.key = key;
- this.profile = profile;
- this.hasMacro = value.contains("${");
- this.propsData = propsData;
- }
-
- /**
- * Returns the raw value. Macros are not replaced.
- */
- public String getValue() {
- return value;
- }
-
- /**
- * Returns the property value, with replaced macros.
- */
- public String getValue(final String... profiles) {
- if (hasMacro) {
- return propsData.resolveMacros(value, profiles);
- }
- return value;
- }
-
- /**
- * Returns property key.
- */
- public String getKey() {
- return key;
- }
-
- /**
- * Returns property profile or null
if this is a base property.
- */
- public String getProfile() {
- return profile;
- }
-
- /**
- * Returns true
if value has a macro to resolve.
- */
- public boolean hasMacro() {
- return hasMacro;
- }
-
- @Override
- public String toString() {
- return "PropsEntry{" + key + (profile != null ? '<' + profile + '>' : "") + '=' + value + '}';
- }
-
-}
\ No newline at end of file
diff --git a/jodd-props/src/main/java/jodd/props/PropsParser.java b/jodd-props/src/main/java/jodd/props/PropsParser.java
deleted file mode 100644
index f598f9466..000000000
--- a/jodd-props/src/main/java/jodd/props/PropsParser.java
+++ /dev/null
@@ -1,516 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.props;
-
-import jodd.util.CharUtil;
-import jodd.util.StringPool;
-import jodd.util.StringUtil;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * {@link Props} parser.
- */
-public class PropsParser implements Cloneable {
-
- protected static final String PROFILE_LEFT = "<";
-
- protected static final String PROFILE_RIGHT = ">";
-
- protected final PropsData propsData;
-
- /**
- * Value that will be inserted when escaping the new line.
- */
- protected String escapeNewLineValue = StringPool.EMPTY;
-
- /**
- * Trims left the value.
- */
- protected boolean valueTrimLeft = true;
-
- /**
- * Trims right the value.
- */
- protected boolean valueTrimRight = true;
-
- /**
- * Defines if starting whitespaces when value is split in the new line
- * should be ignored or not.
- */
- protected boolean ignorePrefixWhitespacesOnNewLine = true;
-
- /**
- * Defines if multi-line values may be written using triple-quotes
- * as in python.
- */
- protected boolean multilineValues = true;
-
- /**
- * Don't include empty properties.
- */
- protected boolean skipEmptyProps = true;
-
- public PropsParser() {
- this.propsData = new PropsData();
- }
-
- public PropsParser(final PropsData propsData) {
- this.propsData = propsData;
- }
-
- public PropsData getPropsData() {
- return propsData;
- }
-
- @Override
- public PropsParser clone() {
- final PropsParser pp = new PropsParser(this.propsData.clone());
-
- pp.escapeNewLineValue = escapeNewLineValue;
- pp.valueTrimLeft = valueTrimLeft;
- pp.valueTrimRight = valueTrimRight;
- pp.ignorePrefixWhitespacesOnNewLine = ignorePrefixWhitespacesOnNewLine;
- pp.skipEmptyProps = skipEmptyProps;
- pp.multilineValues = multilineValues;
-
- return pp;
- }
-
- /**
- * Parsing states.
- */
- protected enum ParseState {
- TEXT,
- ESCAPE,
- ESCAPE_NEWLINE,
- COMMENT,
- VALUE
- }
-
- /**
- * Different assignment operators.
- */
- protected enum Operator {
- ASSIGN,
- QUICK_APPEND,
- COPY
- }
-
- /**
- * Loads properties.
- */
- public void parse(final String in) {
- ParseState state = ParseState.TEXT;
- ParseState stateOnEscape = null;
-
- boolean insideSection = false;
- String currentSection = null;
- String key = null;
- Operator operator = Operator.ASSIGN;
- final StringBuilder sb = new StringBuilder();
-
- final int len = in.length();
- int ndx = 0;
- while (ndx < len) {
- final char c = in.charAt(ndx);
- ndx++;
-
- if (state == ParseState.COMMENT) {
- // comment, skip to the end of the line
- if (c == '\r') {
- if ((ndx < len) && (in.charAt(ndx) == '\n')) {
- ndx++;
- }
- state = ParseState.TEXT;
- }
- else if (c == '\n') {
- state = ParseState.TEXT;
- }
- } else if (state == ParseState.ESCAPE) {
- state = stateOnEscape; //ParseState.VALUE;
- switch (c) {
- case '\r':
- if ((ndx < len) && (in.charAt(ndx) == '\n')) {
- ndx++;
- }
- case '\n':
- // need to go 1 step back in order to escape
- // the current line ending in the follow-up state
- ndx--;
- state = ParseState.ESCAPE_NEWLINE;
- break;
- // encode UTF character
- case 'u':
- int value = 0;
-
- for (int i = 0; i < 4; i++) {
- final char hexChar = in.charAt(ndx++);
- if (CharUtil.isDigit(hexChar)) {
- value = (value << 4) + hexChar - '0';
- } else if (hexChar >= 'a' && hexChar <= 'f') {
- value = (value << 4) + 10 + hexChar - 'a';
- } else if (hexChar >= 'A' && hexChar <= 'F') {
- value = (value << 4) + 10 + hexChar - 'A';
- } else {
- throw new IllegalArgumentException("Malformed \\uXXXX encoding.");
- }
- }
- sb.append((char) value);
- break;
- case 't':
- sb.append('\t');
- break;
- case 'n':
- sb.append('\n');
- break;
- case 'r':
- sb.append('\r');
- break;
- case 'f':
- sb.append('\f');
- break;
- default:
- sb.append(c);
- }
- } else if (state == ParseState.TEXT) {
- switch (c) {
- case '\\':
- // escape char, take the next char as is
- stateOnEscape = state;
- state = ParseState.ESCAPE;
- break;
-
- // start section
- case '[':
- if (sb.length() > 0) {
- if (StringUtil.isNotBlank(sb)) {
- sb.append(c);
- // previous string is not blank, hence it's not the section
- break;
- }
- }
- sb.setLength(0);
- insideSection = true;
- break;
-
- // end section
- case ']':
- if (insideSection) {
- currentSection = sb.toString().trim();
- sb.setLength(0);
- insideSection = false;
- if (currentSection.length() == 0) {
- currentSection = null;
- }
- } else {
- sb.append(c);
- }
- break;
-
- case '#':
- case ';':
- state = ParseState.COMMENT;
- break;
-
- // copy operator
- case '<':
- if (ndx == len || in.charAt(ndx) != '=') {
- sb.append(c);
- break;
- }
- operator = Operator.COPY;
- //ndx++;
- continue;
-
- // assignment operator
- case '+':
- if (ndx == len || in.charAt(ndx) != '=') {
- sb.append(c);
- break;
- }
- operator = Operator.QUICK_APPEND;
- //ndx++;
- continue;
- case '=':
- case ':':
- if (key == null) {
- key = sb.toString().trim();
- sb.setLength(0);
- } else {
- sb.append(c);
- }
- state = ParseState.VALUE;
- break;
-
- case '\r':
- case '\n':
- add(currentSection, key, sb, true, operator);
- sb.setLength(0);
- key = null;
- operator = Operator.ASSIGN;
- break;
-
- case ' ':
- case '\t':
- // ignore whitespaces
- break;
- default:
- sb.append(c);
- }
- } else {
- switch (c) {
- case '\\':
- // escape char, take the next char as is
- stateOnEscape = state;
- state = ParseState.ESCAPE;
- break;
-
- case '\r':
- if ((ndx < len) && (in.charAt(ndx) == '\n')) {
- ndx++;
- }
- case '\n':
- if (state == ParseState.ESCAPE_NEWLINE) {
- sb.append(escapeNewLineValue);
- if (!ignorePrefixWhitespacesOnNewLine) {
- state = ParseState.VALUE;
- }
- } else {
- add(currentSection, key, sb, true, operator);
- sb.setLength(0);
- key = null;
- operator = Operator.ASSIGN;
-
- // end of value, continue to text
- state = ParseState.TEXT;
- }
- break;
-
- case ' ':
- case '\t':
- if (state == ParseState.ESCAPE_NEWLINE) {
- break;
- }
- default:
- sb.append(c);
- state = ParseState.VALUE;
-
- if (multilineValues) {
- if (sb.length() == 3) {
-
- // check for ''' beginning
- if (sb.toString().equals("'''")) {
- sb.setLength(0);
- int endIndex = in.indexOf("'''", ndx);
- if (endIndex == -1) {
- endIndex = in.length();
- }
- sb.append(in, ndx, endIndex);
-
- // append
- add(currentSection, key, sb, false, operator);
- sb.setLength(0);
- key = null;
- operator = Operator.ASSIGN;
-
- // end of value, continue to text
- state = ParseState.TEXT;
- ndx = endIndex + 3;
- }
- }
- }
- }
- }
- }
-
- if (key != null) {
- add(currentSection, key, sb, true, operator);
- }
- }
-
- // ---------------------------------------------------------------- add
-
- /**
- * Adds accumulated value to key and current section.
- */
- protected void add(
- final String section, final String key,
- final StringBuilder value, final boolean trim, final Operator operator) {
-
- // ignore lines without : or =
- if (key == null) {
- return;
- }
- String fullKey = key;
-
- if (section != null) {
- if (fullKey.length() != 0) {
- fullKey = section + '.' + fullKey;
- } else {
- fullKey = section;
- }
- }
- String v = value.toString();
-
- if (trim) {
- if (valueTrimLeft && valueTrimRight) {
- v = v.trim();
- } else if (valueTrimLeft) {
- v = StringUtil.trimLeft(v);
- } else {
- v = StringUtil.trimRight(v);
- }
- }
-
- if (v.length() == 0 && skipEmptyProps) {
- return;
- }
-
- extractProfilesAndAdd(fullKey, v, operator);
- }
-
- /**
- * Extracts profiles from the key name and adds key-value to them.
- */
- protected void extractProfilesAndAdd(final String key, final String value, final Operator operator) {
- String fullKey = key;
- int ndx = fullKey.indexOf(PROFILE_LEFT);
- if (ndx == -1) {
- justAdd(fullKey, value, null, operator);
- return;
- }
-
- // extract profiles
- final ArrayList keyProfiles = new ArrayList<>();
-
- while (true) {
- ndx = fullKey.indexOf(PROFILE_LEFT);
- if (ndx == -1) {
- break;
- }
-
- final int len = fullKey.length();
-
- int ndx2 = fullKey.indexOf(PROFILE_RIGHT, ndx + 1);
- if (ndx2 == -1) {
- ndx2 = len;
- }
-
- // remember profile
- final String profile = fullKey.substring(ndx + 1, ndx2);
- keyProfiles.add(profile);
-
- // extract profile from key
- ndx2++;
- final String right = (ndx2 == len) ? StringPool.EMPTY : fullKey.substring(ndx2);
- fullKey = fullKey.substring(0, ndx) + right;
- }
-
- if (fullKey.startsWith(StringPool.DOT)) {
- // check for special case when only profile is defined in section
- fullKey = fullKey.substring(1);
- }
-
- // add value to extracted profiles
- justAdd(fullKey, value, keyProfiles, operator);
- }
-
- /**
- * Core key-value addition.
- */
- protected void justAdd(final String key, final String value, final ArrayList keyProfiles, final Operator operator) {
- if (operator == Operator.COPY) {
- final HashMap target = new HashMap<>();
-
- String[] profiles = null;
- if (keyProfiles != null) {
- profiles = keyProfiles.toArray(new String[0]);
- }
-
- final String[] sources = StringUtil.splitc(value, ',');
- for (String source : sources) {
- source = source.trim();
-
- // try to extract profile for parsing
-
- String[] lookupProfiles = profiles;
- String lookupProfilesString = null;
-
- final int leftIndex = source.indexOf('<');
- if (leftIndex != -1) {
- final int rightIndex = source.indexOf('>');
-
- lookupProfilesString = source.substring(leftIndex + 1, rightIndex);
- source = source.substring(0, leftIndex).concat(source.substring(rightIndex + 1));
-
- lookupProfiles = StringUtil.splitc(lookupProfilesString, ',');
-
- StringUtil.trimAll(lookupProfiles);
- }
-
- final String[] wildcards = new String[] {source + ".*"};
-
- propsData.extract(target, lookupProfiles, wildcards, null);
-
- for (final Map.Entry entry : target.entrySet()) {
- final String entryKey = entry.getKey();
- final String suffix = entryKey.substring(source.length());
-
- final String newKey = key + suffix;
-
- String newValue = "${" + entryKey;
- if (lookupProfilesString != null) {
- newValue += "<" + lookupProfilesString + ">";
- }
- newValue += "}";
-
- if (profiles == null) {
- propsData.putBaseProperty(newKey, newValue, false);
- } else {
- for (final String p : profiles) {
- propsData.putProfileProperty(newKey, newValue, p, false);
- }
- }
- }
- }
- return;
- }
-
- final boolean append = operator == Operator.QUICK_APPEND;
- if (keyProfiles == null) {
- propsData.putBaseProperty(key, value, append);
- return;
- }
- for (final String p : keyProfiles) {
- propsData.putProfileProperty(key, value, p, append);
- }
-
- }
-
-}
diff --git a/jodd-props/src/main/java/jodd/props/package-info.java b/jodd-props/src/main/java/jodd/props/package-info.java
deleted file mode 100644
index 0eb879737..000000000
--- a/jodd-props/src/main/java/jodd/props/package-info.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-/**
- * Super properties.
- */
-package jodd.props;
\ No newline at end of file
diff --git a/jodd-props/src/main/resources/META-INF/LICENSE b/jodd-props/src/main/resources/META-INF/LICENSE
deleted file mode 100644
index a040a3b95..000000000
--- a/jodd-props/src/main/resources/META-INF/LICENSE
+++ /dev/null
@@ -1,24 +0,0 @@
-Copyright (c) 2003-present, Jodd Team (https://jodd.org)
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice,
-this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright
-notice, this list of conditions and the following disclaimer in the
-documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
diff --git a/jodd-props/src/test/java/jodd/props/BasePropsTest.java b/jodd-props/src/test/java/jodd/props/BasePropsTest.java
deleted file mode 100644
index a44ece890..000000000
--- a/jodd-props/src/test/java/jodd/props/BasePropsTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.props;
-
-import jodd.io.FastCharArrayWriter;
-import jodd.io.IOUtil;
-import jodd.util.ResourcesUtil;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Writer;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.charset.Charset;
-
-abstract class BasePropsTest {
-
- protected InputStream readDataToInputstream(final String fileName) throws IOException {
- String dataFolder = this.getClass().getPackage().getName() + ".data.";
- dataFolder = dataFolder.replace('.', '/');
- return ResourcesUtil.getResourceAsStream(dataFolder + fileName);
- }
-
- protected File readDataToFile(final String fileName) throws URISyntaxException {
- String dataFolder = this.getClass().getPackage().getName() + ".data.";
- dataFolder = dataFolder.replace('.', '/');
-
- final URL url = ResourcesUtil.getResourceUrl("/" + dataFolder + fileName);
- return new File(url.toURI());
- }
-
- protected String readDataFile(final String fileName) throws IOException {
- String dataFolder = this.getClass().getPackage().getName() + ".data.";
- dataFolder = dataFolder.replace('.', '/');
-
- final InputStream is = ResourcesUtil.getResourceAsStream(dataFolder + fileName);
- final Writer out = new FastCharArrayWriter();
- String encoding = "UTF-8";
- if (fileName.endsWith(".properties")) {
- encoding = "ISO-8859-1";
- }
- IOUtil.copy(is, out, Charset.forName(encoding));
- IOUtil.close(is);
- return out.toString();
- }
-
- protected Props loadProps(final Props p, final String fileName) throws IOException {
- String dataFolder = this.getClass().getPackage().getName() + ".data.";
- dataFolder = dataFolder.replace('.', '/');
-
- final InputStream is = ResourcesUtil.getResourceAsStream(dataFolder + fileName);
- String encoding = "UTF-8";
- if (fileName.endsWith(".properties")) {
- encoding = "ISO-8859-1";
- }
- p.load(is, encoding);
- return p;
- }
-
- protected Props loadProps(final String fileName) throws IOException {
- final Props p = new Props();
- return loadProps(p, fileName);
- }
-}
diff --git a/jodd-props/src/test/java/jodd/props/PropertiesToPropsTestHelper.java b/jodd-props/src/test/java/jodd/props/PropertiesToPropsTestHelper.java
deleted file mode 100644
index fb47919f9..000000000
--- a/jodd-props/src/test/java/jodd/props/PropertiesToPropsTestHelper.java
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.props;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.Map;
-import java.util.Properties;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.fail;
-
-public class PropertiesToPropsTestHelper {
-
- private PropertiesToPropsTestHelper() {
- }
-
- public static String safelyWritePropertiesToProps(final Properties baseProperties) throws IOException {
- final StringWriter writer = new StringWriter();
- safelyWritePropertiesToProps(writer, baseProperties);
- return writer.toString();
- }
-
- public static void safelyWritePropertiesToProps(final Writer writer, final Properties baseProperties)
- throws IOException {
- try {
- PropsConverter.convert(writer, baseProperties);
- } finally {
- try {
- writer.close();
- } catch (IOException e) {
- // close quietly
- }
- }
- }
-
- public static String safelyWritePropertiesToProps(final Properties baseProperties,
- final Map profiles) throws IOException {
- final StringWriter writer = new StringWriter();
- safelyWritePropertiesToProps(writer, baseProperties, profiles);
- return writer.toString();
- }
-
- public static void safelyWritePropertiesToProps(final Writer writer, final Properties baseProperties,
- final Map profiles) throws IOException {
- try {
- PropsConverter.convert(writer, baseProperties, profiles);
- } finally {
- try {
- writer.close();
- } catch (IOException e) {
- // close quietly
- }
- }
- }
-
- // todo: implement equals and hashCode in Jodd Props, PropsData and PropsEntry then use assertEqualProps
- public static void assertEqualProps(final String actual, final String expectedResourceFileName) {
- final Props actualProps = new Props();
- actualProps.load(actual);
-
- final Props expectedProps = new Props();
- try {
- expectedProps.load(getResourceFile(expectedResourceFileName));
- } catch (IOException | URISyntaxException e) {
- fail(e.getMessage());
- throw new IllegalStateException(e);
- }
- assertEquals(expectedProps, actualProps);
- }
-
- public static void assertEqualsToPropsFile(final String actual, final String resourceNameWithExpectedFileContent)
- throws URISyntaxException {
- assertEqualsToPropsFile(actual, getResourceFile(resourceNameWithExpectedFileContent));
- }
-
- public static File getResourceFile(final String name) throws URISyntaxException {
- final URL resourceFile = PropertiesToPropsTestHelper.class.getResource(name);
- return new File(resourceFile.toURI());
- }
-
- public static void assertEqualsToPropsFile(final String actual, final File expectedFile) {
- final FileReader reader = getFileReader(expectedFile);
- final BufferedReader bufferedReader = new BufferedReader(reader);
- final String expected;
- try {
- expected = readContent(bufferedReader);
- } catch (IOException e) {
- fail(e.getMessage());
- throw new IllegalStateException(e);
- }
-
- String actualUnixStyle = actual.replace("\r\n", "\n");
- assertEquals(expected, actualUnixStyle);
- }
-
- private static String readContent(final BufferedReader reader) throws IOException {
- String content = "";
- String line;
- while ((line = reader.readLine()) != null) {
- content += line;
- // Mimic Props writer functionality
- content += '\n';
- }
- return content;
- }
-
- private static FileReader getFileReader(final File file) {
- final FileReader reader;
- try {
- reader = new FileReader(file);
- } catch (FileNotFoundException e) {
- fail(e.getMessage());
- throw new IllegalStateException(e);
- }
- return reader;
- }
-}
diff --git a/jodd-props/src/test/java/jodd/props/Props141Test.java b/jodd-props/src/test/java/jodd/props/Props141Test.java
deleted file mode 100644
index 6927f5db6..000000000
--- a/jodd-props/src/test/java/jodd/props/Props141Test.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.props;
-
-import jodd.util.StringPool;
-import org.junit.jupiter.api.Test;
-
-import java.io.IOException;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
-
-class Props141Test extends BasePropsTest {
-
- @Test
- void test141Simple() throws IOException {
- Props props = new Props();
- String data = readDataFile("i141.props");
- props.load(data);
-
- assertEquals("value1", props.getValue("key1"));
-
- assertNull(props.getValue(".key1", "ONE"));
- assertEquals("value1#ONE", props.getValue("key1", "ONE"));
- assertEquals("value1", props.getValue("key1", "qwe", null));
- }
-
- @Test
- void test141Complex() throws IOException {
- Props props = new Props();
- String data = readDataFile("i141-2.props");
- props.load(data);
-
- // Without profile, and using ERROR profile
- assertEquals("NOT AN ERROR 1", props.getValue("code", StringPool.EMPTY));
- assertEquals("NOT AN ERROR 2", props.getValue("label", StringPool.EMPTY));
- assertEquals("NOT AN ERROR 3", props.getValue("details", StringPool.EMPTY));
-
- assertEquals("#UNDEFINED", props.getValue("code", "ERROR"));
- assertEquals("UNDEFINED LABEL", props.getValue("label", "ERROR"));
- assertEquals("UNDEFINED DETAILS", props.getValue("details", "ERROR"));
-
- // Using the ERROR.ONE inner profile
- assertEquals("#ONE", props.getValue("code", "ERROR.ONE"));
- assertEquals("THIS IS ERROR #ONE", props.getValue("label", "ERROR.ONE"));
- assertEquals("UNDEFINED DETAILS", props.getValue("details", "ERROR.ONE"));
-
- // Now, using ERROR.TWO inner profile, which uses another syntax:
- assertEquals("#TWO", props.getValue("code", "ERROR.TWO"));
- assertEquals("THIS IS ERROR #TWO", props.getValue("label", "ERROR.TWO"));
- assertEquals("UNDEFINED DETAILS", props.getValue("details", "ERROR.TWO"));
-
- // trying to use a third inner profile, not defined in the properties
- assertEquals("#UNDEFINED", props.getValue("code", "ERROR.THREE"));
- assertEquals("UNDEFINED LABEL", props.getValue("label", "ERROR.THREE"));
- assertEquals("UNDEFINED DETAILS", props.getValue("details", "ERROR.THREE"));
- }
-}
diff --git a/jodd-props/src/test/java/jodd/props/Props146Test.java b/jodd-props/src/test/java/jodd/props/Props146Test.java
deleted file mode 100644
index e0c0d48af..000000000
--- a/jodd-props/src/test/java/jodd/props/Props146Test.java
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.props;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-class Props146Test {
-
- @Test
- void testIssue146ActiveProfile() {
- String data =
- "root=/app\n" +
- "root=/foo\n" +
- "root=/bar\n" +
- "data.path=${root}/data";
-
- Props props = new Props();
-
- props.load(data);
-
- assertEquals("/app", props.getValue("root"));
- assertEquals("/app/data", props.getValue("data.path"));
-
- // set active profile, now we expect
- props.setActiveProfiles("foo");
-
- assertEquals("/foo", props.getValue("root"));
- assertEquals("/foo/data", props.getValue("data.path"));
-
- // different active profile
- props.setActiveProfiles("bar");
-
- assertEquals("/bar", props.getValue("root"));
- assertEquals("/bar/data", props.getValue("data.path"));
- }
-
- @Test
- void testIssue146DeclaredProfile() {
- String data =
- "root=/app\n" +
- "root=/foo\n" +
- "data.path=${root}/data\n" +
- "data.path=${root}/data"
- ;
-
- Props props = new Props();
- props.load(data);
-
- assertEquals("/app", props.getValue("root"));
- assertEquals("/app/data", props.getValue("data.path"));
-
- props.setActiveProfiles("foo");
-
- assertEquals("/foo", props.getValue("root"));
- assertEquals("/foo/data", props.getValue("data.path"));
- }
-
- @Test
- void testIssue146Directly() {
- String data =
- "root=/app\n" +
- "root=/foo\n" +
- "data.path=${root}/data";
-
- Props props = new Props();
- props.load(data);
-
- assertEquals("/app", props.getValue("root"));
- assertEquals("/app/data", props.getValue("data.path"));
-
- assertEquals("/foo", props.getValue("root", "foo"));
- assertEquals("/foo/data", props.getValue("data.path", "foo"));
- }
-
- @Test
- void testAddonFor146() {
- String data =
- "key1=DEFAULT\n" +
- "key1=FOO\n" +
- "\n" +
- "key2=${key1}\n" +
- "\n" +
- "key3=${key1}\n" +
- "\n" +
- "key4=${key1}\n" +
- "key4=${key1}BAR\n" +
- "\n" +
- "[group1]\n" +
- "key=DEFAULT\n" +
- "key=FOO\n" +
- "[group2]\n" +
- "<= group1";
-
- Props props = new Props();
- props.load(data);
-
- assertEquals("FOO", props.getValue("key1", "foo"));
- assertEquals("DEFAULT", props.getValue("key1"));
-
- assertEquals("FOO", props.getValue("key3"));
- assertEquals("FOO", props.getValue("key3", "foo"));
- assertEquals("FOO", props.getValue("key3", "foo", "bar"));
-
- assertEquals("FOO", props.getValue("key4", "foo"));
- assertEquals("FOOBAR", props.getValue("key4", "bar"));
- assertEquals("DEFAULT", props.getValue("key4"));
-
- assertEquals("FOO", props.getValue("group2.key")); // == ${group1.key}
-
- }
-
-}
diff --git a/jodd-props/src/test/java/jodd/props/Props610Test.java b/jodd-props/src/test/java/jodd/props/Props610Test.java
deleted file mode 100644
index 06e3e04f0..000000000
--- a/jodd-props/src/test/java/jodd/props/Props610Test.java
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.props;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-class Props610Test {
-
- @Test
- void testMapKey_withEscape() {
- final Props props = new Props();
- props.load("map\\[k1]=v1");
-
- assertEquals("map[k1]", props.entries().iterator().next().getKey());
- }
-
- @Test
- void testMapKey_withoutEscape() {
- final Props props = new Props();
- props.load("map[k1]=v1");
-
- assertEquals("map[k1]", props.entries().iterator().next().getKey());
- }
-
- @Test
- void testMapKey_withoutSpaces() {
- final Props props = new Props();
- props.load(" [k1]=v1");
-
- assertEquals("k1", props.entries().iterator().next().getKey());
- }
-
-}
diff --git a/jodd-props/src/test/java/jodd/props/PropsBeanTest.java b/jodd-props/src/test/java/jodd/props/PropsBeanTest.java
deleted file mode 100644
index 74224268f..000000000
--- a/jodd-props/src/test/java/jodd/props/PropsBeanTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.props;
-
-import jodd.bean.BeanCopy;
-import jodd.bean.BeanUtil;
-import org.junit.jupiter.api.Test;
-
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-class PropsBeanTest {
-
- public static class HttpConfig {
- public int port;
- public String address;
- public int pool;
- }
-
- @Test
- void testInnerMapToBean() {
- final String data = "http.port=10101\n" +
- "http.address=localhost\n" +
- "http.pool=30\n" +
- "foo=bar";
-
- Props props = new Props();
- props.load(data);
-
- final Map innerMap = props.innerMap("http");
- assertEquals(3, innerMap.size());
-
- final HttpConfig httpConfig = new HttpConfig();
-
- BeanCopy.from(innerMap).to(httpConfig).copy();
-
- assertEquals(10101, httpConfig.port);
- assertEquals(30, httpConfig.pool);
- assertEquals("localhost", httpConfig.address);
-
- // back
-
- props = new Props();
- props.addInnerMap("http", innerMap);
-
- assertEquals("10101", props.getValue("http.port"));
- assertEquals("30", props.getValue("http.pool"));
- assertEquals("localhost", props.getValue("http.address"));
- }
-
- @Test
- void testToBean() {
- final String data = "port=10101\n" +
- "address=localhost\n" +
- "pool=30\n" +
- "foo=bar";
-
- final Props props = new Props();
- props.load(data);
-
- final HttpConfig httpConfig = new HttpConfig();
-
- props.entries().forEach(pe -> BeanUtil.silent.setProperty(httpConfig, pe.getKey(), pe.getValue()));
-
- assertEquals(10101, httpConfig.port);
- assertEquals(30, httpConfig.pool);
- assertEquals("localhost", httpConfig.address);
- }
-}
diff --git a/jodd-props/src/test/java/jodd/props/PropsLoaderTest.java b/jodd-props/src/test/java/jodd/props/PropsLoaderTest.java
deleted file mode 100644
index 53f88eaa9..000000000
--- a/jodd-props/src/test/java/jodd/props/PropsLoaderTest.java
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.props;
-
-import jodd.test.DisabledOnJava;
-import org.junit.jupiter.api.Test;
-
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.net.URISyntaxException;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Properties;
-
-import static jodd.props.PropertiesToPropsTestHelper.assertEqualsToPropsFile;
-import static jodd.props.PropertiesToPropsTestHelper.safelyWritePropertiesToProps;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-
-class PropsLoaderTest {
-
- private static final String PROPSUTIL_CONVERT_PATH = "propsutil/convert";
-
- @Test
- void testCanUseBufferedWriterToWriteBasePropertiesToProps() throws IOException, URISyntaxException {
- final Properties properties = new Properties();
- properties.setProperty("myOneProperty", "and it's value");
-
- final StringWriter stringWriter = new StringWriter();
- final BufferedWriter writer = new BufferedWriter(stringWriter);
- safelyWritePropertiesToProps(writer, properties);
-
- final String expectedResourceFileName = getResourcePath("/singleProperty.props");
- final String actual = stringWriter.toString();
-
- assertEqualsToPropsFile(actual, expectedResourceFileName);
- }
-
- @Test
- void testCanWriteBasePropertiesToProps() throws IOException, URISyntaxException {
- final Properties properties = new Properties();
- properties.setProperty("myOneProperty", "and it's value");
-
- final String actual = safelyWritePropertiesToProps(properties);
- final String expectedResourceFileName = getResourcePath("singleProperty.props");
-
- assertEqualsToPropsFile(actual, expectedResourceFileName);
- }
-
- @Test
- void testCanWriteBaseWithProfilePropertiesToProps() throws IOException, URISyntaxException {
- final Properties baseProperties = new Properties();
- baseProperties.setProperty("myOneProperty", "and it's value");
-
- final Properties productionProperties = new Properties();
- productionProperties.setProperty("myOneProperty", "I've got production written all over me");
-
- final Map profiles = new LinkedHashMap() {
- {
- put("production", productionProperties);
- }
- };
- final String actual = safelyWritePropertiesToProps(baseProperties, profiles);
- final String expectedResourceFileName = getResourcePath("oneProfile.props");
-
- assertEqualsToPropsFile(actual, expectedResourceFileName);
- }
-
- @Test
- void testCanWriteBaseWithTwoProfilePropertiesToProps() throws IOException, URISyntaxException {
- final Properties baseProperties = new Properties();
- baseProperties.setProperty("myOneProperty", "and it's value");
-
- final Properties productionProperties = new Properties();
- productionProperties.setProperty("myOneProperty", "I've got production written all over me");
-
- final Properties testProperties = new Properties();
- testProperties.setProperty("myOneProperty", "TEST TEST TEST!!");
-
- final Map profiles = new LinkedHashMap() {
- {
- put("production", productionProperties);
- put("test", testProperties);
- }
- };
- final String actual = safelyWritePropertiesToProps(baseProperties, profiles);
- final String expectedResourceFileName = getResourcePath("twoProfiles.props");
-
-// assertEqualProps(actual, expectedResourceFileName);
- assertEqualsToPropsFile(actual, expectedResourceFileName);
- }
-
- @Test
- void testCanWriteMoreProfileThanBasePropertiesToProps() throws IOException, URISyntaxException {
- final Properties baseProperties = new Properties();
- baseProperties.setProperty("myOneProperty", "and it's value");
-
- final Properties productionProperties = new Properties();
- productionProperties.setProperty("mySecondProperty", "I've got production written all over me");
-
- final Properties testProperties = new Properties();
- testProperties.setProperty("mySecondProperty", "TEST TEST TEST!!");
-
- final Map profiles = new LinkedHashMap() {
- {
- put("production", productionProperties);
- put("test", testProperties);
- }
- };
- final String actual = safelyWritePropertiesToProps(baseProperties, profiles);
- final String expectedResourceFileName = getResourcePath("moreProfilePropertiesThanBase.props");
-
- assertEqualsToPropsFile(actual, expectedResourceFileName);
- }
-
- @Test
- void testCanWriteMultilineValuesToProps() throws IOException, URISyntaxException {
- final Properties baseProperties = new Properties();
- baseProperties.setProperty("myOneProperty", "long value\\\nin two lines");
-
- final String actual = safelyWritePropertiesToProps(baseProperties);
- final String expectedResourceFileName = getResourcePath("multilineValue.props");
-
- assertEqualsToPropsFile(actual, expectedResourceFileName);
- }
-
- @Test
- void testCanWriteUtf8ValuesToProps() throws IOException, URISyntaxException {
- final Properties baseProperties = new Properties();
- baseProperties.setProperty("myOneProperty", "some utf8 \\u0161\\u0111\\u017e\\u010d\\u0107");
-
- final String actual = safelyWritePropertiesToProps(baseProperties);
- final String expectedResourceFileName = getResourcePath("utf8Value.props");
-
- assertEqualsToPropsFile(actual, expectedResourceFileName);
- }
-
- @Test
- @DisabledOnJava(value = 9, description = "Classpath loading only works with MR-JAR jars as they don't work in exploded mode.")
- void testCreateFromClasspath_WithExistingFileThroughPattern() {
- final Props actual = Props.create().loadFromClasspath("*jodd/props/data/test.properties");
-
- // asserts
- assertNotNull(actual);
- assertEquals(3, actual.countTotalProperties());
- assertEquals("value", actual.getValue("one"));
- assertEquals("long valuein two lines", actual.getValue("two"));
- assertEquals("some utf8 šđžčć", actual.getValue("three"));
-
- }
-
- @Test
- void testCreateFromClasspath_WithNotExistingFileThroughPattern() {
-
- final Props actual = Props.create().loadFromClasspath("*jodd/props/data/test_properties");
-
- // asserts
- assertNotNull(actual);
- assertEquals(0, actual.countTotalProperties());
- assertNull(actual.getValue("one"));
- }
-
-
- private String getResourcePath(final String name) {
- return PROPSUTIL_CONVERT_PATH + "/" + name;
- }
-
-}
diff --git a/jodd-props/src/test/java/jodd/props/PropsTest.java b/jodd-props/src/test/java/jodd/props/PropsTest.java
deleted file mode 100644
index b59f39aa6..000000000
--- a/jodd-props/src/test/java/jodd/props/PropsTest.java
+++ /dev/null
@@ -1,1126 +0,0 @@
-// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-
-package jodd.props;
-
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Method;
-import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Properties;
-import java.util.stream.Stream;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestInstance;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.MethodSource;
-
-class PropsTest extends BasePropsTest {
-
- @Test
- void testBasic() throws IOException {
- Props p = new Props();
- p.load(readDataFile("test.props"));
-
- assertEquals(17, p.countTotalProperties());
-
- assertEquals("Snow White and the Seven Dwarfs", p.getValue("story"));
- assertEquals("Walt Disney's New characters in his first full-length production!", p.getValue("Tagline"));
- assertEquals("C:\\local\\snowwhite.mpg", p.getValue("file"));
- assertEquals("Snow White, pursued by a jealous queen, hides with the Dwarfs; the queen feeds her a poison apple, but Prince Charming awakens her with a kiss.", p.getValue("plot"));
-
- assertEquals("45.7", p.getValue("bashful.weight"));
- assertEquals("49.5", p.getValue("doc.weight"));
-
- assertEquals("Čađavi Žar utf8", p.getValue("comment"));
-
- assertEquals("foo\tboo\rzoo\nxxx\ftoo", p.getValue("special-chars"));
- assertEquals("\\\\a", p.getValue("special2"));
- assertEquals(3, p.getValue("special2").length());
-
- assertNull(p.getValue("non existing"));
-
- Properties prop = new Properties();
- p.extractProps(prop, null);
- assertEquals("1937{c}", prop.getProperty("year"));
- assertEquals("49.5", prop.getProperty("doc.weight"));
- assertEquals("Čađavi Žar utf8", prop.getProperty("comment"));
- }
-
- @Test
- void testEscapeNewValue() throws IOException {
- Props p = new Props();
- p.setEscapeNewLineValue(" ");
- p.load(readDataFile("test.props"));
- assertEquals("Snow White, pursued by a jealous queen, hides with the Dwarfs; the queen feeds her a poison apple, but Prince Charming awakens her with a kiss.", p.getValue("plot"));
- }
-
- @Test
- void testIgnorePrefixWhitespace() throws IOException {
- Props p = new Props();
- p.setIgnorePrefixWhitespacesOnNewLine(false);
- p.load(readDataFile("test.props"));
- assertEquals("Snow White, pursued by a jealous queen, hides with the Dwarfs; \t\tthe queen feeds her a poison apple, but Prince Charming \t\tawakens her with a kiss.", p.getValue("plot"));
- }
-
- @Test
- void testProfiles() throws IOException {
- Props p = new Props();
- p.load(readDataFile("test-profiles.props"));
-
- assertEquals("one", p.getValue("foo"));
- assertEquals("one", p.getValue("foo", "non_existing_profile"));
- assertEquals("one", p.getValue("foo", "qwe"));
- assertEquals("ten", p.getValue("bar"));
-
- assertEquals("12345", p.getValue("vitamine", "aaa"));
-
- assertEquals(8, p.countTotalProperties());
-
- assertNull(p.getValue("db.url"));
- assertEquals("localhost", p.getValue("db.url", "develop"));
- assertEquals("localhost", p.getValue("db.url", "develop", "deploy"));
- assertEquals("192.168.1.102", p.getValue("db.url", "deploy", "develop"));
- assertEquals("192.168.1.102", p.getValue("db.url", "deploy"));
-
- Properties prop = new Properties();
- p.extractProps(prop, null);
- assertEquals("one", prop.getProperty("foo"));
-
- prop.clear();
- p.extractProps(prop, "non_existing");
- assertEquals("one", prop.getProperty("foo"));
-
- prop.clear();
- p.extractProps(prop, "aaa");
- assertEquals("12345", prop.getProperty("vitamine"));
-
- prop.clear();
- p.extractProps(prop, "develop");
- assertEquals("localhost", prop.getProperty("db.url"));
- assertEquals("one", prop.getProperty("foo"));
-
- prop.clear();
- p.extractProps(prop, "develop", "deploy");
- assertEquals("localhost", prop.getProperty("db.url"));
- assertEquals("one", prop.getProperty("foo"));
-
- prop.clear();
- p.extractProps(prop, "deploy", "develop");
- assertEquals("192.168.1.102", prop.getProperty("db.url"));
- assertEquals("one", prop.getProperty("foo"));
-
- prop.clear();
- p.extractProps(prop, "deploy");
- assertEquals("192.168.1.102", prop.getProperty("db.url"));
- assertEquals("one", prop.getProperty("foo"));
-
- prop.clear();
- p.setActiveProfiles("deploy");
- p.extractSubProps(prop, "db.*");
- assertEquals(2, prop.size());
- }
-
- @Test
- void testDefaultProfile() {
- Props p = new Props();
- p.load(
- "key1=hello\n" +
- "key1=Hi!\n" +
- " \n" +
- "@profiles=one");
-
- assertEquals("Hi!", p.getValue("key1"));
- assertEquals("Hi!", p.getValue("key1", "one"));
- }
-
- @Test
- void testNestedProfiles() throws IOException {
- Props p = new Props();
- p.load(readDataFile("test-profiles.props"));
-
- assertEquals("hello", p.getBaseValue("key1"));
- assertEquals("hello", p.getValue("key1"));
- assertEquals("Hi!", p.getValue("key1", "one"));
- assertEquals("Hola!", p.getValue("key1", "one.two"));
- assertEquals("world", p.getValue("key2", "one.two"));
- assertNull(p.getValue("key2", "one"));
- assertEquals("Grazias", p.getValue("key3", "one.two"));
- assertEquals("Grazias", p.getValue("key3", "one"));
-
- Properties prop = new Properties();
- p.extractProps(prop);
- assertEquals(3, prop.size());
- assertEquals("hello", prop.getProperty("key1"));
-
- prop.clear();
- p.extractProps(prop, "one");
- assertEquals(3 + 1, prop.size());
- assertEquals("Hi!", prop.getProperty("key1"));
- assertEquals("Grazias", prop.getProperty("key3"));
-
- prop.clear();
- p.extractProps(prop, "one.two");
- assertEquals(3 + 2, prop.size());
- assertEquals("Hola!", prop.getProperty("key1"));
- assertEquals("world", prop.getProperty("key2"));
- assertEquals("Grazias", prop.getProperty("key3"));
- }
-
- @Test
- void testMacros() throws IOException {
- Props p = new Props();
- p.load(readDataFile("test2.props"));
-
- assertEquals("/roo/mypath", p.getValue("data.mypath"));
-
- assertEquals("/app/data", p.getValue("data.path"));
- assertEquals("/app/data2", p.getValue("data.path", "@prof1"));
- assertEquals("/foo/data3", p.getValue("data.path", "@prof2"));
-
- assertEquals("/roo/re", p.getValue("data.path", "@p1"));
- assertEquals("/app/re", p.getValue("data.path", "@p2"));
-
- Properties prop = new Properties();
- p.extractProps(prop, "@prof2");
- assertEquals("/foo/data3", prop.getProperty("data.path"));
- }
-
- @Test
- void testMacrosNew() throws IOException {
- Props p = new Props();
- p.load(readDataFile("test2.props"));
-
- assertEquals("/app/data", p.getValue("data.path"));
- assertEquals("/app/data2", p.getValue("data.path", "@prof1"));
- assertEquals("/foo/data3", p.getValue("data.path", "@prof2"));
-
- assertEquals("/roo/re", p.getValue("data.path", "@p1"));
- assertEquals("/app/re", p.getValue("data.path", "@p2"));
-
- Properties prop = new Properties();
- p.extractProps(prop, "@prof2");
- assertEquals("/foo/data3", prop.getProperty("data.path"));
-
- // activate profiles
-
- p.setActiveProfiles("@prof2");
- assertEquals("/foo/data3", p.getValue("data.path", "@prof2"));
-
- p.setActiveProfiles("@p1", "@p2");
- assertEquals("/app/re", p.getValue("data.path", "@p2"));
- }
-
-
- @Test
- void testMacros2() throws IOException {
- Props p = new Props();
- p.setValue("key1", "**${key${key3}}**");
- p.setValue("key3", "2");
- p.setValue("key2", "++++");
-
- assertEquals("**++++**", p.getValue("key1"));
- }
-
- @Test
- void testMacroNotExist() {
- Props p = new Props();
- p.setValue("mac1", "value1");
- p.setValue("key1", "${mac1}");
- p.setValue("key2", "${mac2}");
-
- assertEquals("value1", p.getValue("mac1"));
- assertEquals("value1", p.getValue("key1"));
- assertEquals("${mac2}", p.getValue("key2"));
- }
-
- @Test
- void testMacroNotExistIgnoreMissing() {
- Props p = new Props();
- p.setIgnoreMissingMacros(true);
- p.setValue("mac1", "value1");
- p.setValue("key1", "${mac1}");
- p.setValue("key2", "${mac2}");
-
- assertEquals("value1", p.getValue("mac1"));
- assertEquals("value1", p.getValue("key1"));
- assertNull(p.getValue("key2"));
- }
-
- @Test
- void testMacroNotExistSkipEmpty() {
- Props p = new Props();
- p.setIgnoreMissingMacros(true);
- p.setSkipEmptyProps(false);
- p.setValue("mac1", "value1");
- p.setValue("key1", "${mac1}");
- p.setValue("key2", "${mac2}");
-
- assertEquals("value1", p.getValue("mac1"));
- assertEquals("value1", p.getValue("key1"));
- assertEquals("", p.getValue("key2"));
- }
-
- @Test
- void testClone() throws IOException {
- Props p = new Props();
- p.load(readDataFile("test2.props"));
-
- Props p2 = p.clone();
- p2.load(readDataFile("test.props"));
-
- assertEquals(3, p.countTotalProperties());
- assertEquals(20, p2.countTotalProperties());
-
- assertEquals("/app/data", p.getValue("data.path"));
- assertEquals("/app/data2", p.getValue("data.path", "@prof1"));
- assertEquals("/foo/data3", p.getValue("data.path", "@prof2"));
- }
-
- @Test
- void testEmpty() throws IOException {
- Props p = new Props();
- p.setSkipEmptyProps(false);
- p.load(readDataFile("test-e.props"));
-
- assertEquals(2, p.countTotalProperties());
- assertEquals("good", p.getValue("ok"));
- assertEquals("", p.getValue("empty"));
- }
-
- @Test
- void testActiveProfiles() throws IOException {
- Props p = loadProps("test-actp.props");
-
- assertEquals("hello", p.getBaseValue("key1"));
- assertEquals("Hola!", p.getValue("key1"));
- assertEquals("world", p.getValue("key2"));
-
- assertEquals(1, p.getActiveProfiles().length);
- assertEquals("one.two", p.getActiveProfiles()[0]);
- }
-
- @Test
- void testProperties() throws IOException {
- Props p = loadProps("test.properties");
-
- assertEquals("value", p.getValue("one"));
- assertEquals("long valuein two lines", p.getValue("two"));
- assertEquals("some utf8 šđžčć", p.getValue("three"));
- }
-
- @Test
- void testAdd() {
- Props p = new Props();
- p.setValue("key1", "val${key2}");
-
- assertEquals("val${key2}", p.getValue("key1"));
- assertNull(p.getValue("key1${key2}"));
-
- p.setValue("key2", "hurrey\tme!");
-
- assertEquals("valhurrey\tme!", p.getValue("key1"));
- }
-
- @Test
- void testDuplicate() throws IOException {
- Props p = new Props();
- loadProps(p, "test-dupl.props");
-
- assertEquals("three", p.getValue("foo"));
- assertEquals("everywhere", p.getValue("bar", "prof"));
-
- p = new Props();
- p.setAppendDuplicateProps(true);
- loadProps(p, "test-dupl.props");
-
- assertEquals("one,two,three", p.getValue("foo"));
- assertEquals("here,there,everywhere", p.getValue("bar", "prof"));
- }
-
- @Test
- void testDoubleLoadsAndResolves() {
- Props props = new Props();
- props.load("pojoBean2.val2=123");
- props.load("pojoBean2.val1=\\\\${pojo}");
-
- assertEquals("123", props.getValue("pojoBean2.val2"));
- // BeanTemplate resolves \${foo} to ${foo}
- // we must be sure that escaped value is not resolved.
- assertEquals("\\${pojo}", props.getValue("pojoBean2.val1"));
-
- props.load("pojoBean2.val1=\\\\${pojo} ${pojo}");
- assertEquals(2, props.countTotalProperties());
- assertEquals("\\${pojo} ${pojo}", props.getValue("pojoBean2.val1"));
- }
-
- @Test
- void testSystemProperties() {
- Props props = new Props();
- assertEquals(0, props.countTotalProperties());
- assertNull(props.getValue("user.dir"));
-
- props.loadSystemProperties("sys");
- assertTrue(props.countTotalProperties() > 0);
- assertNotNull(props.getValue("sys.user.dir"));
- }
-
- @Test
- void testEnvironment() {
- Props props = new Props();
- assertEquals(0, props.countTotalProperties());
-
- props.loadEnvironment("env");
- assertTrue(props.countTotalProperties() > 0);
- }
-
- @Test
- void testValueWithBracket() throws IOException {
- Props p = new Props();
- p.load(readDataFile("test3.props"));
-
- assertEquals("info@jodd.org;patrick@jodd.org", p.getValue("email.from"));
- assertEquals("[ERROR] Got %s exceptions", p.getValue("email.subject"));
- assertEquals("line1line2line3", p.getValue("email.text"));
-
- p = new Props();
- p.setIgnorePrefixWhitespacesOnNewLine(false);
- p.load(readDataFile("test3.props"));
-
- assertEquals("info@jodd.org;patrick@jodd.org", p.getValue("email.from"));
- assertEquals("[ERROR] Got %s exceptions", p.getValue("email.subject"));
- assertEquals("line1\tline2line3", p.getValue("email.text"));
-
- p = new Props();
- p.setIgnorePrefixWhitespacesOnNewLine(false);
- p.setEscapeNewLineValue("\n");
- p.load(readDataFile("test3.props"));
-
- assertEquals("info@jodd.org;patrick@jodd.org", p.getValue("email.from"));
- assertEquals("[ERROR] Got %s exceptions", p.getValue("email.subject"));
- assertEquals("line1\n\tline2\nline3", p.getValue("email.text"));
- }
-
- @Test
- void testMultilineValue() throws IOException {
- Props p = new Props();
- p.setValueTrimLeft(true);
- p.load(readDataFile("test3.props"));
-
- assertEquals(System.lineSeparator() + "\tHello from" + System.lineSeparator() + "\tthe multiline" + System.lineSeparator() + "\tvalue" + System.lineSeparator() , p.getValue("email.footer"));
- assertEquals("aaa", p.getValue("email.header"));
- }
-
- @Test
- void testAppend() {
- Props p = new Props();
- p.setAppendDuplicateProps(true);
- p.load("foo=123\nfoo=456");
- assertEquals("123,456", p.getValue("foo"));
-
- p = new Props();
- p.load("foo=123\nfoo+=456");
- assertEquals("123,456", p.getValue("foo"));
- }
-
- @Test
- void testAppend2() {
- Props p = new Props();
- p.setAppendDuplicateProps(false);
- p.load("foo=one\nfoo=two\nfoo+=three");
- assertEquals("two,three", p.getValue("foo"));
-
- p = new Props();
- p.setAppendDuplicateProps(true);
- p.load("foo=one\nfoo=two\nfoo+=three");
- assertEquals("one,two,three", p.getValue("foo"));
-
- p = new Props();
- p.setAppendDuplicateProps(false);
- p.load("foo=one\nfoo=two\nfoo+=three\nfoo=four");
- assertEquals("four", p.getValue("foo"));
- }
-
- @Test
- void testAppendEof() {
- Props p = new Props();
- p.setAppendDuplicateProps(false);
- p.load("foo=one\nfoo=two\nfoo+");
- assertEquals("two", p.getValue("foo"));
- }
-
- @Test
- void testActiveProfileBeforeInit() {
- Props p = new Props();
- p.setActiveProfiles("xxx");
- p.load("foo=one");
- assertNotNull(p.getActiveProfiles());
- assertEquals("xxx", p.getActiveProfiles()[0]);
- }
-
- @Test
- void testDoubleInitialization() {
- Props p = new Props();
- p.setValue("bar", "two.${foo}.${wer}");
- p.setValue("foo", "one");
-
- assertEquals("two.one.${wer}", p.getValue("bar"));
-
- p.setValue("wer", "zero");
-
- assertEquals("two.one.zero", p.getValue("bar"));
- }
-
- @Test
- void testCategoriesInValues() {
- Props p = new Props();
- p.load( "[section]\n" +
- "foo = aaa, [bbb:ccc]\n" +
- "bar = teapot");
-
- assertEquals("aaa, [bbb:ccc]", p.getValue("section.foo"));
- assertEquals("teapot", p.getValue("section.bar"));
- }
-
- @Test
- void testDuplicatedValue() {
- Props p = new Props();
- p.setValue("foo", "bar");
- p.setValue("foo", "aaa", "prof1");
- p.setValue("foo", "bbb", "prof2");
-
- assertEquals("bar", p.getValue("foo"));
- assertEquals("aaa", p.getValue("foo", "prof1"));
- assertEquals("bbb", p.getValue("foo", "prof2"));
-
- assertEquals("aaa", p.getValue("foo", "prof1", "prof2"));
- assertEquals("bbb", p.getValue("foo", "prof2", "prof1"));
- }
-
- @Test
- void testIteratorEmpty() {
- Props p = new Props();
-
- Iterator it = p.iterator();
-
- assertFalse(it.hasNext());
-
- try {
- it.next();
- fail("error");
- } catch (Exception ignore) {
- }
- }
-
- @Test
- void testIteratorSkip() {
- Props p = new Props();
-
- p.load("zorg=zero\n" +
- "foo=one\n" +
- "bar=two\n" +
- "foo=zero");
-
- Iterator it = p.iterator();
-
- assertTrue(it.hasNext());
-
- PropsEntry pe = it.next();
- assertEquals("foo", pe.getKey());
- pe = it.next();
- assertEquals("bar", pe.getKey());
-
- assertFalse(it.hasNext());
- try {
- it.next();
- fail("error");
- } catch (Exception ignore) {
- }
-
- p.setActiveProfiles("prof1", "prof2");
-
- it = p.iterator();
- assertEquals("zorg", it.next().getKey());
- assertEquals("foo", it.next().getKey());
- assertEquals("bar", it.next().getKey());
- assertEquals("foo", it.next().getKey());
- assertFalse(it.hasNext());
-
- it = p.entries().activeProfiles().skipDuplicatesByValue().iterator();
-
- assertEquals("zorg", it.next().getKey());
- assertEquals("bar", it.next().getKey());
- assertEquals("foo", it.next().getKey());
- assertFalse(it.hasNext());
-
- it = p.entries().profile("prof1").skipDuplicatesByValue().iterator();
- assertEquals("bar", it.next().getKey());
- assertEquals("foo", it.next().getKey());
- assertFalse(it.hasNext());
-
-
- it = p.entries().activeProfiles().skipDuplicatesByPosition().iterator();
-
- assertEquals("zorg", it.next().getKey());
- assertEquals("foo", it.next().getKey());
- assertEquals("bar", it.next().getKey());
- assertFalse(it.hasNext());
-
- it = p.entries().profile("prof1").skipDuplicatesByPosition().iterator();
- assertEquals("foo", it.next().getKey());
- assertEquals("bar", it.next().getKey());
- assertFalse(it.hasNext());
- }
-
- @Test
- void testIteratorSections() {
- Props p = new Props();
-
- p.load("aaa.zorg=zero\n" +
- "bbb.foo=one\n" +
- "ccc.bar=two\n" +
- "bbb.foo=zero");
-
-
- p.setActiveProfiles("prof1", "prof2");
-
- Iterator it = p.entries().section("bbb").profile("prof1", "prof2").iterator();
- assertEquals("bbb.foo", it.next().getKey());
- assertEquals("bbb.foo", it.next().getKey());
- assertFalse(it.hasNext());
- }
-
- @Test
- void testGetAllProfiles() {
- Props p = new Props();
-
- p.load("zorg=zero\n" +
- "foo=one\n" +
- "bar=two\n" +
- "foo=zero");
-
- String[] profiles = p.getAllProfiles();
- Arrays.sort(profiles);
- assertArrayEquals(new String[] {"prof1", "prof2"}, profiles);
- }
-
- @Test
- void testGetProfilesForKey() {
- Props p = new Props();
-
- p.load("zorg=zero\n" +
- "foo=one\n" +
- "bar=two\n" +
- "[foo]\n" +
- "info=zero\n" +
- "info2=zero2");
-
- String[] profiles = p.getProfilesFor("zorg");
-
- assertEquals(1, profiles.length);
- assertEquals("prof2", profiles[0]);
-
- profiles = p.getProfilesFor("zor*");
-
- assertEquals(1, profiles.length);
- assertEquals("prof2", profiles[0]);
-
- profiles = p.getProfilesFor("foo");
- assertEquals(0, profiles.length);
-
- profiles = p.getProfilesFor("foo.*");
- assertEquals(1, profiles.length);
- assertEquals("prof1", profiles[0]);
-
- profiles = p.getProfilesFor("foo*");
- assertEquals(1, profiles.length);
- assertEquals("prof1", profiles[0]);
- }
-
- @Test
- void testChangeActiveProfile() {
- Props p = new Props();
-
- p.load("foo=one\n" +
- "bar=two\n" +
- "foo=aaa\n" +
- "foo=bbb\n");
-
- p.setActiveProfiles("prof1");
- assertEquals("aaa", p.getValue("foo"));
-
- p.setActiveProfiles("prof2");
- assertEquals("bbb", p.getValue("foo"));
- }
-
- @Test
- void testWeirdKey() {
- Props p = new Props();
-
- p.load("org.jodd.Foo@Bar=one\n" +
- "org.jodd.Foo@*Bar=two\n" +
- "org.jodd.Foo@*Bar\\#me=three\n");
-
- assertEquals("one", p.getValue("org.jodd.Foo@Bar"));
- assertEquals("two", p.getValue("org.jodd.Foo@*Bar"));
- assertEquals("three", p.getValue("org.jodd.Foo@*Bar#me"));
- }
-
- @Test
- void testMultipleProfilesAtOnce() {
- Props p = new Props();
- p.load(
- "foo.one=111\n" +
- "foo.one=111222\n" +
- "foo.one=111222333\n"
- );
-
- p.setActiveProfiles(null);
- assertEquals("111", p.getValue("foo.one"));
-
- p.setActiveProfiles("pr1");
- assertEquals("111222", p.getValue("foo.one"));
-
- p.setActiveProfiles("pr2");
- assertEquals("111222333", p.getValue("foo.one"));
-
- p.setActiveProfiles("pr1", "pr2");
- assertEquals("111222", p.getValue("foo.one"));
-
- p.setActiveProfiles("pr2", "pr1");
- assertEquals("111222333", p.getValue("foo.one"));
- }
-
- @Test
- void testMacrosAndProfiles() {
- Props p = new Props();
- p.load(
- "one=111\n" +
- "one=111222\n" +
- "one=111222333\n" +
- "wow=${one}"
- );
-
- p.setActiveProfiles(null);
- assertEquals("111", p.getValue("wow"));
-
- p.setActiveProfiles("pr1");
- assertEquals("111222", p.getValue("wow"));
-
- p.setActiveProfiles("pr2");
- assertEquals("111222333", p.getValue("wow"));
-
- p.setActiveProfiles("pr1", "pr2");
- assertEquals("111222", p.getValue("wow"));
- }
-
- @Test
- void testMacrosAndProfilesAsBefore() {
- Props p = new Props();
- p.load(
- "one=111\n" +
- "one=111222\n" +
- "one=111222333\n" +
- "wow=${one}"
- );
-
- p.setActiveProfiles(null);
- assertEquals("111", p.getValue("wow"));
-
- p.setActiveProfiles("pr1");
- assertEquals("111222", p.getValue("wow"));
-
- p.setActiveProfiles("pr2");
- assertEquals("111222333", p.getValue("wow"));
-
- p.setActiveProfiles("pr1", "pr2");
- assertEquals("111222", p.getValue("wow"));
-
- // wow needs to be defined in a profile to get the profile value in macro
- // NOT ANYMORE!
-
- p = new Props();
- p.load(
- "one=111\n" +
- "one=111222\n" +
- "one=111222333\n" +
- "wow=${one}"
- );
-
- p.setActiveProfiles(null);
- assertEquals(null, p.getValue("wow"));
-
- p.setActiveProfiles("pr1");
- assertEquals("111222", p.getValue("wow"));
-
- p.setActiveProfiles("pr2");
- assertEquals(null, p.getValue("wow"));
-
- p.setActiveProfiles("pr1", "pr2");
- assertEquals("111222", p.getValue("wow"));
-
-
- p = new Props();
- p.load(
- "one=111\n" +
- "one=111222\n" +
- "one=111222333\n" +
- "wow=${one}"
- );
-
- p.setActiveProfiles(null);
- assertEquals(null, p.getValue("wow"));
-
- p.setActiveProfiles("pr1");
- assertEquals("111222", p.getValue("wow"));
-
- p.setActiveProfiles("pr2");
- assertEquals("111222333", p.getValue("wow"));
-
- p.setActiveProfiles("pr1", "pr2");
- assertEquals("111222", p.getValue("wow"));
- }
-
- @Test
- void testCopy() {
- Props p = new Props();
-
- p.load("foo.one=111\n" +
- "fig.two=222\n" +
- "bar <= foo, fig");
-
- assertEquals("111", p.getValue("foo.one"));
- assertEquals("222", p.getValue("fig.two"));
- assertEquals("111", p.getValue("bar.one"));
- assertEquals("222", p.getValue("bar.two"));
- }
-
- @Test
- void testCopyWithProfiles() {
- Props p = new Props();
- p.load(
- "foo.one=111\n" +
- "foo.one=111111\n" +
- "foo.one=111111111\n" +
- "fig.two=222\n" +
- "bar <= foo, fig");
-
- assertEquals("111", p.getValue("foo.one"));
- assertEquals(null, p.getValue("fig.two"));
- assertEquals("111", p.getValue("bar.one"));
- assertEquals(null, p.getValue("bar.two"));
-
- p = new Props();
- p.load(
- "foo.one=111\n" +
- "foo.one=111111\n" +
- "foo.one=111111111\n" +
- "fig.two=222\n" +
- "bar <= foo, fig");
-
- p.setActiveProfiles("pr1");
-
- assertEquals("111111", p.getValue("foo.one"));
- assertEquals(null, p.getValue("fig.two"));
- assertEquals("111111", p.getValue("bar.one"));
- assertEquals(null, p.getValue("bar.two"));
-
- p = new Props();
- p.load(
- "foo.one=111\n" +
- "foo.one=111111\n" +
- "foo.one=111111111\n" +
- "fig.two=222\n" +
- "bar <= foo, fig\n"
- );
-
- p.setActiveProfiles("pr1", "pr2");
-
- assertEquals("111111", p.getValue("foo.one"));
- assertEquals("222", p.getValue("fig.two"));
- assertEquals("111111", p.getValue("bar.one"));
- assertEquals("222", p.getValue("bar.two"));
- }
-
- @Test
- void testCopyEmpty() {
- Props p = new Props();
-
- p.load("foo.one=111\n" +
- "fig.two=222\n" +
- "[bar]\n" +
- "<= foo, fig");
-
- assertEquals("111", p.getValue("foo.one"));
- assertEquals("222", p.getValue("fig.two"));
- assertEquals("111", p.getValue("bar.one"));
- assertEquals("222", p.getValue("bar.two"));
- }
-
- @Test
- void testIssue78() {
- String data =
- "@profiles=o\n" +
- "\n" +
- "prefix = is Good\n" +
- "prefix = is very Good\n" +
- "\n" +
- "[user]\n" +
- "name = jodd ${prefix}";
-
- Props props = new Props();
- props.load(data);
-
- assertEquals("jodd is Good", props.getValue("user.name"));
- }
-
- @Test
- void testAdditionalEquals() {
- String data =
- "account-dn = cn=accountname,ou=users,o=organization\n";
-
- Props props = new Props();
- props.load(data);
-
- assertEquals("cn=accountname,ou=users,o=organization", props.getValue("account-dn"));
- }
-
-
- @Test
- void testDifferentLineEndings() {
- Props props = new Props();
- props.setIgnorePrefixWhitespacesOnNewLine(true);
- props.load("text=line1\\\n line2\\\r\n line3\\\r line4");
-
- assertEquals("line1line2line3line4", props.getValue("text"));
-
- props = new Props();
- props.setIgnorePrefixWhitespacesOnNewLine(false);
- props.load("text=line1\\\n line2\\\r\n line3\\\r line4");
-
- assertEquals("line1 line2 line3 line4", props.getValue("text"));
-
- props = new Props();
- props.setIgnorePrefixWhitespacesOnNewLine(false);
- props.setEscapeNewLineValue("|");
- props.load("text=line1\\\n line2\\\r\n line3\\\r line4");
-
- assertEquals("line1| line2| line3| line4", props.getValue("text"));
- }
-
- @Test
- void testLoad_with_file_props() throws IOException, URISyntaxException {
- final File src = readDataToFile("test2.props");
- final Props actual = new Props().load(src);
-
- // asserts
- assertEquals(3, actual.countTotalProperties());
- }
-
- @Test
- void testLoad_with_file_properties() throws IOException, URISyntaxException {
- final File src = readDataToFile("test.properties");
- final Props actual = new Props().load(src);
-
- // asserts
- assertEquals(3, actual.countTotalProperties());
- }
-
- @Test
- void testLoad_with_file_and_encoding() throws IOException, URISyntaxException {
- final File src = readDataToFile("test2.props");
- final Props actual = new Props().load(src, StandardCharsets.UTF_8.name());
-
- // asserts
- assertEquals(3, actual.countTotalProperties());
- }
-
- @Test
- void testLoad_with_inputstream() throws IOException {
- try (final InputStream is = readDataToInputstream("test2.props")) {
- final Props actual = new Props().load(is);
- // asserts
- assertEquals(3, actual.countTotalProperties());
- }
- }
-
- @Nested
- @DisplayName("test for Props#getXXXValue() - methods")
- @TestInstance(TestInstance.Lifecycle.PER_CLASS) // needed because annotation MethodSource requires static method without that
- class GetXXXValue {
-
- Props props;
-
- @BeforeEach
- void beforeEach() {
- props = new Props();
- Map map = new HashMap<>();
-
- // test data
- map.put("string_jodd", "jodd");
- map.put("boolean_true", "true");
- map.put("boolean_false", "false");
- map.put("integer_0", "0");
- map.put("integer_1234567890", "1234567890");
- map.put("integer_-45232", "-45232");
- map.put("long_0", "0");
- map.put("long_1234567890", "1234567890");
- map.put("long_-2789899", "-2789899");
- map.put("double_1234567890_12", "1234567890.12");
- map.put("double_12345678903333_34", "12345678903333.34");
- map.put("double_-43478954.44", "-43478954.44");
-
- props.load(map);
- }
-
- @ParameterizedTest (name = "{index} - Props#{1}(''{2}'') == {0}")
- @MethodSource(value = "testdata")
- void testGetXXXValue(final Object expected, final String methodName, final String key) throws Exception {
-
- Method method = props.getClass().getDeclaredMethod(methodName, String.class);
- final Object actual = method.invoke(props, key);
-
- // asserts
- assertEquals(expected, actual);
- }
-
- @ParameterizedTest (name = "{index} - Props#{1}(''{2}'', null) == {0}")
- @MethodSource(value = "testdata")
- void testGetXXXValue_WithProfile(final Object expected, final String methodName, final String key) throws Exception {
-
- Method method = props.getClass().getDeclaredMethod(methodName, String.class, String[].class);
- final Object actual = method.invoke(props, key, (String[])null);
-
- // asserts
- assertEquals(expected, actual);
- }
-
- @ParameterizedTest (name = "{index} - Props#{1}(''{2}'', {3}, ''{4}'') == {0}")
- @MethodSource(value = "testdata_for_defaultvalues_and_profiles_test")
- void testGetXXXValue_WithDefaultValueAndProfiles(final Object expected, final String methodName, final String key, final Class clazzDefaultValue, final String[] profiles) throws Exception {
- Method method = props.getClass().getDeclaredMethod(methodName, String.class, expected.getClass(), String[].class);
- final Object actual = method.invoke(props, key, expected, new String[] {"jodd"});
-
- // asserts
- assertEquals(expected, actual);
- }
-
- @ParameterizedTest (name = "{index} - Props#{1}(''{2}'', {3}) == {0}")
- @MethodSource(value = "testdata_for_defaultvalues_test")
- void testGetXXXValue_WithDefaultValue(final Object expected, final String methodName, final String key, final Class clazzDefaultValue) throws Exception {
- Method method = props.getClass().getDeclaredMethod(methodName, String.class, expected.getClass());
- final Object actual = method.invoke(props, key, expected);
-
- // asserts
- assertEquals(expected, actual);
- }
-
- private Stream testdata() {
- return Stream.of(
- // getValue
- Arguments.of("jodd", "getValue", "string_jodd"),
- Arguments.of(null, "getValue", "unknown_key"),
- // getBooleanValue
- Arguments.of(Boolean.TRUE, "getBooleanValue", "boolean_true"),
- Arguments.of(Boolean.FALSE, "getBooleanValue", "boolean_false"),
- Arguments.of(null, "getBooleanValue", "unknown_key"),
- // getIntegerValue
- Arguments.of(0, "getIntegerValue", "integer_0"),
- Arguments.of(1234567890, "getIntegerValue", "integer_1234567890"),
- Arguments.of(-45232, "getIntegerValue", "integer_-45232"),
- Arguments.of(null, "getIntegerValue", "unknown_key"),
- // getLongValue
- Arguments.of(0L, "getLongValue", "long_0"),
- Arguments.of(1234567890L, "getLongValue", "long_1234567890"),
- Arguments.of(-2789899L, "getLongValue", "long_-2789899"),
- Arguments.of(null, "getLongValue", "unknown_key"),
- // getDoubleValue
- Arguments.of(1234567890.12D, "getDoubleValue", "double_1234567890_12"),
- Arguments.of(12345678903333.34D, "getDoubleValue", "double_12345678903333_34"),
- Arguments.of(-43478954.44D, "getDoubleValue", "double_-43478954.44"),
- Arguments.of(null, "getDoubleValue", "unknown_key")
- );
- }
-
- private Stream testdata_for_defaultvalues_and_profiles_test() {
- final String an_unknown_key = "this_is_definitely_an_unknown_key_for_test_in_props_test";
- final String[] profiles = new String[] {"jodd", "db"};
- return Stream.of(
- // getBooleanValue
- Arguments.of(Boolean.FALSE, "getBooleanValue", an_unknown_key, Boolean.class, profiles),
- Arguments.of(Boolean.TRUE, "getBooleanValue", "boolean_true", Boolean.class, profiles),
- // getIntegerValue
- Arguments.of(-45232, "getIntegerValue", an_unknown_key, Integer.class, profiles),
- Arguments.of(0, "getIntegerValue", "integer_0", Integer.class, profiles),
- // getLongValue
- Arguments.of(1234567890L, "getLongValue", an_unknown_key, Long.class, profiles),
- Arguments.of(-2789899L, "getLongValue", "long_-2789899", Long.class, profiles),
- // getDoubleValue
- Arguments.of(-888.541D, "getDoubleValue", an_unknown_key, Double.class, profiles),
- Arguments.of(1234567890.12, "getDoubleValue", "double_1234567890_12", Double.class, profiles)
- );
- }
-
- private Stream testdata_for_defaultvalues_test() {
- final String an_unknown_key = "this_is_definitely_an_unknown_key_for_test_in_props_test";
- return Stream.of(
- // getBooleanValue
- Arguments.of(Boolean.FALSE, "getBooleanValue", an_unknown_key, Boolean.class),
- Arguments.of(Boolean.TRUE, "getBooleanValue", "boolean_true", Boolean.class),
- // getIntegerValue
- Arguments.of(-45232, "getIntegerValue", an_unknown_key, Integer.class),
- Arguments.of(0, "getIntegerValue", "integer_0", Integer.class),
- // getLongValue
- Arguments.of(0L, "getLongValue", an_unknown_key, Long.class),
- Arguments.of(-2789899L, "getLongValue", "long_-2789899", Long.class),
- // getDoubleValue
- Arguments.of(-888.541D, "getDoubleValue", an_unknown_key, Double.class),
- Arguments.of(1234567890.12, "getDoubleValue", "double_1234567890_12", Double.class),
- // getValueOrDefault
- Arguments.of("jodd", "getValueOrDefault", an_unknown_key, String.class),
- Arguments.of("jodd", "getValueOrDefault", "string_jodd", String.class)
- );
- }
-
- }
-}
diff --git a/jodd-props/src/test/resources/jodd/props/data/i141-2.props b/jodd-props/src/test/resources/jodd/props/data/i141-2.props
deleted file mode 100644
index 0f49fc224..000000000
--- a/jodd-props/src/test/resources/jodd/props/data/i141-2.props
+++ /dev/null
@@ -1,22 +0,0 @@
-[]
-code=NOT AN ERROR 1
-label=NOT AN ERROR 2
-details=NOT AN ERROR 3
-
-[]
-code=#UNDEFINED
-label=UNDEFINED LABEL
-detail