diff --git a/src/tap/ServiceConnection.java b/src/tap/ServiceConnection.java
index dfa94731379eb8a655551504abdbf1394510ca4d..6545b5f3cc6df64e2e01499da27cc65d78b3ed4d 100644
--- a/src/tap/ServiceConnection.java
+++ b/src/tap/ServiceConnection.java
@@ -2,27 +2,28 @@ package tap;
 
 /*
  * This file is part of TAPLibrary.
- * 
+ *
  * TAPLibrary is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
- * 
+ *
  * TAPLibrary is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU Lesser General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Lesser General Public License
  * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
- * 
- * Copyright 2012-2015 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *
+ * Copyright 2012-2018 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.Collection;
 import java.util.Iterator;
 
+import adql.db.FunctionDef;
 import tap.db.DBConnection;
 import tap.formatter.OutputFormat;
 import tap.log.DefaultTAPLog;
@@ -31,29 +32,29 @@ import tap.metadata.TAPMetadata;
 import uws.service.UserIdentifier;
 import uws.service.file.LocalUWSFileManager;
 import uws.service.file.UWSFileManager;
-import adql.db.FunctionDef;
 
 /**
- * <p>Description and parameters list of a TAP service.</p>
- * 
+ * Description and parameters list of a TAP service.
+ *
  * <p>
- * 	Through this object, it is possible to configure the different limits and formats,
- * 	but also to list all available tables and columns, to declare geometry features as all allowed user defined functions
- * 	and to say where log and other kinds of files must be stored.
+ * 	Through this object, it is possible to configure the different limits and
+ * 	formats, but also to list all available tables and columns, to declare
+ * 	geometry features as well as all allowed user defined functions and to say
+ * 	where log and other kinds of files must be stored.
  * </p>
- * 
+ *
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (03/2015)
+ * @version 2.3 (09/2018)
  */
 public interface ServiceConnection {
 
 	/**
 	 * List of possible limit units.
-	 * 
+	 *
 	 * @author Gr&eacute;gory Mantelet (CDS;ARI)
 	 * @version 2.0 (01/2015)
 	 */
-	public static enum LimitUnit{
+	public static enum LimitUnit {
 		rows("row"), bytes("byte"), kilobytes("kilobyte"), megabytes("megabyte"), gigabytes("gigabyte");
 
 		private final String str;
@@ -64,11 +65,11 @@ public interface ServiceConnection {
 
 		/**
 		 * Tells whether the given unit has the same type (bytes or rows).
-		 * 
+		 *
 		 * @param anotherUnit	A unit.
-		 * 
+		 *
 		 * @return				true if the given unit has the same type, false otherwise.
-		 * 
+		 *
 		 * @since 1.1
 		 */
 		public boolean isCompatibleWith(final LimitUnit anotherUnit){
@@ -81,9 +82,9 @@ public interface ServiceConnection {
 		/**
 		 * Gets the factor to convert into bytes the value expressed in this unit.
 		 * <i>Note: if this unit is not a factor of bytes, 1 is returned (so that the factor does not affect the value).</i>
-		 * 
+		 *
 		 * @return The factor need to convert a value expressed in this unit into bytes, or 1 if not a bytes derived unit.
-		 * 
+		 *
 		 * @since 1.1
 		 */
 		public long bytesFactor(){
@@ -103,25 +104,57 @@ public interface ServiceConnection {
 
 		/**
 		 * Compares the 2 given values (each one expressed in the given unit).
-		 * Conversions are done internally in order to make a correct comparison between the 2 limits.
-		 * 
-		 * @param leftLimit	Value/Limit of the comparison left part.
-		 * @param leftUnit	Unit of the comparison left part value.
+		 *
+		 * <p>
+		 * 	Conversions are done internally in order to make a correct
+		 * 	comparison between the 2 limits.
+		 * </p>
+		 *
+		 * @param leftLimit		Value/Limit of the comparison left part.
+		 * @param leftUnit		Unit of the comparison left part value.
 		 * @param rightLimit	Value/Limit of the comparison right part.
 		 * @param rightUnit		Unit of the comparison right part value.
-		 * 
-		 * @return the value 0 if x == y; a value less than 0 if x < y; and a value greater than 0 if x > y
-		 * 
+		 *
+		 * @return	the value 0 if x == y; a value less than 0 if x < y;
+		 *        	and a value greater than 0 if x > y
+		 *
 		 * @throws TAPException If the two given units are not compatible.
-		 * 
+		 *
+		 * @see #compare(long, LimitUnit, long, LimitUnit)
+		 *
+		 * @since 1.1
+		 */
+		public static int compare(final int leftLimit, final LimitUnit leftUnit, final int rightLimit, final LimitUnit rightUnit) throws TAPException{
+			return compare(leftLimit, leftUnit, rightLimit, rightUnit);
+		}
+
+		/**
+		 * Compares the 2 given values (each one expressed in the given unit).
+		 *
+		 * <p>
+		 * 	Conversions are done internally in order to make a correct
+		 * 	comparison between the 2 limits.
+		 * </p>
+		 *
+		 * @param leftLimit		Value/Limit of the comparison left part.
+		 * @param leftUnit		Unit of the comparison left part value.
+		 * @param rightLimit	Value/Limit of the comparison right part.
+		 * @param rightUnit		Unit of the comparison right part value.
+		 *
+		 * @return	the value 0 if x == y;
+		 *        	a value less than 0 if x < y;
+		 *        	and a value greater than 0 if x > y
+		 *
+		 * @throws TAPException If the two given units are not compatible.
+		 *
 		 * @see tap.ServiceConnection.LimitUnit#isCompatibleWith(tap.ServiceConnection.LimitUnit)
 		 * @see #bytesFactor()
 		 * @see Integer#compare(int, int)
 		 * @see Long#compare(long, long)
-		 * 
-		 * @since 1.1
+		 *
+		 * @since 2.3
 		 */
-		public static int compare(final int leftLimit, final LimitUnit leftUnit, final int rightLimit, final LimitUnit rightUnit) throws TAPException{
+		public static int compare(final long leftLimit, final LimitUnit leftUnit, final long rightLimit, final LimitUnit rightUnit) throws TAPException{
 			if (!leftUnit.isCompatibleWith(rightUnit))
 				throw new TAPException("Limit units (" + leftUnit + " and " + rightUnit + ") are not compatible!");
 
@@ -146,7 +179,7 @@ public interface ServiceConnection {
 		 * @return the value {@code 0} if {@code x == y};
 		 *         a value less than {@code 0} if {@code x < y}; and
 		 *         a value greater than {@code 0} if {@code x > y}
-		 * 
+		 *
 		 * @since 1.1
 		 */
 		private static int compare(int x, int y){
@@ -168,7 +201,7 @@ public interface ServiceConnection {
 		 * @return the value {@code 0} if {@code x == y};
 		 *         a value less than {@code 0} if {@code x < y}; and
 		 *         a value greater than {@code 0} if {@code x > y}
-		 * 
+		 *
 		 * @since 1.1
 		 */
 		public static int compare(long x, long y){
@@ -184,11 +217,11 @@ public interface ServiceConnection {
 	/**
 	 * <i>[OPTIONAL]</i>
 	 * <p>Name of the service provider ; it can be an organization as an individual person.</p>
-	 * 
+	 *
 	 * <p>There is no restriction on the syntax or on the label to use ; this information is totally free</p>
-	 * 
+	 *
 	 * <p>It will be used as additional information (INFO tag) in any VOTable and HTML output.</p>
-	 * 
+	 *
 	 * @return	The TAP service provider or NULL to leave this field blank.
 	 */
 	public String getProviderName();
@@ -196,9 +229,9 @@ public interface ServiceConnection {
 	/**
 	 * <i>[OPTIONAL]</i>
 	 * <p>Description of the service provider.</p>
-	 * 
+	 *
 	 * <p>It will be used as additional information (INFO tag) in any VOTable output.</p>
-	 * 
+	 *
 	 * @return	The TAP service description or NULL to leave this field blank.
 	 */
 	public String getProviderDescription();
@@ -207,11 +240,11 @@ public interface ServiceConnection {
 	 * <i><b>[MANDATORY]</b></i>
 	 * <p>This function tells whether the TAP service is available
 	 * (that's to say, "able to execute requests" ; resources like /availability, /capabilities and /tables may still work).</p>
-	 * 
+	 *
 	 * <p>
 	 * 	A message explaining the current state of the TAP service could be provided thanks to {@link #getAvailability()}.
 	 * </p>
-	 * 
+	 *
 	 * @return	<i>true</i> to enable all TAP resources, <i>false</i> to disable all of them (except /availability).
 	 */
 	public boolean isAvailable();
@@ -221,7 +254,7 @@ public interface ServiceConnection {
 	 * <p>Get an explanation about the current TAP service state (working or not).
 	 * This message aims to provide more details to the users about the availability of this service,
 	 * or more particularly about its unavailability.</p>
-	 * 
+	 *
 	 * @return	Explanation about the TAP service state.
 	 */
 	public String getAvailability();
@@ -233,10 +266,10 @@ public interface ServiceConnection {
 	 * 	If false, /sync and /async won't answer any more to requests and a HTTP-503 (Service unavailable)
 	 * 	error will be returned.
 	 * </p>
-	 * 
+	 *
 	 * @param isAvailable	<i>true</i> to enable all resources, <i>false</i> to forbid /sync and /async (all other resources will still be available).
 	 * @param message		A message describing the current state of the service. If NULL, a default message may be set by the library.
-	 * 
+	 *
 	 * @since 2.0
 	 */
 	public void setAvailable(final boolean isAvailable, final String message);
@@ -244,12 +277,12 @@ public interface ServiceConnection {
 	/**
 	 * <i>[OPTIONAL]</i>
 	 * <p>Get the limit of the retention period (in seconds).</p>
-	 * 
+	 *
 	 * <p>
 	 * 	It is the maximum period while an asynchronous job can leave in the jobs list
 	 * 	and so can stay on the server.
 	 * </p>
-	 * 
+	 *
 	 * <p><b>Important notes:</b></p>
 	 * <ul>
 	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
@@ -262,7 +295,7 @@ public interface ServiceConnection {
 	 * 	<li><b>The default value</b> MUST be less or equals the maximum value.</li>
 	 * 	<li><b>Both values must be positive</b>. If a negative value is given it will be interpreted as "no limit".</li>
 	 * </ul>
-	 * 
+	 *
 	 * @return NULL if no limit must be set, or a two-items array ([0]: default value, [1]: maximum value).
 	 */
 	public int[] getRetentionPeriod();
@@ -270,12 +303,12 @@ public interface ServiceConnection {
 	/**
 	 * <i>[OPTIONAL]</i>
 	 * <p>Get the limit of the job execution duration (in milliseconds).</p>
-	 * 
+	 *
 	 * <p>
 	 * 	It is the duration of a running job (including the query execution).
 	 * 	This duration is used for synchronous AND asynchronous jobs.
 	 * </p>
-	 * 
+	 *
 	 * <p><b>Important notes:</b></p>
 	 * <ul>
 	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
@@ -288,7 +321,7 @@ public interface ServiceConnection {
 	 * 	<li><b>The default value</b> MUST be less or equals the maximum value.</li>
 	 * 	<li><b>Both values must be positive</b>. If a negative value is given it will be interpreted as "no limit".</li>
 	 * </ul>
-	 * 
+	 *
 	 * @return	NULL if no limit must be set, or a two-items array ([0]: default value, [1]: maximum value).
 	 */
 	public int[] getExecutionDuration();
@@ -296,12 +329,12 @@ public interface ServiceConnection {
 	/**
 	 * <i>[OPTIONAL]</i>
 	 * <p>Get the limit of the job execution result.</p>
-	 * 
+	 *
 	 * <p>
 	 * 	This value will limit the size of the query results, either in rows or in bytes.
 	 * 	The type of limit is defined by the function {@link #getOutputLimitType()}.
 	 * </p>
-	 * 
+	 *
 	 * <p><b>Important notes:</b></p>
 	 * <ul>
 	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
@@ -316,16 +349,16 @@ public interface ServiceConnection {
 	 * 	<li><b>The default value</b> MUST be less or equals the maximum value.</li>
 	 * 	<li><b>Both values must be positive</b>. If a negative value is given it will be interpreted as "no limit".</li>
 	 * </ul>
-	 * 
+	 *
 	 * <p><i><b>Important note:</b>
 	 * 	Currently, the default implementations of the library is only able to deal with output limits in ROWS.<br/>
 	 * 	Anyway, in order to save performances, it is strongly recommended to use ROWS limit rather than in bytes. Indeed, the rows limit can be taken
 	 * 	into account at the effective execution of the query (so before getting the result), on the contrary of the bytes limit which
 	 * 	will be applied on the query result.
 	 * </i></p>
-	 * 
+	 *
 	 * @return	NULL if no limit must be set, or a two-items array ([0]: default value, [1]: maximum value).
-	 * 
+	 *
 	 * @see #getOutputLimitType()
 	 */
 	public int[] getOutputLimit();
@@ -333,7 +366,7 @@ public interface ServiceConnection {
 	/**
 	 * <i>[OPTIONAL]</i>
 	 * <p>Get the type of each output limit set by this service connection (and accessible with {@link #getOutputLimit()}).</p>
-	 * 
+	 *
 	 * <p><b>Important notes:</b></p>
 	 * <ul>
 	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
@@ -341,16 +374,16 @@ public interface ServiceConnection {
 	 * 	<li><b>The structure of the object</b> returned by this function MUST be the same as the object returned by {@link #getOutputLimit()}.
 	 * 	    Particularly, the type given by the N-th item of this function must correspond to the N-th limit returned by {@link #getOutputLimit()}.</li>
 	 * </ul>
-	 * 
+	 *
 	 * <p><i><b>Important note:</b>
 	 * 	Currently, the default implementations of the library is only able to deal with output limits in ROWS.<br/>
 	 * 	Anyway, in order to save performances, it is strongly recommended to use ROWS limit rather than in bytes. Indeed, the rows limit can be taken
 	 * 	into account at the effective execution of the query (so before getting the result), on the contrary of the bytes limit which
 	 * 	will be applied on the query result.
 	 * </i></p>
-	 * 
-	 * @return	NULL if limits should be expressed in ROWS, or a two-items array ([0]: type of getOutputLimit()[0], [1]: type of getOutputLimit()[1]). 
-	 * 
+	 *
+	 * @return	NULL if limits should be expressed in ROWS, or a two-items array ([0]: type of getOutputLimit()[0], [1]: type of getOutputLimit()[1]).
+	 *
 	 * @see #getOutputLimit()
 	 */
 	public LimitUnit[] getOutputLimitType();
@@ -358,111 +391,131 @@ public interface ServiceConnection {
 	/**
 	 * <i>[OPTIONAL]</i>
 	 * <p>Get the object to use in order to identify users at the origin of requests.</p>
-	 * 
+	 *
 	 * @return	NULL if no user identification should be done, a {@link UserIdentifier} instance otherwise.
 	 */
 	public UserIdentifier getUserIdentifier();
 
 	/**
 	 * <i><b>[MANDATORY]</b></i>
-	 * <p>This function let enable or disable the upload capability of this TAP service.</p>
-	 * 
+	 * <p>This function lets enable or disable the upload capability of this TAP
+	 * service.</p>
+	 *
 	 * <p><i>Note:
-	 * 	If the upload is disabled, the request is aborted and an HTTP-400 error is thrown each time some tables are uploaded.
+	 * 	If the upload is disabled, the request is aborted and an HTTP-400 error
+	 * 	is thrown each time some tables are uploaded.
 	 * </i></p>
-	 * 
-	 * @return	<i>true</i> to enable the upload capability, <i>false</i> to disable it.
+	 *
+	 * @return	<i>true</i> to enable the upload capability,
+	 *        	<i>false</i> to disable it.
 	 */
 	public boolean uploadEnabled();
 
 	/**
 	 * <i>[OPTIONAL]</i>
 	 * <p>Get the maximum size of EACH uploaded table.</p>
-	 * 
+	 *
 	 * <p>
 	 * 	This value is expressed either in rows or in bytes.
 	 * 	The unit limit is defined by the function {@link #getUploadLimitType()}.
 	 * </p>
-	 * 
+	 *
 	 * <p><b>Important notes:</b></p>
 	 * <ul>
 	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
 	 * 	<li><b>If NULL</b>, the upload limit is not limited and uploads could be
 	 * 	    theoretically unlimited.</li>
-	 * 	<li><b>If not NULL</b>, the 2 values must correspond to the default upload limit
-	 * 	    and the maximum upload limit.</li>
-	 * 	<li><b>The default value</b> is used inform the user about the server wishes.</li>
-	 * 	<li><b>The maximum value</b> is used to really limit the upload limit.</li>
-	 * 	<li><b>The structure of the object</b> returned by this function MUST be the same as the object returned by {@link #getUploadLimitType()}.
-	 * 	    Particularly, the type given by the N-th item of {@link #getUploadLimitType()} must correspond to the N-th limit returned by this function.</li>
-	 * 	<li><b>The default value</b> MUST be less or equals the maximum value.</li>
-	 * 	<li><b>Both values must be positive</b>. If a negative value is given it will be interpreted as "no limit".</li>
+	 * 	<li><b>If not NULL</b>, the 2 values must correspond to the default
+	 * 	    upload limit and the maximum upload limit.</li>
+	 * 	<li><b>The default value</b> is used to inform the user about the server
+	 * 	    wishes. <i><b>In this version of TAPLib, this value is never used,
+	 * 		whatever is its value.</b> It is defined here only in case the
+	 * 		TAP protocol evolves and allow a such default value for UPLOAD
+	 * 		limits.</i></li>
+	 * 	<li><b>The maximum value</b> is used to really limit the upload
+	 * 	    limit.</li>
+	 * 	<li><b>The structure of the object</b> returned by this function MUST be
+	 * 	    the same as the object returned by {@link #getUploadLimitType()}.
+	 * 	    Particularly, the type given by the N-th item of
+	 * 	    {@link #getUploadLimitType()} must correspond to the N-th limit
+	 * 	    returned by this function.</li>
+	 * 	<li><b>The default value</b> MUST be less or equals the maximum
+	 * 	    value.</li>
+	 * 	<li><b>Both values must be positive</b>. If a negative value is given it
+	 * 	    will be interpreted as "no limit".</li>
 	 * </ul>
-	 * 
+	 *
 	 * <p><i><b>Important note:</b>
-	 * 	To save performances, it is recommended to use BYTES limit rather than in rows. Indeed, the bytes limit can be taken
-	 * 	into account at directly when reading the bytes of the request, on the contrary of the rows limit which
-	 * 	requires to parse the uploaded tables.
+	 * 	To save performances, it is recommended to use BYTES limit rather than
+	 * 	in rows. Indeed, the bytes limit can be taken into account at directly
+	 * 	when reading the bytes of the request, on the contrary of the rows limit
+	 * 	which requires to parse the uploaded tables.
 	 * </i></p>
-	 * 
-	 * @return	NULL if no limit must be set, or a two-items array ([0]: default value, [1]: maximum value).
-	 * 
+	 *
+	 * @return	NULL if no limit must be set,
+	 *        	or a two-items array ([0]: default value, [1]: maximum value).
+	 *
 	 * @see #getUploadLimitType()
 	 */
-	public int[] getUploadLimit();
+	public long[] getUploadLimit();
 
 	/**
 	 * <i>[OPTIONAL]</i>
-	 * <p>Get the type of each upload limit set by this service connection (and accessible with {@link #getUploadLimit()}).</p>
-	 * 
+	 * <p>Get the type of each upload limit set by this service connection (and
+	 * accessible with {@link #getUploadLimit()}).</p>
+	 *
 	 * <p><b>Important notes:</b></p>
 	 * <ul>
 	 * 	<li><b>Exactly 2 values or a NULL object is expected here.</b></li>
-	 * 	<li><b>If NULL</b>, the upload limit will be considered as expressed in ROWS.</li>
-	 * 	<li><b>The structure of the object</b> returned by this function MUST be the same as the object returned by {@link #getUploadLimit()}.
-	 * 	    Particularly, the type given by the N-th item of this function must correspond to the N-th limit returned by {@link #getUploadLimit()}.</li>
+	 * 	<li><b>If NULL</b>, the upload limit will be considered as expressed in
+	 * 	    ROWS.</li>
+	 * 	<li><b>The structure of the object</b> returned by this function MUST be
+	 * 	    the same as the object returned by {@link #getUploadLimit()}.
+	 * 	    Particularly, the type given by the N-th item of this function must
+	 * 	    correspond to the N-th limit returned by {@link #getUploadLimit()}.
+	 * 	</li>
 	 * </ul>
-	 * 
+	 *
 	 * <p><i><b>Important note:</b>
-	 * 	To save performances, it is recommended to use BYTES limit rather than in rows. Indeed, the bytes limit can be taken
-	 * 	into account at directly when reading the bytes of the request, on the contrary of the rows limit which
-	 * 	requires to parse the uploaded tables.
+	 * 	To save performances, it is recommended to use BYTES limit rather than
+	 * 	in rows. Indeed, the bytes limit can be taken into account at directly
+	 * 	when reading the bytes of the request, on the contrary of the rows limit
+	 * 	which requires to parse the uploaded tables.
 	 * </i></p>
-	 * 
-	 * @return	NULL if limits should be expressed in ROWS, or a two-items array ([0]: type of getUploadLimit()[0], [1]: type of getUploadLimit()[1]). 
-	 * 
+	 *
+	 * @return	NULL if limits should be expressed in ROWS,
+	 *        	or a two-items array ([0]: type of getUploadLimit()[0],
+	 *        	[1]: type of getUploadLimit()[1]).
+	 *
 	 * @see #getUploadLimit()
 	 */
 	public LimitUnit[] getUploadLimitType();
 
 	/**
 	 * <i>[OPTIONAL]</i>
-	 * <p>Get the maximum size of the whole set of all tables uploaded in one request.
-	 * This size is expressed in bytes.</p>
-	 * 
-	 * <p><b>IMPORTANT 1:
-	 * 	This value is always used when the upload capability is enabled.
-	 * </b></p>
-	 * 
-	 * <p><b>IMPORTANT 2:
-	 * 	The value returned by this function MUST always be positive.
-	 * 	A zero or negative value will throw an exception later while
-	 * 	reading parameters in a request with some uploaded tables.
-	 * </b></p>
-	 * 
-	 * @return	A positive (&gt;0) value corresponding to the maximum number of bytes of all uploaded tables sent in one request.
+	 * <p>Get the maximum size of the whole set of all tables uploaded in one
+	 * request. This size is expressed in bytes.</p>
+	 *
+	 * <p><i><b>NOTE:</b>
+	 * 	This value can be negative. In such case, there will be no limit on the
+	 * 	size of an HTTP request.
+	 * </i></p>
+	 *
+	 * @return	A positive value (&gt;0) corresponding to the maximum number of
+	 *        	bytes of all uploaded tables sent in one request.
+	 *        	A negative value (&le;0) means "unlimited".
 	 */
-	public int getMaxUploadSize();
+	public long getMaxUploadSize();
 
 	/**
 	 * <i><b>[MANDATORY]</b></i>
 	 * <p>Get the list of all available tables and columns.</p>
-	 * 
+	 *
 	 * <p>
 	 * 	This object is really important since it lets the library check ADQL queries properly and set the good type
 	 * 	and formatting in the query results.
 	 * </p>
-	 *  
+	 *
 	 * @return	A TAPMetadata object. <b>NULL is not allowed and will throw a grave error at the service initialization.</b>
 	 */
 	public TAPMetadata getTAPMetadata();
@@ -470,18 +523,18 @@ public interface ServiceConnection {
 	/**
 	 * <i>[OPTIONAL]</i>
 	 * <p>Get the list of all allowed coordinate systems.</p>
-	 * 
+	 *
 	 * <u><b>Special values</b></u>
-	 * 
+	 *
 	 * <p>Two special values can be returned by this function:</p>
 	 * <ul>
 	 * 	<li><b>NULL</b> which means that all coordinate systems are allowed,</li>
 	 * 	<li><b>the empty list</b> which means that no coordinate system - except
 	 * 	    the default one (which can be reduced to an empty string) - is allowed.</li>
 	 * </ul>
-	 * 
+	 *
 	 * <u><b>List item syntax</b></u>
-	 * 
+	 *
 	 * <p>
 	 * 	Each item of this list is a <b>pattern</b> and not a simple coordinate system.
 	 * 	Thus each item MUST respect the following syntax:
@@ -500,12 +553,12 @@ public interface ServiceConnection {
 	 * 	For instance: <code>(ICRS|FK4) HELIOCENTER *</code> is a good syntax,
 	 * 	but not <code>ICRS</code> or <code>ICRS HELIOCENTER</code>.
 	 * </p>
-	 * 
+	 *
 	 * <p><i>Note:
 	 * 	Even if not explicitly part of the possible values, the default value of each part (i.e. UNKNOWNFRAME for frame) is always taken into account by the library.
 	 * 	Particularly, the empty string will always be allowed even if not explicitly listed in the list returned by this function.
 	 * </i></p>
-	 * 
+	 *
 	 * @return	NULL to allow ALL coordinate systems, an empty list to allow NO coordinate system,
 	 *        	or a list of coordinate system patterns otherwise.
 	 */
@@ -514,27 +567,27 @@ public interface ServiceConnection {
 	/**
 	 * <i>[OPTIONAL]</i>
 	 * <p>Get the list of all allowed geometrical functions.</p>
-	 * 
+	 *
 	 * <u><b>Special values</b></u>
-	 * 
+	 *
 	 * <p>Two special values can be returned by this function:</p>
 	 * <ul>
 	 * 	<li><b>NULL</b> which means that all geometrical functions are allowed,</li>
 	 * 	<li><b>the empty list</b> which means that no geometrical functions is allowed.</li>
 	 * </ul>
-	 * 
+	 *
 	 * <u><b>List item syntax</b></u>
-	 * 
+	 *
 	 * <p>
 	 * 	Each item of the returned list MUST be a function name (i.e. "CONTAINS", "POINT").
 	 * 	It can also be a type of STC region to forbid (i.e. "POSITION", "UNION").
 	 * </p>
-	 * 
+	 *
 	 * <p>The given names are not case sensitive.</p>
-	 * 
+	 *
 	 * @return	NULL to allow ALL geometrical functions, an empty list to allow NO geometrical function,
 	 *        	or a list of geometrical function names otherwise.
-	 * 
+	 *
 	 * @since 2.0
 	 */
 	public Collection<String> getGeometries();
@@ -542,37 +595,37 @@ public interface ServiceConnection {
 	/**
 	 * <i>[OPTIONAL]</i>
 	 * <p>Get the list of all allowed User Defined Functions (UDFs).</p>
-	 * 
+	 *
 	 * <u><b>Special values</b></u>
-	 * 
+	 *
 	 * <p>Two special values can be returned by this function:</p>
 	 * <ul>
 	 * 	<li><b>NULL</b> which means that all unknown functions (which should be UDFs) are allowed,</li>
 	 * 	<li><b>the empty list</b> which means that no unknown functions (which should be UDFs) is allowed.</li>
 	 * </ul>
-	 * 
+	 *
 	 * <u><b>List item syntax</b></u>
-	 * 
+	 *
 	 * <p>
 	 * 	Each item of the returned list MUST be an instance of {@link FunctionDef}.
 	 * </p>
-	 * 
+	 *
 	 * @return	NULL to allow ALL unknown functions, an empty list to allow NO unknown function,
 	 *        	or a list of user defined functions otherwise.
-	 * 
+	 *
 	 * @since 2.0
 	 */
 	public Collection<FunctionDef> getUDFs();
 
 	/**
 	 * <i>[OPTIONAL]</i>
-	 * 
+	 *
 	 * <p>Get the maximum number of asynchronous jobs that can run in the same time.</p>
-	 * 
-	 * <p>A null or negative value means <b>no limit</b> on the number of running asynchronous jobs.</p> 
-	 * 
+	 *
+	 * <p>A null or negative value means <b>no limit</b> on the number of running asynchronous jobs.</p>
+	 *
 	 * @return	Maximum number of running jobs (&le;0 => no limit).
-	 * 
+	 *
 	 * @since 2.0
 	 */
 	public int getNbMaxAsyncJobs();
@@ -580,16 +633,16 @@ public interface ServiceConnection {
 	/**
 	 * <i><b>[MANDATORY]</b></i>
 	 * <p>Get the logger to use in the whole service when any error, warning or info happens.</p>
-	 * 
+	 *
 	 * <p><b>IMPORTANT:
 	 * 	If NULL is returned by this function, grave errors will occur while executing a query or managing an error.
 	 * 	It is strongly recommended to provide a logger, even a basic implementation.
 	 * </b></p>
-	 * 
+	 *
 	 * <p><i>Piece of advice:
 	 * 	A default implementation like {@link DefaultTAPLog} would be most of time largely enough.
 	 * </i></p>
-	 * 
+	 *
 	 * @return	An instance of {@link TAPLog}.
 	 */
 	public TAPLog getLogger();
@@ -597,20 +650,20 @@ public interface ServiceConnection {
 	/**
 	 * <i><b>[MANDATORY]</b></i>
 	 * <p>Get the object able to build other objects essentials to configure the TAP service or to run every queries.</p>
-	 * 
+	 *
 	 * <p><b>IMPORTANT:
 	 * 	If NULL is returned by this function, grave errors will occur while initializing the service.
 	 * </b></p>
-	 * 
+	 *
 	 * <p><i>Piece of advice:
 	 * 	The {@link TAPFactory} is an interface which contains a lot of functions to implement.
 	 * 	It is rather recommended to extend {@link AbstractTAPFactory}: just 2 functions
 	 * 	({@link AbstractTAPFactory#freeConnection(DBConnection)} and {@link AbstractTAPFactory#getConnection(String)})
-	 * 	will have to be implemented. 
+	 * 	will have to be implemented.
 	 * </i></p>
-	 * 
+	 *
 	 * @return	An instance of {@link TAPFactory}.
-	 * 
+	 *
 	 * @see AbstractTAPFactory
 	 */
 	public TAPFactory getFactory();
@@ -619,16 +672,16 @@ public interface ServiceConnection {
 	 * <i><b>[MANDATORY]</b></i>
 	 * <p>Get the object in charge of the files management.
 	 * This object manages log, error, result and backup files of the whole service.</p>
-	 * 
+	 *
 	 * <p><b>IMPORTANT:
 	 * 	If NULL is returned by this function, grave errors will occur while initializing the service.
 	 * </b></p>
-	 * 
+	 *
 	 * <p><i>Piece of advice:
 	 * 	The library provides a default implementation of the interface {@link UWSFileManager}:
 	 * 	{@link LocalUWSFileManager}, which stores all files on the local file-system.
 	 * </i></p>
-	 * 
+	 *
 	 * @return	An instance of {@link UWSFileManager}.
 	 */
 	public UWSFileManager getFileManager();
@@ -636,14 +689,14 @@ public interface ServiceConnection {
 	/**
 	 * <i><b>[MANDATORY]</b></i>
 	 * <p>Get the list of all available output formats.</p>
-	 * 
+	 *
 	 * <p><b>IMPORTANT:</b></p>
 	 * <ul>
 	 * 	<li>All formats of this list MUST have a different MIME type.</li>
 	 * 	<li>At least one item must correspond to the MIME type "votable".</li>
 	 * 	<li>If NULL is returned by this function, grave errors will occur while writing the capabilities of this service.</li>
 	 * </li>
-	 * 
+	 *
 	 * @return	An iterator on the list of all available output formats.
 	 */
 	public Iterator<OutputFormat> getOutputFormats();
@@ -651,13 +704,13 @@ public interface ServiceConnection {
 	/**
 	 * <i><b>[MANDATORY]</b></i>
 	 * <p>Get the output format having the given MIME type (or short MIME type ~ alias).</p>
-	 * 
+	 *
 	 * <p><b>IMPORTANT:
 	 * 	This function MUST always return an {@link OutputFormat} instance when the MIME type "votable" is given in parameter.
 	 * </b></p>
-	 * 
+	 *
 	 * @param mimeOrAlias	MIME type or short MIME type of the format to get.
-	 * 
+	 *
 	 * @return	The corresponding {@link OutputFormat} or NULL if not found.
 	 */
 	public OutputFormat getOutputFormat(final String mimeOrAlias);
@@ -665,28 +718,28 @@ public interface ServiceConnection {
 	/**
 	 * <i>[OPTIONAL]</i>
 	 * <p>Get the size of result blocks to fetch from the database.</p>
-	 * 
+	 *
 	 * <p>
 	 * 	Rather than fetching a query result in a whole, it may be possible to specify to the database
 	 * 	that results may be retrieved by blocks whose the size can be specified by this function.
 	 * 	If supported by the DBMS and the JDBC driver, this feature may help sparing memory and avoid
 	 * 	too much waiting time from the TAP /sync users (and thus, avoiding some HTTP client timeouts).
 	 * </p>
-	 * 
+	 *
 	 * <p><i>Note:
 	 * 	Generally, this feature is well supported by DBMS. But for that, the used JDBC driver must use
 	 * 	the V3 protocol. If anyway, this feature is supported neither by the DBMS, the JDBC driver nor your
 	 * 	{@link DBConnection}, no error will be thrown if a value is returned by this function: it will be silently
 	 * 	ignored by the library.
 	 * </i></p>
-	 * 
+	 *
 	 * @return	<i>null</i> or an array of 1 or 2 integers.
 	 * 			If <i>null</i> (or empty array), no attempt to set fetch size will be done and so, ONLY the default
 	 *        	value of the {@link DBConnection} will be used.
 	 *        	[0]=fetchSize for async queries, [1]=fetchSize for sync queries.
 	 *        	If [1] is omitted, it will be considered as equals to [0].
 	 *        	If a fetchSize is negative or null, the default value of your JDBC driver will be used.
-	 * 
+	 *
 	 * @since 2.0
 	 */
 	public int[] getFetchSize();
diff --git a/src/tap/TAPFactory.java b/src/tap/TAPFactory.java
index d7974ab3a6bf2b9d6e28be697085db8d449ce102..26fb3ef91714d0ea4aec08c8467189828fce868a 100644
--- a/src/tap/TAPFactory.java
+++ b/src/tap/TAPFactory.java
@@ -16,7 +16,7 @@ package tap;
  * You should have received a copy of the GNU Lesser General Public License
  * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
  *
- * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ * Copyright 2012-2018 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
@@ -29,6 +29,7 @@ import adql.parser.ADQLParser;
 import adql.parser.ADQLQueryFactory;
 import adql.parser.QueryChecker;
 import adql.query.ADQLQuery;
+import tap.ServiceConnection.LimitUnit;
 import tap.db.DBConnection;
 import tap.metadata.TAPSchema;
 import tap.parameters.TAPParameters;
@@ -63,7 +64,7 @@ import uws.service.request.RequestParser;
  * </ul>
  *
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.2 (09/2017)
+ * @version 2.3 (09/2018)
  */
 public abstract class TAPFactory implements UWSFactory {
 
@@ -457,7 +458,7 @@ public abstract class TAPFactory implements UWSFactory {
 	 * @see #createTAPParameters(Map)
 	 */
 	@Override
-	public final UWSParameters createUWSParameters(Map<String,Object> params) throws UWSException{
+	public final UWSParameters createUWSParameters(Map<String, Object> params) throws UWSException{
 		try{
 			return createTAPParameters(params);
 		}catch(TAPException te){
@@ -481,11 +482,18 @@ public abstract class TAPFactory implements UWSFactory {
 	 *
 	 * @throws TAPException	If any error occurs while creating the {@link TAPParameters} object.
 	 */
-	public abstract TAPParameters createTAPParameters(final Map<String,Object> params) throws TAPException;
+	public abstract TAPParameters createTAPParameters(final Map<String, Object> params) throws TAPException;
 
 	@Override
 	public RequestParser createRequestParser(final UWSFileManager fileManager) throws UWSException{
-		return new TAPRequestParser(fileManager);
+		/* Fetch the maximum size for each uploaded file:
+		 * ....only if set to a positive value and expressed in bytes. */
+		long maxFileSize = -1;
+		if (service.getUploadLimit() != null && service.getUploadLimit().length >= 2 && service.getUploadLimit()[1] > 0 && service.getUploadLimitType() != null && service.getUploadLimitType().length == service.getUploadLimit().length && service.getUploadLimitType()[1] != LimitUnit.rows){
+			maxFileSize = service.getUploadLimit()[1] * service.getUploadLimitType()[1].bytesFactor();
+		}
+		// Finally create the appropriate RequestParser:
+		return new TAPRequestParser(fileManager, service.uploadEnabled(), maxFileSize, service.getMaxUploadSize());
 	}
 
 }
diff --git a/src/tap/TAPRequestParser.java b/src/tap/TAPRequestParser.java
index ba14f935b2428ad159c9f157d2e42fd81e34ffb4..8807761ed726c418932672ac89ff5632e71e45db 100644
--- a/src/tap/TAPRequestParser.java
+++ b/src/tap/TAPRequestParser.java
@@ -2,20 +2,20 @@ package tap;
 
 /*
  * This file is part of TAPLibrary.
- * 
+ *
  * TAPLibrary is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
- * 
+ *
  * TAPLibrary is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU Lesser General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Lesser General Public License
  * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
- * 
+ *
  * Copyright 2014-2017 - Astronomisches Rechen Institut (ARI)
  */
 
@@ -36,26 +36,26 @@ import uws.service.request.UploadFile;
 /**
  * This parser adapts the request parser to use in function of the request
  * content-type:
- * 
+ *
  * <ul>
  * 	<li><b>application/x-www-form-urlencoded</b>: {@link FormEncodedParser}</li>
  * 	<li><b>multipart/form-data</b>: {@link MultipartParser}</li>
  * 	<li><b>other</b>: no parameter is returned</li>
  * </ul>
- * 
+ *
  * <p>
- * 	The request body size is limited for the multipart. If you want to change
- * 	this limit, you MUST do it for each of these parsers, setting the following
- * 	static attributes: {@link MultipartParser#SIZE_LIMIT}.
+ * 	The request body size is limited for the multipart. You can change these
+ * 	limits with the constructor
+ * 	{@link #TAPRequestParser(UWSFileManager, boolean, long, long)}
  * </p>
- * 
+ *
  * <p><i>Note:
- * 	If you want to change the support other request parsing, you will have to
- * 	write your own {@link RequestParser} implementation.
+ * 	If you want to support other request parsing, you have to write your own
+ * 	{@link RequestParser} implementation.
  * </i></p>
- * 
- * @author Gr&eacute;gory Mantelet (ARI)
- * @version 2.1 (06/2017)
+ *
+ * @author Gr&eacute;gory Mantelet (ARI;CDS)
+ * @version 2.3 (09/2018)
  * @since 2.0
  */
 public class TAPRequestParser implements RequestParser {
@@ -76,29 +76,66 @@ public class TAPRequestParser implements RequestParser {
 	 * only when needed, by calling the function {@link #getMultipartParser()}. */
 	private RequestParser multipartParser = null;
 
+	/** Indicates whether this parser should allow uploaded files.
+	 * @since 2.3 */
+	private final boolean allowUpload;
+
+	/** Maximum size of a single uploaded file.
+	 * <p><i><b>Note:</b> If negative (&le;0), "unlimited".</i></p>
+	 * @since 2.3 */
+	private final long maxFileSize;
+
+	/** Maximum size of a whole HTTP Multipart Request.
+	 * <p><i><b>Note:</b> If negative (&le;0), "unlimited".</i></p>
+	 * @since 2.3 */
+	private final long maxMultipartSize;
+
 	/**
 	 * Build a {@link RequestParser} able to choose the most appropriate
 	 * {@link RequestParser} in function of the request content-type.
-	 * 
+	 *
 	 * @param fileManager	The file manager to use in order to store any
 	 *                   	eventual upload. <b>MUST NOT be NULL</b>
 	 */
 	public TAPRequestParser(final UWSFileManager fileManager){
+		this(fileManager, true, -1, -1);
+	}
+
+	/**
+	 * Build a {@link RequestParser} able to choose the most appropriate
+	 * {@link RequestParser} in function of the request content-type.
+	 *
+	 * @param fileManager		The file manager to use in order to store any
+	 *                   		eventual upload. <b>MUST NOT be NULL</b>
+	 * @param uploadEnabled		<code>true</code> to support UPLOADs,
+	 *                     		<code>false</code> otherwise (i.e. all Multipart
+	 *                     		requests with files will be rejected).
+	 * @param maxFileSize		Maximum size for a single uploaded file.
+	 *                   		<i>A negative value means "no limit".</i>
+	 * @param maxRequestSize	Maximum size for a whole Multipart HTTP Request.
+	 *                   		<i>A negative value means "no limit".</i>
+	 *
+	 * @since 2.3
+	 */
+	public TAPRequestParser(final UWSFileManager fileManager, final boolean uploadEnabled, final long maxFileSize, final long maxRequestSize){
 		if (fileManager == null)
 			throw new NullPointerException("Missing file manager => can not create a TAPRequestParser!");
 		this.fileManager = fileManager;
+		this.allowUpload = uploadEnabled;
+		this.maxFileSize = maxFileSize;
+		this.maxMultipartSize = maxRequestSize;
 	}
 
 	@Override
-	public Map<String,Object> parse(final HttpServletRequest req) throws UWSException{
+	public Map<String, Object> parse(final HttpServletRequest req) throws UWSException{
 		if (req == null)
-			return new HashMap<String,Object>();
+			return new HashMap<String, Object>();
 
 		// Get the method:
 		String method = (req.getMethod() == null) ? "" : req.getMethod().toLowerCase();
 
 		if (method.equals("post") || method.equals("put")){
-			Map<String,Object> params = null;
+			Map<String, Object> params = null;
 
 			// Get the parameters:
 			if (FormEncodedParser.isFormEncodedRequest(req))
@@ -106,29 +143,29 @@ public class TAPRequestParser implements RequestParser {
 			else if (MultipartParser.isMultipartContent(req))
 				params = getMultipartParser().parse(req);
 			else
-				params = new HashMap<String,Object>(0);
+				params = new HashMap<String, Object>(0);
 
 			// Only for POST requests, the parameters specified in the URL must be added:
 			if (method.equals("post"))
-				params = UWSToolBox.addGETParameters(req, (params == null) ? new HashMap<String,Object>() : params);
+				params = UWSToolBox.addGETParameters(req, (params == null) ? new HashMap<String, Object>() : params);
 
 			return params;
 		}else
-			return UWSToolBox.addGETParameters(req, new HashMap<String,Object>());
+			return UWSToolBox.addGETParameters(req, new HashMap<String, Object>());
 	}
 
 	/**
 	 * Get the {@link RequestParser} to use for
 	 * application/x-www-form-urlencoded HTTP requests.
 	 * This parser may be created if not already done.
-	 * 
+	 *
 	 * @return	The {@link RequestParser} to use for
 	 *        	application/x-www-form-urlencoded requests. <i>Never NULL</i>
 	 */
 	private synchronized final RequestParser getFormParser(){
-		return (formParser != null) ? formParser : (formParser = new FormEncodedParser(){
+		return (formParser != null) ? formParser : (formParser = new FormEncodedParser() {
 			@Override
-			protected void consumeParameter(String name, Object value, final Map<String,Object> allParams){
+			protected void consumeParameter(String name, Object value, final Map<String, Object> allParams){
 				// Modify the value if it is an UPLOAD parameter:
 				if (name != null && name.equalsIgnoreCase("upload")){
 					// if no value, ignore this parameter:
@@ -150,14 +187,14 @@ public class TAPRequestParser implements RequestParser {
 	 * Get the {@link RequestParser} to use for multipart/form-data HTTP
 	 * requests.
 	 * This parser may be created if not already done.
-	 * 
+	 *
 	 * @return	The {@link RequestParser} to use for multipart/form-data
 	 *        	requests. <i>Never NULL</i>
 	 */
 	private synchronized final RequestParser getMultipartParser(){
-		return (multipartParser != null) ? multipartParser : (multipartParser = new MultipartParser(fileManager){
+		return (multipartParser != null) ? multipartParser : (multipartParser = new MultipartParser(allowUpload, fileManager, maxFileSize, maxMultipartSize) {
 			@Override
-			protected void consumeParameter(String name, Object value, final Map<String,Object> allParams){
+			protected void consumeParameter(String name, Object value, final Map<String, Object> allParams){
 				// Modify the value if it is an UPLOAD parameter:
 				if (name != null && name.equalsIgnoreCase(TAPJob.PARAM_UPLOAD)){
 					// if no value, ignore this parameter:
@@ -167,7 +204,8 @@ public class TAPRequestParser implements RequestParser {
 					else if (name.equals(TAPJob.PARAM_UPLOAD) && value instanceof UploadFile){
 						try{
 							((UploadFile)value).deleteFile();
-						}catch(IOException ioe){}
+						}catch(IOException ioe){
+						}
 						return;
 					}
 					// use the same case for the parameter name:
@@ -185,10 +223,10 @@ public class TAPRequestParser implements RequestParser {
 	/**
 	 * Create a new array in which the given String is appended at the end of
 	 * the given array.
-	 * 
+	 *
 	 * @param value		String to append in the array.
 	 * @param oldValue	The array after which the given String must be appended.
-	 * 
+	 *
 	 * @return	The new array containing the values of the array and then the
 	 *        	given String.
 	 */
diff --git a/src/tap/config/ConfigurableServiceConnection.java b/src/tap/config/ConfigurableServiceConnection.java
index 3fe6d2fb091703c498d2e37f832f66e944a529b0..4f979337f8448d3acb08bfbc89b522910014f116 100644
--- a/src/tap/config/ConfigurableServiceConnection.java
+++ b/src/tap/config/ConfigurableServiceConnection.java
@@ -16,7 +16,8 @@ package tap.config;
  * You should have received a copy of the GNU Lesser General Public License
  * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
  *
- * Copyright 2016-2018 - Astronomisches Rechen Institut (ARI)
+ * Copyright 2016-2018 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import static tap.config.TAPConfiguration.DEFAULT_ASYNC_FETCH_SIZE;
@@ -25,9 +26,10 @@ import static tap.config.TAPConfiguration.DEFAULT_EXECUTION_DURATION;
 import static tap.config.TAPConfiguration.DEFAULT_GROUP_USER_DIRECTORIES;
 import static tap.config.TAPConfiguration.DEFAULT_LOGGER;
 import static tap.config.TAPConfiguration.DEFAULT_MAX_ASYNC_JOBS;
+import static tap.config.TAPConfiguration.DEFAULT_MAX_UPLOAD_LIMIT;
 import static tap.config.TAPConfiguration.DEFAULT_RETENTION_PERIOD;
 import static tap.config.TAPConfiguration.DEFAULT_SYNC_FETCH_SIZE;
-import static tap.config.TAPConfiguration.DEFAULT_UPLOAD_MAX_FILE_SIZE;
+import static tap.config.TAPConfiguration.DEFAULT_UPLOAD_MAX_REQUEST_SIZE;
 import static tap.config.TAPConfiguration.KEY_ASYNC_FETCH_SIZE;
 import static tap.config.TAPConfiguration.KEY_COORD_SYS;
 import static tap.config.TAPConfiguration.KEY_DEFAULT_EXECUTION_DURATION;
@@ -56,7 +58,7 @@ import static tap.config.TAPConfiguration.KEY_SYNC_FETCH_SIZE;
 import static tap.config.TAPConfiguration.KEY_TAP_FACTORY;
 import static tap.config.TAPConfiguration.KEY_UDFS;
 import static tap.config.TAPConfiguration.KEY_UPLOAD_ENABLED;
-import static tap.config.TAPConfiguration.KEY_UPLOAD_MAX_FILE_SIZE;
+import static tap.config.TAPConfiguration.KEY_UPLOAD_MAX_REQUEST_SIZE;
 import static tap.config.TAPConfiguration.KEY_USER_IDENTIFIER;
 import static tap.config.TAPConfiguration.SLF4J_LOGGER;
 import static tap.config.TAPConfiguration.VALUE_ALL;
@@ -130,8 +132,8 @@ import uws.service.log.UWSLog.LogLevel;
  * 	TAP configuration file thanks to the implementation {@link ConfigurableTAPFactory}.
  * </p>
  *
- * @author Gr&eacute;gory Mantelet (ARI)
- * @version 2.3 (04/2018)
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 2.3 (09/2018)
  * @since 2.0
  */
 public final class ConfigurableServiceConnection implements ServiceConnection {
@@ -180,13 +182,15 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
 	/** Indicate whether the UPLOAD feature is enabled or not. */
 	private boolean isUploadEnabled = false;
 	/** Array of 2 integers: resp. default and maximum upload limit.
-	 * <em>Each limit is expressed in a unit specified in the array {@link #uploadLimitTypes}.</em> */
-	private int[] uploadLimits = new int[]{ -1, -1 };
-	/** Array of 2 limit units: resp. unit of the default upload limit and unit of the maximum upload limit. */
+	 * <p><em>Each limit is expressed in a unit specified in the array
+	 * {@link #uploadLimitTypes}.</em></p> */
+	private long[] uploadLimits = new long[]{ -1L, -1L };
+	/** Array of 2 limit units: resp. unit of the default upload limit and unit
+	 * of the maximum upload limit. */
 	private LimitUnit[] uploadLimitTypes = new LimitUnit[2];
 	/** The maximum size of a set of uploaded files.
-	 * <em>This size is expressed in bytes.</em> */
-	private int maxUploadSize = DEFAULT_UPLOAD_MAX_FILE_SIZE;
+	 * <p><em>This size is expressed in bytes.</em></p> */
+	private long maxUploadSize = DEFAULT_UPLOAD_MAX_REQUEST_SIZE;
 
 	/** Array of 2 integers: resp. default and maximum fetch size.
 	 * <em>Both sizes are expressed in number of rows.</em> */
@@ -247,14 +251,14 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
 		// 4. GET THE METADATA:
 		metadata = initMetadata(tapConfig, webAppRootDir);
 
-		// 5. SET ALL GENERAL SERVICE CONNECTION INFORMATION:
+		// 6. SET ALL GENERAL SERVICE CONNECTION INFORMATION:
 		providerName = getProperty(tapConfig, KEY_PROVIDER_NAME);
 		serviceDescription = getProperty(tapConfig, KEY_SERVICE_DESCRIPTION);
 		initMaxAsyncJobs(tapConfig);
 		initRetentionPeriod(tapConfig);
 		initExecutionDuration(tapConfig);
 
-		// 6. CONFIGURE OUTPUT:
+		// 7. CONFIGURE OUTPUT:
 		// default output format = VOTable:
 		outputFormats = new ArrayList<OutputFormat>(1);
 		// set output formats:
@@ -264,7 +268,7 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
 		// set fetch size:
 		initFetchSize(tapConfig);
 
-		// 7. CONFIGURE THE UPLOAD:
+		// 8. CONFIGURE THE UPLOAD:
 		// is upload enabled ?
 		isUploadEnabled = Boolean.parseBoolean(getProperty(tapConfig, KEY_UPLOAD_ENABLED));
 		// set upload limits:
@@ -272,10 +276,10 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
 		// set the maximum upload file size:
 		initMaxUploadSize(tapConfig);
 
-		// 8. SET A USER IDENTIFIER:
+		// 9. SET A USER IDENTIFIER:
 		initUserIdentifier(tapConfig);
 
-		// 9. CONFIGURE ADQL:
+		// 10. CONFIGURE ADQL:
 		initCoordSys(tapConfig);
 		initADQLGeometries(tapConfig);
 		initUDFs(tapConfig);
@@ -954,45 +958,69 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
 	}
 
 	/**
-	 * Initialize the default and maximum upload limits.
+	 * Initialize the maximum upload limit.
+	 *
+	 * <p><em><b>Note:</b>
+	 * 	The default upload limit is still fetched in this function, but only
+	 * 	in case no maximum limit is provided, just for backward compatibility
+	 * 	with versions 2.2 or less.
+	 * </em></p>
 	 *
 	 * @param tapConfig	The content of the TAP configuration file.
 	 *
-	 * @throws TAPException	If the corresponding TAP configuration properties are wrong.
+	 * @throws TAPException	If the corresponding TAP configuration properties
+	 *                     	are wrong.
 	 */
 	private void initUploadLimits(final Properties tapConfig) throws TAPException{
-		Object[] limit = parseLimit(getProperty(tapConfig, KEY_DEFAULT_UPLOAD_LIMIT), KEY_DEFAULT_UPLOAD_LIMIT, true);
-		uploadLimitTypes[0] = (LimitUnit)limit[1];
-		setDefaultUploadLimit((Integer)limit[0]);
+		// Fetch the given default and maximum limits:
+		String defaultDBLimit = getProperty(tapConfig, KEY_DEFAULT_UPLOAD_LIMIT);
+		String maxDBLimit = getProperty(tapConfig, KEY_MAX_UPLOAD_LIMIT);
+		Object[] limit = null;
+
+		/* Parse the given maximum limit. */
+		if (maxDBLimit != null)
+			limit = parseLimit(maxDBLimit, KEY_MAX_UPLOAD_LIMIT, true, true);
 
-		limit = parseLimit(getProperty(tapConfig, KEY_MAX_UPLOAD_LIMIT), KEY_MAX_UPLOAD_LIMIT, true);
-		if (!((LimitUnit)limit[1]).isCompatibleWith(uploadLimitTypes[0]))
-			throw new TAPException("The default upload limit (in " + uploadLimitTypes[0] + ") and the maximum upload limit (in " + limit[1] + ") MUST be expressed in the same unit!");
+		/* If none is provided, try to use the deprecated default limit
+		 * (just for backward compatibility). */
+		else if (defaultDBLimit != null)
+			limit = parseLimit(defaultDBLimit, KEY_DEFAULT_UPLOAD_LIMIT, true, true);
+
+		/* If still no value is provided, set the default value. */
 		else
-			uploadLimitTypes[1] = (LimitUnit)limit[1];
-		setMaxUploadLimit((Integer)limit[0]);
+			limit = parseLimit(DEFAULT_MAX_UPLOAD_LIMIT, KEY_DEFAULT_UPLOAD_LIMIT, true, true);
+
+		// Finally, set the new limits:
+		uploadLimitTypes[0] = uploadLimitTypes[1] = (LimitUnit)limit[1];
+		setDefaultUploadLimit((Long)limit[0]);
+		setMaxUploadLimit((Long)limit[0]);
+
 	}
 
 	/**
-	 * Initialize the maximum size (in bytes) of a VOTable files set upload.
+	 * Initialize the maximum size (in bytes) of a whole HTTP Multipart request.
+	 *
+	 * <p><em><b>Note:</b>
+	 * 	This maximum size includes the HTTP header (normal parameters included)
+	 * 	and the sum of the size of all uploaded files.
+	 * </em></p>
 	 *
 	 * @param tapConfig	The content of the TAP configuration file.
 	 *
-	 * @throws TAPException	If the corresponding TAP configuration property is wrong.
+	 * @throws TAPException	If the corresponding TAP configuration property is
+	 *                     	wrong.
 	 */
 	private void initMaxUploadSize(final Properties tapConfig) throws TAPException{
-		String propValue = getProperty(tapConfig, KEY_UPLOAD_MAX_FILE_SIZE);
+		String propValue = getProperty(tapConfig, KEY_UPLOAD_MAX_REQUEST_SIZE);
 		// If a value is specified...
 		if (propValue != null){
 			// ...parse the value:
-			Object[] limit = parseLimit(propValue, KEY_UPLOAD_MAX_FILE_SIZE, true);
-			if (((Integer)limit[0]).intValue() <= 0)
-				limit[0] = new Integer(TAPConfiguration.DEFAULT_UPLOAD_MAX_FILE_SIZE);
+			Object[] limit = parseLimit(propValue, KEY_UPLOAD_MAX_REQUEST_SIZE, true, true);
 			// ...check that the unit is correct (bytes):
 			if (!LimitUnit.bytes.isCompatibleWith((LimitUnit)limit[1]))
-				throw new TAPException("The maximum upload file size " + KEY_UPLOAD_MAX_FILE_SIZE + " (here: " + propValue + ") can not be expressed in a unit different from bytes (B, kB, MB, GB)!");
-			// ...set the max file size:
-			int value = (int)((Integer)limit[0] * ((LimitUnit)limit[1]).bytesFactor());
+				throw new TAPException("The maximum upload request size " + KEY_UPLOAD_MAX_REQUEST_SIZE + " (here: " + propValue + ") can not be expressed in a unit different from bytes (B, kB, MB, GB)!");
+			// ...set the max request size:
+			long value = (Long)limit[0] * ((LimitUnit)limit[1]).bytesFactor();
 			setMaxUploadSize(value);
 		}
 	}
@@ -1483,7 +1511,7 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
 	}
 
 	@Override
-	public int[] getUploadLimit(){
+	public long[] getUploadLimit(){
 		return uploadLimits;
 	}
 
@@ -1503,18 +1531,43 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
 	}
 
 	/**
-	 * <p>Set the default upload limit.</p>
+	 * Set the default upload limit.
 	 *
 	 * <p><em><b>Important note:</b>
-	 * 	This function will apply the given upload limit only if legal compared to the currently set maximum value.
-	 * 	In other words, if the given value is less or equals to the current maximum upload limit.
+	 * 	This function will apply the given upload limit only if legal compared
+	 * 	to the currently set maximum value. In other words, if the given value
+	 * 	is less or equals to the current maximum upload limit.
 	 * </em></p>
 	 *
 	 * @param limit	New default upload limit.
 	 *
-	 * @return	<i>true</i> if the given upload limit has been successfully set, <i>false</i> otherwise.
+	 * @return	<i>true</i> if the given upload limit has been successfully set,
+	 *        	<i>false</i> otherwise.
+	 *
+	 * @deprecated	Since 2.3, use {@link #setDefaultUploadLimit(long)} instead.
 	 */
+	@Deprecated
 	public boolean setDefaultUploadLimit(final int limit){
+		return setDefaultUploadLimit((long)limit);
+	}
+
+	/**
+	 * Set the default upload limit.
+	 *
+	 * <p><em><b>Important note:</b>
+	 * 	This function will apply the given upload limit only if legal compared
+	 * 	to the currently set maximum value. In other words, if the given value
+	 * 	is less or equals to the current maximum upload limit.
+	 * </em></p>
+	 *
+	 * @param limit	New default upload limit.
+	 *
+	 * @return	<i>true</i> if the given upload limit has been successfully set,
+	 *        	<i>false</i> otherwise.
+	 *
+	 * @since 2.3
+	 */
+	public boolean setDefaultUploadLimit(final long limit){
 		try{
 			if ((uploadLimits[1] <= 0) || (limit > 0 && LimitUnit.compare(limit, uploadLimitTypes[0], uploadLimits[1], uploadLimitTypes[1]) <= 0)){
 				uploadLimits[0] = limit;
@@ -1526,19 +1579,41 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
 	}
 
 	/**
-	 * <p>Set the maximum upload limit.</p>
+	 * Set the maximum upload limit.
 	 *
 	 * <p>This upload limit limits the default upload limit.</p>
 	 *
 	 * <p><em><b>Important note:</b>
-	 * 	This function may reduce the default upload limit if the current default upload limit is bigger
-	 * 	to the new maximum upload limit. In a such case, the default upload limit is set to the
-	 * 	new maximum upload limit.
+	 * 	This function may reduce the default upload limit if the current default
+	 * 	upload limit is bigger to the new maximum upload limit. In a such case,
+	 * 	the default upload limit is set to the new maximum upload limit.
 	 * </em></p>
 	 *
 	 * @param limit	New maximum upload limit.
+	 *
+	 * @deprecated	Since 2.3, use {@link #setMaxUploadLimit(long)} instead.
 	 */
+	@Deprecated
 	public void setMaxUploadLimit(final int limit){
+		setMaxUploadLimit((long)limit);
+	}
+
+	/**
+	 * Set the maximum upload limit.
+	 *
+	 * <p>This upload limit limits the default upload limit.</p>
+	 *
+	 * <p><em><b>Important note:</b>
+	 * 	This function may reduce the default upload limit if the current default
+	 * 	upload limit is bigger to the new maximum upload limit. In a such case,
+	 * 	the default upload limit is set to the new maximum upload limit.
+	 * </em></p>
+	 *
+	 * @param limit	New maximum upload limit.
+	 *
+	 * @since 2.3
+	 */
+	public void setMaxUploadLimit(final long limit){
 		try{
 			// Decrease the default output limit if it will be bigger than the new maximum output limit:
 			if (limit > 0 && (uploadLimits[0] <= 0 || LimitUnit.compare(limit, uploadLimitTypes[1], uploadLimits[0], uploadLimitTypes[0]) < 0))
@@ -1550,24 +1625,28 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
 	}
 
 	@Override
-	public int getMaxUploadSize(){
+	public long getMaxUploadSize(){
 		return maxUploadSize;
 	}
 
 	/**
-	 * <p>Set the maximum size of a VOTable files set that can be uploaded in once.</p>
+	 * Set the maximum size of a VOTable files set that can be uploaded in once.
 	 *
 	 * <p><b>Warning:
-	 * 	This size can not be negative or 0. If the given value is in this case, nothing will be done
-	 * 	and <i>false</i> will be returned.
-	 * 	On the contrary to the other limits, no "unlimited" limit is possible here ; only the
+	 * 	This size can not be negative or 0. If the given value is in this case,
+	 * 	nothing will be done and <i>false</i> will be returned. On the contrary
+	 * 	to the other limits, no "unlimited" limit is possible here ; only the
 	 * 	maximum value can be set (i.e. maximum positive integer value).
 	 * </b></p>
 	 *
 	 * @param maxSize	New maximum size (in bytes).
 	 *
-	 * @return	<i>true</i> if the size has been successfully set, <i>false</i> otherwise.
+	 * @return	<i>true</i> if the size has been successfully set,
+	 *        	<i>false</i> otherwise.
+	 *
+	 * @deprecated	Since 2.3, use {@link #setMaxUploadSize(long)} instead.
 	 */
+	@Deprecated
 	public boolean setMaxUploadSize(final int maxSize){
 		// No "unlimited" value possible there:
 		if (maxSize <= 0)
@@ -1578,6 +1657,33 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
 		return true;
 	}
 
+	/**
+	 * Set the maximum size of a VOTable files set that can be uploaded in once.
+	 *
+	 * <p><b>Warning:
+	 * 	This size can not be negative or 0. If the given value is in this case,
+	 * 	nothing will be done and <i>false</i> will be returned. On the contrary
+	 * 	to the other limits, no "unlimited" limit is possible here ; only the
+	 * 	maximum value can be set (i.e. maximum positive integer value).
+	 * </b></p>
+	 *
+	 * @param maxSize	New maximum size (in bytes).
+	 *
+	 * @return	<i>true</i> if the size has been successfully set,
+	 *        	<i>false</i> otherwise.
+	 *
+	 * @since 2.3
+	 */
+	public boolean setMaxUploadSize(final long maxSize){
+		// No "unlimited" value possible there:
+		if (maxSize <= 0)
+			return false;
+
+		// Otherwise, set the maximum upload file size:
+		maxUploadSize = maxSize;
+		return true;
+	}
+
 	@Override
 	public int getNbMaxAsyncJobs(){
 		return maxAsyncJobs;
diff --git a/src/tap/config/TAPConfiguration.java b/src/tap/config/TAPConfiguration.java
index 16b19d6362b211ca9bc6ea6e7f914833444e01bc..84e1a84742b2d5c11eeb0b64319946b03f32139a 100644
--- a/src/tap/config/TAPConfiguration.java
+++ b/src/tap/config/TAPConfiguration.java
@@ -31,13 +31,17 @@ import tap.TAPFactory;
 import tap.backup.DefaultTAPBackupManager;
 
 /**
- * <p>Utility class gathering tool functions and properties' names useful to deal with a TAP configuration file.</p>
+ * Utility class gathering tool functions and properties' names useful to
+ * deal with a TAP configuration file.
  *
- * <p><i>This class implements the Design Pattern "Utility": no instance of this class can be created, it can not be extended,
- * and it must be used only thanks to its static classes and attributes.</i></p>
+ * <p><i>
+ * 	This class implements the Design Pattern "Utility": no instance of this
+ * 	class can be created, it can not be extended, and it must be used only
+ * 	thanks to its static classes and attributes.
+ * </i></p>
  *
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.3 (07/2018)
+ * @version 2.3 (09/2018)
  * @since 2.0
  */
 public final class TAPConfiguration {
@@ -234,17 +238,42 @@ public final class TAPConfiguration {
 	public final static String KEY_SERVICE_DESCRIPTION = "service_description";
 
 	/* UPLOAD KEYS */
-	/** Name/Key of the property indicating whether the UPLOAD feature must be enabled or not.
-	 * By default, this feature is disabled. */
+	/** Name/Key of the property indicating whether the UPLOAD feature must be
+	 * enabled or not. By default, this feature is disabled. */
 	public final static String KEY_UPLOAD_ENABLED = "upload_enabled";
-	/** Name/Key of the property specifying the default limit (in rows or bytes) on the uploaded VOTable(s). */
+	/** Name/Key of the property specifying the default limit (in rows or bytes)
+	 * on the uploaded VOTable(s).
+	 * @deprecated	Since 2.3, use the property {@value #KEY_MAX_UPLOAD_LIMIT}
+	 *            	instead. */
+	@Deprecated
 	public final static String KEY_DEFAULT_UPLOAD_LIMIT = "upload_default_db_limit";
-	/** Name/Key of the property specifying the maximum limit (in rows or bytes) on the uploaded VOTable(s). */
+	/** Name/Key of the property specifying the maximum limit (in rows or bytes)
+	 * on the uploaded VOTable(s). */
 	public final static String KEY_MAX_UPLOAD_LIMIT = "upload_max_db_limit";
-	/** Name/Key of the property specifying the maximum size of all VOTable(s) uploaded in a query. */
+	/** Default value of the property {@value #KEY_MAX_UPLOAD_LIMIT} =
+	 * {@value #DEFAULT_MAX_UPLOAD_LIMIT}.
+	 * @since 2.3 */
+	public final static String DEFAULT_MAX_UPLOAD_LIMIT = "1000000r";
+	/** Name/Key of the property specifying the maximum size of all VOTable(s)
+	 * uploaded in a query.
+	 * @deprecated	Since 2.3, use the property {@value #KEY_MAX_UPLOAD_LIMIT}
+	 *            	and {@value #KEY_MAX_REQUEST_LIMIT} instead. */
+	@Deprecated
 	public final static String KEY_UPLOAD_MAX_FILE_SIZE = "upload_max_file_size";
-	/** Default value of the property {@link #KEY_UPLOAD_MAX_FILE_SIZE}: {@value #DEFAULT_UPLOAD_MAX_FILE_SIZE}.  */
+	/** Default value of the property {@value #KEY_UPLOAD_MAX_FILE_SIZE} =
+	 * {@value #DEFAULT_UPLOAD_MAX_FILE_SIZE}.
+	 * @deprecated	Since 2.3, use the property {@value #KEY_MAX_UPLOAD_LIMIT}
+	 *            	and {@value #KEY_MAX_REQUEST_LIMIT} instead. */
+	@Deprecated
 	public final static int DEFAULT_UPLOAD_MAX_FILE_SIZE = Integer.MAX_VALUE;
+	/** Name/Key of the property specifying the maximum size of a whole HTTP
+	 * Multipart Request.
+	 * @since 2.3 */
+	public final static String KEY_UPLOAD_MAX_REQUEST_SIZE = "upload_max_request_size";
+	/** Default value of the property {@value #KEY_UPLOAD_MAX_REQUEST_SIZE} =
+	 * {@value #DEFAULT_UPLOAD_MAX_REQUEST_SIZE}.
+	 * @since 2.3 */
+	public final static int DEFAULT_UPLOAD_MAX_REQUEST_SIZE = 250 * 1024 * 1024;
 
 	/* OUTPUT KEYS */
 	/** Name/Key of the property specifying the list of all result output formats to support.
@@ -525,41 +554,97 @@ public final class TAPConfiguration {
 	}
 
 	/**
-	 * <p>Lets parsing a limit (for output, upload, ...) with its numeric value and its unit.</p>
+	 * Lets parsing a limit (for output, upload, ...) with its numeric value
+	 * and its unit.
+	 *
 	 * <p>
 	 * 	Here is the expected syntax: num_val[unit].
-	 * 	Where unit is optional and should be one of the following values: r or R, B, kB, MB, GB.
-	 * 	If the unit is not specified, it is set by default to ROWS.
+	 * 	Where unit is optional and should be one of the following values:
+	 * 	r or R, B, kB, MB, GB. If the unit is not specified, it is set by
+	 * 	default to ROWS.
 	 * </p>
-	 * <p><i>Note: If the value is strictly less than 0 (whatever is the unit), the returned value will be -1.</i></p>
 	 *
-	 * @param value				Property value (must follow the limit syntax: num_val[unit] ; ex: 20kB or 2000 (for 2000 rows)).
+	 * <p><i>Note:
+	 * 	If the value is strictly less than 0 (whatever is the unit), the
+	 * 	returned value will be -1.
+	 * </i></p>
+	 *
+	 * @param value				Property value (must follow the limit syntax:
+	 *             				num_val[unit] ; ex: 20kB or 2000
+	 *             				(for 2000 rows)).
 	 * @param propertyName		Name of the property which specify the limit.
-	 * @param areBytesAllowed	Tells whether the unit BYTES is allowed. If not and a BYTES unit is encountered, then an exception is thrown.
+	 * @param areBytesAllowed	Tells whether the unit BYTES is allowed. If not
+	 *                       	and a BYTES unit is encountered, then an
+	 *                       	exception is thrown.
 	 *
-	 * @return	An array with always 2 items: [0]=numeric value (of type Integer), [1]=unit (of type {@link LimitUnit}).
+	 * @return	An array with always 2 items:
+	 *        	[0]=numeric value (of type Integer),
+	 *        	[1]=unit (of type {@link LimitUnit}).
 	 *
-	 * @throws TAPException	If the syntax is incorrect or if a not allowed unit has been used.
+	 * @throws TAPException	If the syntax is incorrect
+	 *                     	or if a not allowed unit has been used.
+	 *
+	 * @see #parseLimit(String, String, boolean, boolean)
 	 */
 	public final static Object[] parseLimit(String value, final String propertyName, final boolean areBytesAllowed) throws TAPException{
+		return parseLimit(value, propertyName, areBytesAllowed, false);
+	}
+
+	/**
+	 * Lets parsing a limit (for output, upload, ...) with its numeric value
+	 * and its unit.
+	 *
+	 * <p>
+	 * 	Here is the expected syntax: num_val[unit].
+	 * 	Where unit is optional and should be one of the following values:
+	 * 	r or R, B, kB, MB, GB. If the unit is not specified, it is set by
+	 * 	default to ROWS.
+	 * </p>
+	 *
+	 * <p><i>Note:
+	 * 	If the value is strictly less than 0 (whatever is the unit), the
+	 * 	returned value will be -1.
+	 * </i></p>
+	 *
+	 * @param value				Property value (must follow the limit syntax:
+	 *             				num_val[unit] ; ex: 20kB or 2000
+	 *             				(for 2000 rows)).
+	 * @param propertyName		Name of the property which specify the limit.
+	 * @param areBytesAllowed	Tells whether the unit BYTES is allowed. If not
+	 *                       	and a BYTES unit is encountered, then an
+	 *                       	exception is thrown.
+	 * @param longValue			<code>true</code> to get the limit as a long,
+	 *                 			<code>false</code> to get it as an int.
+	 *
+	 * @return	An array with always 2 items:
+	 *        	[0]=numeric value (of type Integer or Long in function of the
+	 *        	    parameter longValue),
+	 *        	[1]=unit (of type {@link LimitUnit}).
+	 *
+	 * @throws TAPException	If the syntax is incorrect
+	 *                     	or if a not allowed unit has been used.
+	 *
+	 * @since 2.3
+	 */
+	public final static Object[] parseLimit(String value, final String propertyName, final boolean areBytesAllowed, final boolean longValue) throws TAPException{
 		// Remove any whitespace inside or outside the numeric value and its unit:
 		if (value != null)
 			value = value.replaceAll("\\s", "");
 
 		// If empty value, return an infinite limit:
 		if (value == null || value.length() == 0)
-			return new Object[]{ -1, LimitUnit.rows };
+			return (longValue ? new Object[]{ new Long(-1L), LimitUnit.rows } : new Object[]{ new Integer(-1), LimitUnit.rows });
 
 		// A. Parse the string from the end in order to extract the unit part.
 		//    The final step of the loop is the extraction of the numeric value, when the first digit is encountered.
-		int numValue = -1;
+		long numValue = -1;
 		LimitUnit unit;
 		StringBuffer buf = new StringBuffer();
 		for(int i = value.length() - 1; i >= 0; i--){
 			// if a digit, extract the numeric value:
 			if (value.charAt(i) >= '0' && value.charAt(i) <= '9'){
 				try{
-					numValue = Integer.parseInt(value.substring(0, i + 1));
+					numValue = Long.parseLong(value.substring(0, i + 1));
 					break;
 				}catch(NumberFormatException nfe){
 					throw new TAPException("Integer expected for the property " + propertyName + " for the substring \"" + value.substring(0, i + 1) + "\" of the whole value: \"" + value + "\"!");
@@ -615,7 +700,10 @@ public final class TAPConfiguration {
 			}
 		}
 
-		return new Object[]{ ((numValue < 0) ? -1 : numValue), unit };
+		if (numValue < 0)
+			return (longValue ? new Object[]{ new Long(-1L), unit } : new Object[]{ new Integer(-1), unit });
+		else
+			return (longValue ? new Object[]{ new Long(numValue), unit } : new Object[]{ new Integer((int)numValue), unit });
 	}
 
 }
diff --git a/src/tap/config/tap_configuration_file.html b/src/tap/config/tap_configuration_file.html
index d9d5450314538f203bdcdedcaa4671d179df2c2a..1f5c46199b0368a591ad4f5d3a4abd7f3eaa3faa 100644
--- a/src/tap/config/tap_configuration_file.html
+++ b/src/tap/config/tap_configuration_file.html
@@ -44,6 +44,9 @@
 			td:nth-child(5) {
 				font-family: monospace;
 			}
+			tr.deprecated td:first-child {
+				text-decoration: line-through;
+			}
 			table ul  { list-style-position:inside; padding: 0; margin: 0; }
 			table p   { margin: 0; padding: 0; text-align: left; text-indent: 0; }
 			table p+p { padding-top: .5em; }
@@ -650,25 +653,27 @@
 				<td></td>
 				<td>boolean</td>
 				<td>
-					<p>Tells whether the Upload must be enabled. If enabled, files can be uploaded in the file_root_path, 
-					the corresponding tables can be added inside the UPLOAD_SCHEMA of the database, queried and then deleted.</p>
-					<p><i>By default, the Upload is disabled: upload_enabled=false.</i></p>
+					<p>Tells whether the Upload must be enabled. If enabled, files can be uploaded in the <code>file_root_path</code>, 
+					the corresponding tables can be added inside the <code>UPLOAD_SCHEMA</code> of the database, queried and then deleted.</p>
+					<p><i><b>Note:</b> Before being stored in the directory file_root_path, it is first
+					uploaded in the temporary directory (defined in the JVM ; generally
+					<code>/tmp</code> on Unix system and <code>c:\temp</code> ; it can be changed at start of the
+					JVM with the property <code>java.io.tmpdir</code>). When the upload is complete,
+					the file is finally moved in <code>file_root_path</code>.</i></p>
+					<p><i>By default, the Upload is disabled: <code>upload_enabled=false</code>.</i></p>
 				</td>
 				<td><ul><li>false <em>(default)</em></li><li>true</li></ul></td>
 			</tr>
-			<tr class="optional">
+			<tr class="deprecated optional" title="Deprecated since 2.3. Use upload_max_db_limit instead.">
 				<td class="done">upload_default_db_limit</td>
 				<td></td>
 				<td>text</td>
 				<td>
-					<p>Default limit for the number of uploaded records that can be inserted inside the database. The prefix "default" means here that this value will be set if the client does not provide one.</p>
-					<p>This limit can be expressed with 2 types: rows or bytes. For rows, you just have to suffix the value by a "r" (upper- or lower-case)
-					or by nothing (by default, nothing will mean "rows"). For bytes, you have to suffix the numeric value by "B", "kB", "MB" or "GB".
-					Here, unit is case sensitive. No other storage unit is allowed.</p>
-					<p>A negative value means there is no restriction on this limit. Float values are not allowed.</p>
-					<p><b>Warning!</b> Obviously this limit MUST be less or equal than upload_max_db_limit, and MUST be of the same type as it.
-					If the chosen type is rows, this limit MUST also be strictly less than upload_max_file_size.</p>
-					<p><em>Default: <code>upload_default_db_limit=-1</code> (no restriction)</em></p>
+					<p>Default limit for the number of uploaded records that can be inserted inside the database.</p>
+					<p><em>This property is DEPRECATED. You should use <code>upload_max_db_limit</code> instead.
+					If it is set anyway, its value will be used as value for
+					<code>upload_max_db_limit</code> ONLY IF this latter is not set.</em></p>
+					<p><em>Default: <code>upload_default_db_limit=-1</code> (i.e. unlimited)</em></p>
 				</td>
 				<td><ul><li>-1 <em>(default)</em></li><li>20</li><li>20r</li><li>20R</li><li>200kB</li></ul></td>
 			</tr>
@@ -677,30 +682,63 @@
 				<td></td>
 				<td>text</td>
 				<td>
-					<p>Maximum limit for the number of uploaded records that can be inserted inside the database. The prefix "max" means here that the client can not set a limit greater than this one.</p>
+					<p>Maximum limit for the number of uploaded records that can be inserted inside the database.</p>
 					<p>This limit can be expressed with 2 types: rows or bytes. For rows, you just have to suffix the value by a "r" (upper- or lower-case),
 					with nothing (by default, nothing will mean "rows"). For bytes, you have to suffix the numeric value by "B", "kB", "MB" or "GB".
 					Here, unit is case sensitive. No other storage unit is allowed.</p>
 					<p>A negative value means there is no restriction on this limit. Float values are not allowed.</p>
-					<p><b>Warning!</b> Obviously this limit MUST be greater or equal than upload_default_db_limit, and MUST be of the same type as it.
-					If the chosen type is rows, this limit MUST also be strictly less than upload_max_file_size.</p>
-					<p><em>Default: <code>upload_max_db_limit=-1</code> (no restriction)</em></p>
+					<p><b>Important note:</b> the specified limit will be checked at a different step of
+					a query processing in function of its unit.
+					If expressed in bytes, the file size will be checked when
+					uploading the file on disk. Thus, when the uploading file
+					starts to exceed the set limit, it will be no longer uploaded
+					and the whole request will be immediately rejected.
+					On the contrary, if the limit is expressed in rows, it will
+					be tested only when ingesting the whole uploaded file
+					(whatever is its size) in the database ; so, after it has been
+					uploaded. As soon as, the rows insertion in the database
+					exceeds the limit, the query is rejected.
+					Consequently, a very huge file could potentially be
+					completely uploaded before being rejected if this property is
+					expressed in rows. Then, it is very important to set the
+					property <code>upload_max_request_size</code> limiting the size of a
+					whole HTTP request in order to better preserve your machine
+					from running out of disk space.</p>
+					<p><em>Default: <code>upload_max_db_limit=1000000r</code> (i.e. 1 million rows)</em></p>
+				</td>
+				<td><ul><li>1000000r <em>(default)</em></li><li>-1 <em>(unlimited)</em></li><li>10000</li><li>10000r</li><li>10000R</li><li>1MB</li></ul></td>
+			</tr>
+			<tr class="deprecated optional" title="Deprecated since 2.3. Use upload_max_db_limit and upload_max_request_size instead.">
+				<td class="done">upload_max_file_size</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>Maximum allowed size for the uploaded file.</p>
+					<p><em>This property is DEPRECATED. You should use <code>upload_max_db_limit</code> with a value
+					expressed in bytes instead. If it is set anyway, its value will be used as
+					value for <code>upload_max_db_limit</code> ONLY IF this latter and
+					<code>upload_default_db_limit</code> are not set.</em></p>
+					<p><em>Default: <code>upload_max_file_size=-1</code> (i.e. unlimited)</em></p>
 				</td>
-				<td><ul><li>-1 <em>(default)</em></li><li>10000</li><li>10000r</li><li>10000R</li><li>1MB</li></ul></td>
+				<td><ul><li>-1 <em>(default)</em></li><li>2147483647B</li><li>2MB</li></ul></td>
 			</tr>
 			<tr class="optional">
-				<td class="done">upload_max_file_size</td>
+				<td class="done">upload_max_request_size</td>
 				<td></td>
 				<td>text</td>
 				<td>
-					<p>Maximum allowed size for the uploaded file.</p>
-					<p>This limit MUST be expressed in bytes. Thus, you have to suffix the numeric value by "B", "kB", "MB" or "GB".
-					Here, unit is case sensitive. No other storage unit is allowed.</p>
-					<p><b>Warning!</b> When the upload is enabled, there must be a maximum file size. Here, no "unlimited" value is possible ; 0 and any negative value are not allowed.</p>
-					<p><b>Warning!</b> In function of the chosen upload_max_db_limit type, upload_max_file_size MUST be greater in order to figure out the file metadata part.</p>
-					<p><em>Default: <code>upload_max_file_size=2147483647B</code> (~2GB ; maximum possible value)</em></p>
+					<p>Maximum allowed size for a whole HTTP multipart request (i.e. request with uploads).</p>
+					<p>This limit MUST be expressed in bytes. Thus, you have to suffix the numeric
+					value by "B", "kB", "MB" or "GB". Here, unit is case sensitive. No other
+					storage unit is allowed.</p>
+					<p>A negative value means there is no restriction on this limit. Float values are
+					not allowed.</p>
+					<p><b>Warning:</b> It is highly recommended to set this property in order to prevent
+					exceeding the disk storage space/quota (especially if
+					<code>upload_max_db_limit</code> is not set or is set in rows).</p> 
+					<p><em>Default: <code>upload_max_request_size=250MB</code></em></p>
 				</td>
-				<td><ul><li>2147483647B <em>(default)</em></li><li>2MB</li></ul></td>
+				<td><ul><li>250MB <em>(default)</em></li><li>-1 <em>(unlimited)</em></li></ul></td>
 			</tr>
 			
 			<tr><td colspan="5">User identification</td></tr>
diff --git a/src/tap/config/tap_full.properties b/src/tap/config/tap_full.properties
index 301f0cb754b1348a9d4f6af39a1b52b0ff0841c4..f7e5f931b7bc4197ae7a102bf91c827dc0d1c1f4 100644
--- a/src/tap/config/tap_full.properties
+++ b/src/tap/config/tap_full.properties
@@ -2,7 +2,7 @@
 #                        FULL TAP CONFIGURATION FILE                           #
 #                                                                              #
 # TAP Version: 2.3                                                             #
-# Date: 31 July 2018                                                           #
+# Date: 5 Sept. 2018                                                           #
 # Author: Gregory Mantelet (CDS;ARI)                                           #
 #                                                                              #
 ################################################################################ 
@@ -50,7 +50,7 @@ home_page_mime_type =
 
 # [OPTIONAL]
 # Name of the provider of the TAP Service.
-provider_name = ARI
+provider_name = CDS
 
 # [OPTIONAL]
 # Description of the TAP Service.
@@ -559,17 +559,20 @@ output_max_limit = -1
 # If enabled, files can be uploaded in the file_root_path, the corresponding
 # tables can be added inside the UPLOAD_SCHEMA of the database, queried and then
 # deleted.
+#
+# NOTE: Before being stored in the directory file_root_path, it is first
+#       uploaded in the temporary directory (defined in the JVM ; generally
+#       `/tmp` on Unix system and `c:\temp` ; it can be changed at start of the
+#       JVM with the property `java.io.tmpdir`). When the upload is complete,
+#       the file is finally moved in file_root_path.
 # 
 # Allowed values: true, false (default).
 upload_enabled = false
 
 # [OPTIONAL]
-# Default limit for the number of uploaded records that can be inserted inside
+# Maximum limit for the number of uploaded records that can be inserted inside
 # the database.
 # 
-# The prefix "default" means here that this value will be set if the client does
-# not provide one.
-# 
 # This limit can be expressed with 2 types: rows or bytes. For rows, you just
 # have to suffix the value by a "r" (upper- or lower-case), with nothing (by
 # default, nothing will mean "rows"). For bytes, you have to suffix the numeric
@@ -578,50 +581,68 @@ upload_enabled = false
 # 
 # A negative value means there is no restriction on this limit. Float values are
 # not allowed.
+#
+# IMPORTANT NOTE: the specified limit will be checked at a different step of
+#                 a query processing in function of its unit.
+#                 If expressed in bytes, the file size will be checked when
+#                 uploading the file on disk. Thus, when the uploading file
+#                 starts to exceed the set limit, it will be no longer uploaded
+#                 and the whole request will be immediately rejected.
+#                 On the contrary, if the limit is expressed in rows, it will
+#                 be tested only when ingesting the whole uploaded file
+#                 (whatever is its size) in the database ; so, after it has been
+#                 uploaded. As soon as, the rows insertion in the database
+#                 exceeds the limit, the query is rejected.
+#                 Consequently, a very huge file could potentially be
+#                 completely uploaded before being rejected if this property is
+#                 expressed in rows. Then, it is very important to set the
+#                 property `upload_max_request_size` limiting the size of a
+#                 whole HTTP request in order to better preserve your machine
+#                 from running out of disk space.
+# 
+# Default: upload_max_db_limit=1000000r (i.e. 1 million rows)
+upload_max_db_limit = 1000000r
+
+
+# [OPTIONAL]
+# Maximum allowed size for a whole HTTP multipart request (i.e. request with
+# uploads).
 # 
-# Obviously this limit MUST be less or equal than upload_max_db_limit.
-# 
-# Default: there is no restriction: upload_default_db_limit=-1
-upload_default_db_limit = -1
-
-# [OPTIONAL]
-# Maximum limit for the number of uploaded records that can be inserted inside
-# the database.
-# 
-# The prefix "max" means here that the client can not set a limit greater than
-# this one.
-# 
-# This limit can be expressed with 2 types: rows or bytes. For rows, you just
-# have to suffix the value by a "r" (upper- or lower-case), with nothing (by
-# default, nothing will mean "rows"). For bytes, you have to suffix the numeric
-# value by "b", "kb", "Mb" or "Gb". Here, unit is case sensitive (except for the
-# last character: "b"). No other storage unit is allowed.
+# This limit MUST be expressed in bytes. Thus, you have to suffix the numeric
+# value by "B", "kB", "MB" or "GB". Here, unit is case sensitive. No other
+# storage unit is allowed.
 # 
 # A negative value means there is no restriction on this limit. Float values are
 # not allowed.
 # 
-# Obviously this limit MUST be greater or equal than upload_default_db_limit.
+# Warning: It is highly recommended to set this property in order to prevent
+#          exceeding the disk storage space/quota (especially if
+#          `upload_max_db_limit` is not set or is set in rows). 
 # 
-# Default: there is no restriction: upload_max_db_limit=-1
-upload_max_db_limit = -1
+# Default: upload_max_request_size=250MB
+upload_max_request_size = 250MB
 
-# [OPTIONAL]
-# Maximum allowed size for the uploaded file.
+# [OPTIONAL ; DEPRECATED since v2.3]
+# Default limit for the number of uploaded records that can be inserted inside
+# the database.
 # 
-# This limit MUST be expressed in bytes. Thus, you have to suffix the numeric
-# value by "B", "kB", "MB" or "GB". Here, unit is case sensitive. No other
-# storage unit is allowed.
+# This property is DEPRECATED. You should use `upload_max_db_limit` instead.
+# If it is set anyway, its value will be used as value for
+# `upload_max_db_limit` ONLY IF this latter is not set.
 # 
-# Warning: When the upload is enabled, there must be a maximum file size. Here,
-#          no "unlimited" value is possible ; 0 and any negative value are not
-#          allowed.
+# Default: upload_default_db_limit=-1 (i.e. unlimited)
+upload_default_db_limit = -1
+
+# [OPTIONAL ; DEPRECATED since v2.3]
+# Maximum allowed size for the uploaded file.
 # 
-# Warning: In function of the chosen upload_max_db_limit type,
-#          upload_max_file_size MUST be greater in order to figure out the file
-#          metadata part.
+# This property is DEPRECATED. You should use `upload_max_db_limit` with a value
+# expressed in bytes instead. If it is set anyway, its value will be used as
+# value for `upload_max_db_limit` ONLY IF this latter and
+# `upload_default_db_limit` are not set.
 # 
-# Default: upload_max_file_size=2147483647B (~2GB ; maximum possible value)
-upload_max_file_size = 2147483647B
+# Default: upload_max_file_size=-1 (i.e. unlimited)
+upload_max_file_size = -1
 
 #######################
 # USER IDENTIFICATION #
diff --git a/src/tap/resource/TAP.java b/src/tap/resource/TAP.java
index a3fe3d8cc59bbf4c95df27f39d3af0d128aee47b..f1619a6c6cc54c2511147286816b9c3f93726cdd 100644
--- a/src/tap/resource/TAP.java
+++ b/src/tap/resource/TAP.java
@@ -660,7 +660,7 @@ public class TAP implements VOSIResource {
 		// Upload limits
 		if (service.uploadEnabled()){
 			// Write upload limits:
-			int[] uploadLimit = service.getUploadLimit();
+			long[] uploadLimit = service.getUploadLimit();
 			LimitUnit[] uploadLimitType = service.getUploadLimitType();
 			if (uploadLimit != null && uploadLimit.length >= 2 && uploadLimitType != null && uploadLimitType.length >= 2){
 				if (uploadLimit[0] > -1 || uploadLimit[1] > -1){
diff --git a/src/uws/service/request/MultipartParser.java b/src/uws/service/request/MultipartParser.java
index da2023a306754ba9c5a12d62fb3b1a1e1603d740..052af07e6e4822bed120202e1d91dbc5933c2f73 100644
--- a/src/uws/service/request/MultipartParser.java
+++ b/src/uws/service/request/MultipartParser.java
@@ -43,8 +43,10 @@ import uws.service.UWS;
 import uws.service.file.UWSFileManager;
 
 /**
- * Extract parameters encoded using the Content-type multipart/form-data
- * in an {@link HttpServletRequest}.
+ * Extract parameters encoded using the Content-type
+ * <code>multipart/form-data</code> in an {@link HttpServletRequest}.
+ *
+ * <h4>Uploaded file storage</h4>
  *
  * <p>
  * 	The created file(s) is(are) stored in the temporary upload directory
@@ -53,12 +55,27 @@ import uws.service.file.UWSFileManager;
  * 	implementation to delete unused request files.
  * </p>
  *
+ * <h4>Upload limits</h4>
+ *
+ * <p>
+ * 	The size of the full request body as well as the size of a single uploaded
+ * 	file are both limited. To get and/or change these limits, use the functions:
+ * </p>
+ * <ul>
+ * 	<li><i>single file limit:</i> {@link #getMaxFileSize()} and
+ * 	    {@link #setMaxFileSize(long)}</li>
+ * 	<li><i>multipart request limit:</i> {@link #getMaxRequestSize()} and
+ * 	    {@link #setMaxRequestSize(long)}</li>
+ * </ul>
  * <p>
- * 	The size of the full request body is limited by the static attribute
- * 	{@link #SIZE_LIMIT} before the creation of the file. Its default value is:
- * 	{@link #DEFAULT_SIZE_LIMIT}={@value #DEFAULT_SIZE_LIMIT} bytes.
+ * 	By default, the limit for an uploaded file is
+ * 	{@value #DEFAULT_FILE_SIZE_LIMIT} (i.e. unlimited) and the limit for the
+ * 	whole request (i.e. all uploaded files together + HTTP header) is
+ * 	{@value #DEFAULT_SIZE_LIMIT} (i.e. unlimited}).
  * </p>
  *
+ * <h4>Parameter consumption</h4>
+ *
  * <p>
  * 	By default, this {@link RequestParser} overwrite parameter occurrences in
  * 	the map: that's to say if a parameter is provided several times, only the
@@ -67,16 +84,28 @@ import uws.service.file.UWSFileManager;
  * </p>
  *
  * @author Gr&eacute;gory Mantelet (ARI;CDS)
- * @version 4.4 (08/2018)
- * @since 4.4
+ * @version 4.4 (09/2018)
+ * @since 4.1
  */
 public class MultipartParser implements RequestParser {
 
 	/** HTTP content-type for HTTP request formated in multipart. */
 	public static final String EXPECTED_CONTENT_TYPE = "multipart/form-data";
 
-	/** Default maximum allowed size for an HTTP request content: 10 MiB. */
-	public static final int DEFAULT_SIZE_LIMIT = 10 * 1024 * 1024;
+	/** Default maximum allowed size for a single uploaded file:
+	 * -1 (i.e. unlimited).
+	 * @since 4.4 */
+	public static final int DEFAULT_FILE_SIZE_LIMIT = -1;
+
+	/** Default maximum allowed size for an HTTP request content:
+	 * -1 (i.e. unlimited). */
+	public static final int DEFAULT_SIZE_LIMIT = -1;
+
+	/** Size threshold (in bytes) for an individual file before being stored on
+	 * disk. Below this threshold, the file is only stored in memory.
+	 * <p><i><b>Note:</b> By default, set to 10 kiB.</i></p>
+	 * @since 4.4 */
+	protected final static int SIZE_BEFORE_DISK_STORAGE = 10 * 1024;
 
 	/** Maximum allowed size for an HTTP request content. Over this limit, an
 	 * exception is thrown and the request is aborted.
@@ -90,7 +119,10 @@ public class MultipartParser implements RequestParser {
 	 * 	This limit is expressed in bytes and can not be negative.
 	 *  Its smallest possible value is 0. If the set value is though negative,
 	 *  it will be ignored and {@link #DEFAULT_SIZE_LIMIT} will be used instead.
-	 * </i></p> */
+	 * </i></p>
+	 *
+	 *  @deprecated Since 4.4 ; barely used and never worked (with COS). */
+	@Deprecated
 	public static int SIZE_LIMIT = DEFAULT_SIZE_LIMIT;
 
 	/** Indicates whether this parser should allow inline files or not. */
@@ -145,6 +177,33 @@ public class MultipartParser implements RequestParser {
 	 *                   	eventual upload. <b>MUST NOT be NULL</b>
 	 */
 	protected MultipartParser(final boolean uploadEnabled, final UWSFileManager fileManager){
+		this(uploadEnabled, fileManager, DEFAULT_FILE_SIZE_LIMIT, DEFAULT_SIZE_LIMIT);
+	}
+
+	/**
+	 * Build a {@link MultipartParser}.
+	 *
+	 * <p>
+	 * 	If the first parameter is <i>false</i>, then when an upload
+	 * 	(i.e. submitted inline files) is detected, an exception is thrown
+	 * 	by {@link #parse(HttpServletRequest)} which cancels immediately the
+	 * 	request.
+	 * </p>
+	 *
+	 * @param uploadEnabled		<i>true</i> to allow uploads (i.e. inline files),
+	 *                     		<i>false</i> otherwise. If <i>false</i>, the two
+	 *                     		other parameters are useless.
+	 * @param fileManager		The file manager to use in order to store any
+	 *                   		eventual upload. <b>MUST NOT be NULL</b>
+	 * @param maxFileSize		Maximum size of a single upload file (in bytes).
+	 *                   		<i>A negative value for "unlimited".</i>
+	 * @param maxRequestSize	Maximum size of a whole multipart request (in
+	 *                      	bytes).
+	 *                   		<i>A negative value for "unlimited".</i>
+	 *
+	 * @since 4.4
+	 */
+	protected MultipartParser(final boolean uploadEnabled, final UWSFileManager fileManager, final long maxFileSize, final long maxRequestSize){
 		if (uploadEnabled && fileManager == null)
 			throw new NullPointerException("Missing file manager although the upload capability is enabled => can not create a MultipartParser!");
 
@@ -157,9 +216,67 @@ public class MultipartParser implements RequestParser {
 		// Configure a repository:
 		factory.setRepository(fileManager.getTmpDirectory());
 
-		// Create a new file upload handler
+		/* Set the maximum size of an in-memory file before being stored on the
+		 * disk: */
+		factory.setSizeThreshold(SIZE_BEFORE_DISK_STORAGE);
+
+		// Create a new file upload handler:
 		fileUpload = new ServletFileUpload(factory);
-		fileUpload.setFileSizeMax(SIZE_LIMIT);
+
+		// Set the maximum size for each single file:
+		fileUpload.setFileSizeMax(maxFileSize);
+
+		/* Set the maximum size for a whole multipart HTTP request
+		 * (i.e. all files together): */
+		fileUpload.setSizeMax(maxRequestSize);
+	}
+
+	/**
+	 * Get the maximum size (in bytes) of a single uploaded file.
+	 *
+	 * @return	Maximum upload file size (in bytes),
+	 *        	or -1 if no limit.
+	 *
+	 * @since 4.4
+	 */
+	public final long getMaxFileSize(){
+		return fileUpload.getFileSizeMax();
+	}
+
+	/**
+	 * Set the maximum size (in bytes) of a single uploaded file.
+	 *
+	 * @param maxFileSize	New maximum upload file size (in bytes).
+	 *                      If <code>-1</code>, then there will be no limit.
+	 *
+	 * @since 4.4
+	 */
+	public void setMaxFileSize(final long maxFileSize){
+		fileUpload.setFileSizeMax(maxFileSize);
+	}
+
+	/**
+	 * Get the maximum size (in bytes) of a whole multipart request.
+	 *
+	 * @return	Maximum multipart request size (in bytes),
+	 *        	or -1 if no limit.
+	 *
+	 * @since 4.4
+	 */
+	public final long getMaxRequestSize(){
+		return fileUpload.getSizeMax();
+	}
+
+	/**
+	 * Set the maximum size (in bytes) of a whole multipart request.
+	 *
+	 * @param maxRequestSize	New maximum multipart request size (in bytes).
+	 *                      	If <code>-1</code>, then there will be no limit.
+	 *
+	 * @since 4.4
+	 */
+	public void setMaxRequestSize(final long maxRequestSize){
+		fileUpload.setSizeMax((maxRequestSize < 0) ? -1 : maxRequestSize);
 	}
 
 	@Override
@@ -192,9 +309,9 @@ public class MultipartParser implements RequestParser {
 				item.delete();
 			}
 		}catch(FileUploadException fue){
-			throw new UWSException(UWSException.BAD_REQUEST, fue, "Incorrect HTTP request: " + fue.getMessage() + ".");
+			throw new UWSException(UWSException.BAD_REQUEST, fue, "Incorrect HTTP request: " + fue.getMessage() + " (server limits: each file/parameter <= " + (fileUpload.getFileSizeMax() <= 0 ? "unlimited" : fileUpload.getFileSizeMax() + " bytes") + " and the whole request <= " + (fileUpload.getSizeMax() <= 0 ? "unlimited" : fileUpload.getSizeMax()) + " bytes)");
 		}catch(IOException ioe){
-			throw new UWSException(UWSException.BAD_REQUEST, ioe, "Incorrect HTTP request: " + ioe.getMessage() + ".");
+			throw new UWSException(UWSException.BAD_REQUEST, ioe, "Incorrect HTTP request: " + ioe.getMessage());
 		}catch(IllegalArgumentException iae){
 			String confError = iae.getMessage();
 			if (fileManager.getTmpDirectory() == null)
diff --git a/test/tap/config/TestConfigurableServiceConnection.java b/test/tap/config/TestConfigurableServiceConnection.java
index 23ce7cbeea9a8b76e80a8ecd21ab293b2bdfbc26..ed1957131cf8030ed90394ee21aa3ddf51b9f497 100644
--- a/test/tap/config/TestConfigurableServiceConnection.java
+++ b/test/tap/config/TestConfigurableServiceConnection.java
@@ -13,12 +13,14 @@ import static tap.config.TAPConfiguration.DEFAULT_SYNC_FETCH_SIZE;
 import static tap.config.TAPConfiguration.KEY_ASYNC_FETCH_SIZE;
 import static tap.config.TAPConfiguration.KEY_COORD_SYS;
 import static tap.config.TAPConfiguration.KEY_DEFAULT_OUTPUT_LIMIT;
+import static tap.config.TAPConfiguration.KEY_DEFAULT_UPLOAD_LIMIT;
 import static tap.config.TAPConfiguration.KEY_FILE_MANAGER;
 import static tap.config.TAPConfiguration.KEY_GEOMETRIES;
 import static tap.config.TAPConfiguration.KEY_LOGGER;
 import static tap.config.TAPConfiguration.KEY_LOG_ROTATION;
 import static tap.config.TAPConfiguration.KEY_MAX_ASYNC_JOBS;
 import static tap.config.TAPConfiguration.KEY_MAX_OUTPUT_LIMIT;
+import static tap.config.TAPConfiguration.KEY_MAX_UPLOAD_LIMIT;
 import static tap.config.TAPConfiguration.KEY_METADATA;
 import static tap.config.TAPConfiguration.KEY_METADATA_FILE;
 import static tap.config.TAPConfiguration.KEY_MIN_LOG_LEVEL;
@@ -101,7 +103,8 @@ public class TestConfigurableServiceConnection {
 			badVotFormat6Prop, unknownFormatProp, maxAsyncProp,
 			negativeMaxAsyncProp, notIntMaxAsyncProp, defaultOutputLimitProp,
 			maxOutputLimitProp, bothOutputLimitGoodProp, bothOutputLimitBadProp,
-			syncFetchSizeProp, notIntSyncFetchSizeProp,
+			defaultUploadLimitProp, maxUploadLimitProp, bothUploadLimitGoodProp,
+			bothUploadLimitBadProp, syncFetchSizeProp, notIntSyncFetchSizeProp,
 			negativeSyncFetchSizeProp, notIntAsyncFetchSizeProp,
 			negativeAsyncFetchSizeProp, asyncFetchSizeProp, userIdentProp,
 			notClassPathUserIdentProp, coordSysProp, noneCoordSysProp,
@@ -241,6 +244,20 @@ public class TestConfigurableServiceConnection {
 		bothOutputLimitBadProp.setProperty(KEY_DEFAULT_OUTPUT_LIMIT, "1000");
 		bothOutputLimitBadProp.setProperty(KEY_MAX_OUTPUT_LIMIT, "100");
 
+		defaultUploadLimitProp = (Properties)validProp.clone();
+		defaultUploadLimitProp.setProperty(KEY_DEFAULT_UPLOAD_LIMIT, "100r");
+
+		maxUploadLimitProp = (Properties)validProp.clone();
+		maxUploadLimitProp.setProperty(KEY_MAX_UPLOAD_LIMIT, "1000R");
+
+		bothUploadLimitGoodProp = (Properties)validProp.clone();
+		bothUploadLimitGoodProp.setProperty(KEY_DEFAULT_UPLOAD_LIMIT, "100R");
+		bothUploadLimitGoodProp.setProperty(KEY_MAX_UPLOAD_LIMIT, "10kB");
+
+		bothUploadLimitBadProp = (Properties)validProp.clone();
+		bothUploadLimitBadProp.setProperty(KEY_DEFAULT_UPLOAD_LIMIT, "1000r");
+		bothUploadLimitBadProp.setProperty(KEY_MAX_UPLOAD_LIMIT, "100R");
+
 		syncFetchSizeProp = (Properties)validProp.clone();
 		syncFetchSizeProp.setProperty(KEY_SYNC_FETCH_SIZE, "50");
 
@@ -843,6 +860,62 @@ public class TestConfigurableServiceConnection {
 			fail("This MUST have succeeded because the default output limit is set automatically to the maximum one if bigger! \nCaught exception: " + getPertinentMessage(e));
 		}
 
+		// Test with no upload limit specified:
+		try{
+			ServiceConnection connection = new ConfigurableServiceConnection(validProp);
+			assertEquals(1000000, connection.getUploadLimit()[1]);
+			assertEquals(connection.getUploadLimit()[1], connection.getUploadLimit()[0]);
+			assertEquals(LimitUnit.rows, connection.getUploadLimitType()[1]);
+			assertEquals(connection.getUploadLimitType()[1], connection.getUploadLimitType()[0]);
+		}catch(Exception e){
+			fail("This MUST have succeeded because providing no upload limit is valid! \nCaught exception: " + getPertinentMessage(e));
+		}
+
+		// Test with only a set default upload limit:
+		try{
+			ServiceConnection connection = new ConfigurableServiceConnection(defaultUploadLimitProp);
+			assertEquals(100, connection.getUploadLimit()[1]);
+			assertEquals(connection.getUploadLimit()[1], connection.getUploadLimit()[0]);
+			assertEquals(LimitUnit.rows, connection.getUploadLimitType()[1]);
+			assertEquals(connection.getUploadLimitType()[1], connection.getUploadLimitType()[0]);
+		}catch(Exception e){
+			fail("This MUST have succeeded because setting the default upload limit is valid! \nCaught exception: " + getPertinentMessage(e));
+		}
+
+		// Test with only a set maximum upload limit:
+		try{
+			ServiceConnection connection = new ConfigurableServiceConnection(maxUploadLimitProp);
+			assertEquals(1000, connection.getUploadLimit()[1]);
+			assertEquals(connection.getUploadLimit()[1], connection.getUploadLimit()[0]);
+			assertEquals(LimitUnit.rows, connection.getUploadLimitType()[1]);
+			assertEquals(connection.getUploadLimitType()[1], connection.getUploadLimitType()[0]);
+		}catch(Exception e){
+			fail("This MUST have succeeded because setting only the maximum upload limit is valid! \nCaught exception: " + getPertinentMessage(e));
+		}
+
+		// Test with both a default and a maximum upload limits where default <= max:
+		try{
+			ServiceConnection connection = new ConfigurableServiceConnection(bothUploadLimitGoodProp);
+			assertEquals(10, connection.getUploadLimit()[1]);
+			assertEquals(connection.getUploadLimit()[1], connection.getUploadLimit()[0]);
+			assertEquals(LimitUnit.kilobytes, connection.getUploadLimitType()[1]);
+			assertEquals(connection.getUploadLimitType()[1], connection.getUploadLimitType()[0]);
+		}catch(Exception e){
+			fail("This MUST have succeeded because the default upload limit is less or equal the maximum one! \nCaught exception: " + getPertinentMessage(e));
+		}
+
+		// Test with both a default and a maximum output limits BUT where default > max:
+		/* In a such case, the default value is set silently to the maximum one. */
+		try{
+			ServiceConnection connection = new ConfigurableServiceConnection(bothUploadLimitBadProp);
+			assertEquals(100, connection.getUploadLimit()[1]);
+			assertEquals(connection.getUploadLimit()[1], connection.getUploadLimit()[0]);
+			assertEquals(LimitUnit.rows, connection.getUploadLimitType()[1]);
+			assertEquals(connection.getUploadLimitType()[1], connection.getUploadLimitType()[0]);
+		}catch(Exception e){
+			fail("This MUST have succeeded because the default upload limit is set automatically to the maximum one if bigger! \nCaught exception: " + getPertinentMessage(e));
+		}
+
 		// Test with a not integer sync. fetch size:
 		try{
 			new ConfigurableServiceConnection(notIntSyncFetchSizeProp);
@@ -1280,7 +1353,7 @@ public class TestConfigurableServiceConnection {
 		}
 
 		@Override
-		public JobOwner restoreUser(String id, String pseudo, Map<String,Object> otherData) throws UWSException{
+		public JobOwner restoreUser(String id, String pseudo, Map<String, Object> otherData) throws UWSException{
 			return everybody;
 		}
 
@@ -1307,13 +1380,15 @@ public class TestConfigurableServiceConnection {
 		}
 
 		@Override
-		public void freeConnection(final DBConnection conn){}
+		public void freeConnection(final DBConnection conn){
+		}
 
 		@Override
 		public void destroy(){
 			try{
 				dbConn.getInnerConnection().close();
-			}catch(Exception ex){}
+			}catch(Exception ex){
+			}
 		}
 
 	}
@@ -1340,13 +1415,15 @@ public class TestConfigurableServiceConnection {
 		}
 
 		@Override
-		public void freeConnection(final DBConnection conn){}
+		public void freeConnection(final DBConnection conn){
+		}
 
 		@Override
 		public void destroy(){
 			try{
 				dbConn.getInnerConnection().close();
-			}catch(Exception ex){}
+			}catch(Exception ex){
+			}
 		}
 
 	}
@@ -1369,10 +1446,12 @@ public class TestConfigurableServiceConnection {
 		}
 
 		@Override
-		public void freeConnection(final DBConnection conn){}
+		public void freeConnection(final DBConnection conn){
+		}
 
 		@Override
-		public void destroy(){}
+		public void destroy(){
+		}
 
 	}
 
diff --git a/test/tap/config/TestConfigurableTAPFactory.java b/test/tap/config/TestConfigurableTAPFactory.java
index 41787b918b71154c5864089634215759985ed606..eeb81002d2a5ef700dfb8b4182b48b348894089f 100644
--- a/test/tap/config/TestConfigurableTAPFactory.java
+++ b/test/tap/config/TestConfigurableTAPFactory.java
@@ -183,7 +183,8 @@ public class TestConfigurableTAPFactory {
 				try{
 					((JDBCConnection)connection).getInnerConnection().close();
 					connection = null;
-				}catch(SQLException se){}
+				}catch(SQLException se){
+				}
 			}
 		}
 
@@ -199,7 +200,8 @@ public class TestConfigurableTAPFactory {
 				try{
 					((JDBCConnection)connection).getInnerConnection().close();
 					connection = null;
-				}catch(SQLException se){}
+				}catch(SQLException se){
+				}
 			}
 		}
 
@@ -344,11 +346,11 @@ public class TestConfigurableTAPFactory {
 		// User backup:
 		try{
 			UWSService uws;
-			UserIdentifier userIdent = new UserIdentifier(){
+			UserIdentifier userIdent = new UserIdentifier() {
 				private static final long serialVersionUID = 1L;
 
 				@Override
-				public JobOwner restoreUser(String id, String pseudo, Map<String,Object> otherData) throws UWSException{
+				public JobOwner restoreUser(String id, String pseudo, Map<String, Object> otherData) throws UWSException{
 					return null;
 				}
 
@@ -445,7 +447,7 @@ public class TestConfigurableTAPFactory {
 		}
 
 		@Override
-		public int[] getUploadLimit(){
+		public long[] getUploadLimit(){
 			return null;
 		}
 
@@ -455,8 +457,8 @@ public class TestConfigurableTAPFactory {
 		}
 
 		@Override
-		public int getMaxUploadSize(){
-			return 0;
+		public long getMaxUploadSize(){
+			return 0L;
 		}
 
 		@Override
diff --git a/test/tap/formatter/ServiceConnection4Test.java b/test/tap/formatter/ServiceConnection4Test.java
index 0b55f7851233c44e686f1498b2eb9d25fa82df5c..97f6065c806f8f8f961ffa42c040090c3b8afe27 100644
--- a/test/tap/formatter/ServiceConnection4Test.java
+++ b/test/tap/formatter/ServiceConnection4Test.java
@@ -17,7 +17,8 @@ public class ServiceConnection4Test implements ServiceConnection {
 	private TAPFactory factory = null;
 	private UWSFileManager fileManager = null;
 
-	public ServiceConnection4Test(){}
+	public ServiceConnection4Test(){
+	}
 
 	public ServiceConnection4Test(final TAPMetadata metadata, final UWSFileManager fileManager){
 		this.metadata = metadata;
@@ -26,12 +27,12 @@ public class ServiceConnection4Test implements ServiceConnection {
 
 	@Override
 	public int[] getOutputLimit(){
-		return new int[]{1000000,1000000};
+		return new int[]{ 1000000, 1000000 };
 	}
 
 	@Override
 	public LimitUnit[] getOutputLimitType(){
-		return new LimitUnit[]{LimitUnit.bytes,LimitUnit.bytes};
+		return new LimitUnit[]{ LimitUnit.bytes, LimitUnit.bytes };
 	}
 
 	@Override
@@ -75,7 +76,7 @@ public class ServiceConnection4Test implements ServiceConnection {
 	}
 
 	@Override
-	public int[] getUploadLimit(){
+	public long[] getUploadLimit(){
 		return null;
 	}
 
@@ -85,8 +86,8 @@ public class ServiceConnection4Test implements ServiceConnection {
 	}
 
 	@Override
-	public int getMaxUploadSize(){
-		return 0;
+	public long getMaxUploadSize(){
+		return 0L;
 	}
 
 	@Override
@@ -144,7 +145,8 @@ public class ServiceConnection4Test implements ServiceConnection {
 	}
 
 	@Override
-	public void setAvailable(boolean isAvailable, String message){}
+	public void setAvailable(boolean isAvailable, String message){
+	}
 
 	@Override
 	public int[] getFetchSize(){
diff --git a/test/tap/parameters/ServiceConnectionOfTest.java b/test/tap/parameters/ServiceConnectionOfTest.java
index 029c05663961d7783d6a1c5f6b37727cf35acec0..4a82f6bcd08257426a6a8dca371874c4f14b928f 100644
--- a/test/tap/parameters/ServiceConnectionOfTest.java
+++ b/test/tap/parameters/ServiceConnectionOfTest.java
@@ -5,6 +5,7 @@ import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 
+import adql.db.FunctionDef;
 import tap.ServiceConnection;
 import tap.TAPFactory;
 import tap.TAPJob;
@@ -16,16 +17,15 @@ import tap.log.TAPLog;
 import tap.metadata.TAPMetadata;
 import uws.service.UserIdentifier;
 import uws.service.file.UWSFileManager;
-import adql.db.FunctionDef;
 
 public class ServiceConnectionOfTest implements ServiceConnection {
 
 	private boolean available = true;
 	private String availability = "TAP Service available!";
-	private int[] retentionPeriod = new int[]{-1,-1};
-	private int[] executionDuration = new int[]{(int)TAPJob.UNLIMITED_DURATION,(int)TAPJob.UNLIMITED_DURATION};
-	private int[] outputLimit = new int[]{TAPJob.UNLIMITED_MAX_REC,TAPJob.UNLIMITED_MAX_REC};
-	private List<OutputFormat> outputFormats = Arrays.asList(new OutputFormat[]{new VOTableFormat(this),new SVFormat(this, SVFormat.COMMA_SEPARATOR),new FITSFormat(this)});
+	private int[] retentionPeriod = new int[]{ -1, -1 };
+	private int[] executionDuration = new int[]{ (int)TAPJob.UNLIMITED_DURATION, (int)TAPJob.UNLIMITED_DURATION };
+	private int[] outputLimit = new int[]{ TAPJob.UNLIMITED_MAX_REC, TAPJob.UNLIMITED_MAX_REC };
+	private List<OutputFormat> outputFormats = Arrays.asList(new OutputFormat[]{ new VOTableFormat(this), new SVFormat(this, SVFormat.COMMA_SEPARATOR), new FITSFormat(this) });
 
 	@Override
 	public String getProviderName(){
@@ -88,7 +88,7 @@ public class ServiceConnectionOfTest implements ServiceConnection {
 
 	@Override
 	public LimitUnit[] getOutputLimitType(){
-		return new LimitUnit[]{LimitUnit.rows,LimitUnit.rows};
+		return new LimitUnit[]{ LimitUnit.rows, LimitUnit.rows };
 	}
 
 	@Override
@@ -102,7 +102,7 @@ public class ServiceConnectionOfTest implements ServiceConnection {
 	}
 
 	@Override
-	public int[] getUploadLimit(){
+	public long[] getUploadLimit(){
 		return null;
 	}
 
@@ -112,8 +112,8 @@ public class ServiceConnectionOfTest implements ServiceConnection {
 	}
 
 	@Override
-	public int getMaxUploadSize(){
-		return 0;
+	public long getMaxUploadSize(){
+		return 0L;
 	}
 
 	@Override