title: Could find no Content-Disposition header within part
body: <p>I'm trying to upload a picture file from my Android application to my JavaEE REST service, which is deployed to a JBoss Wildfly 9 server. </p>
  
  <p>My understanding of <code>Content-Disposition</code> is that it should be defined as its own header for each part uploaded, but also can be defined in the request header - if only one file is uploaded. </p>
  
  <p>So in the header I've defined <code>Content-Type: multipart/form-data;boundary=*****</code>, while each (<em>for now only one</em>) part starts with <code>--*****</code>, before I end the request body with <code>--*****--</code>. This is in case I need to upgrade to multiple file upload on a later point. </p>
  
  <p>The server should be accessible from both Android and AngularJS applications. Therefore I've added a <code>ContainerResponseFilter</code> for the AngularJS app, with the following, but I don't see any reason this should be the reason for the blocked request. </p>
  
  <pre><code>@Override
  public void filter(ContainerRequestContext requestCtx, ContainerResponseContext responseCtx)
          throws IOException {
      responseCtx.getHeaders().add("Access-Control-Allow-Origin", "http://localhost:8000");
      responseCtx.getHeaders().add("Access-Control-Allow-Credentials", "true");
      responseCtx.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
      responseCtx.getHeaders().add("Access-Control-Allow-Headers", "Host, "
          + "Accept, "
          + "Origin, "
          + "Connection, "
          + "Content-Type, "
          + "Cache-Control, "
          + "Content-Length, "
          + "Accept-Encoding, "
          + "Content-Disposition");
  }
  </code></pre>
  
  <hr>
  
  <pre><code>@Override
  public void filter(ContainerRequestContext containerRequestContext) throws IOException {
      String reqSource = "(" + servletRequest.getRemoteAddr() + ") "
              + servletRequest.getRemoteUser() + "@"
              + servletRequest.getRemoteHost() + ":"
              + servletRequest.getRemotePort();
      LOGGER.trace(" :: Source :: [{}]", reqSource);
  
      String userId = (securityContext.getUserPrincipal() != null ?
              securityContext.getUserPrincipal().getName() : "unknown");
      LOGGER.trace(" :: User :: [{}]", userId);
  
      String reqUri = servletRequest.getRequestURI();
      String reqType = servletRequest.getMethod();
      LOGGER.trace(" :: Boundary :: [{}] - [{}]", reqUri, reqType);
  }
  </code></pre>
  
  <p>These filters are the only code added by me that interacts with the request and reponse between the applications and the end point. I've also tried to remove these filters, without any luck. Removing the responseFilter breaks the communication with the AngularJS app, while removing the requestFilter only stops the logging. </p>
  
  <pre><code>@POST
  @Consumes({MULTIPART_FORM_DATA})
  public Response createPicture(MultipartFormDataInput input) {
  
      for (InputPart inputPart : input.getParts()) {
          try {
              fileController.saveFile(inputPart);
  
              return Response.ok().build();
          } catch (FileNotSavedException e) {
              return Response.serverError(e.getMessage()).build();
          }
      }
  
      return badRequestNullResponse();
  }
  </code></pre>
  
  <p>Code for uploading the picture (Android):</p>
  
  <pre><code>public static void uploadBitmap(Bitmap bitmap, String filename) 
          throws IOException {
      URL url = new URL(URL_REST_API + FILE);
      HttpURLConnection httpUrlConnection = (HttpURLConnection) url.openConnection();
      httpUrlConnection.setUseCaches(false);
      httpUrlConnection.setDoOutput(true);
      httpUrlConnection.setDoInput(true);
  
      httpUrlConnection.setRequestMethod("POST");
      httpUrlConnection.setRequestProperty("Connection", "Keep-Alive");
      httpUrlConnection.setRequestProperty("Cache-Control", "no-cache");
      httpUrlConnection.setRequestProperty("Accept", "multipart/form-data");
      httpUrlConnection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + BOUNDARY);
  
      // EDIT 2: This following log statement was omitted in the first post.
      // I extracted the setRequestProperty statements from another method
      // due readability of this question, but I had missed to copy this:
      Log.e(LOG_TAG, "Headers: \n" + httpUrlConnection.getHeaderFields());
  
      DataOutputStream request = new DataOutputStream(httpUrlConnection.getOutputStream());
  
      // Part [start]
      request.writeBytes(DOUBLE_HYPHEN + BOUNDARY + CR_LF);
      request.writeBytes("Content-Disposition: form-data;filename=\"" + filename + "\"" + CR_LF);
      request.writeBytes(CR_LF);
  
      // Part [content]
      bitmap.compress(Bitmap.CompressFormat.JPEG, IMAGE_QUALITY_PERCENTAGE, request);
  
      // part [end]
      request.writeBytes(CR_LF);
      request.writeBytes(DOUBLE_HYPHEN + BOUNDARY + DOUBLE_HYPHEN + CR_LF);
  
      request.flush();
      request.close();
  
      httpUrlConnection.disconnect();
  }
  </code></pre>
  
  <p>As mentioned I'm starting "each" part with double hyphens, the boundary and a line break. Since the headers are created by <code>setRequestProperty</code> I would assume the header are ended correctly. So is it caused by a missing body, in that case; why isn't the <code>Content-Disposition</code> or the picture file written to the request? </p>
  
  <hr>
  
  <pre><code>07:55:28,479 WARN  [org.apache.james.mime4j.parser.MimeEntity] (default task-36) Unexpected end of headers detected. Higher level boundary detected or EOF reached.
  07:55:28,479 WARN  [org.apache.james.mime4j.parser.MimeEntity] (default task-36) Invalid header encountered
  07:55:28,479 WARN  [org.apache.james.mime4j.parser.MimeEntity] (default task-36) Body part ended prematurely. Boundary detected in header or EOF reached.
  07:55:28,479 WARN  [org.jboss.resteasy.core.ExceptionHandler] (default task-36) Failed executing POST /file: org.jboss.resteasy.spi.ReaderException: java.lang.RuntimeException: Could find no Content-Disposition header within part
      at org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.java:183)
      at org.jboss.resteasy.core.MethodInjectorImpl.injectArguments(MethodInjectorImpl.java:89)
      at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:112)
      at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:296)
      at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:250)
      at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:237)
      at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:356)
      at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:179)
      at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:220)
      at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
      at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
      at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:86)
      at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
      at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
      at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
      at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
      at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)
      at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
      at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
      at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
      at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
      at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:58)
      at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:72)
      at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
      at io.undertow.security.handlers.SecurityInitialHandler.handleRequest(SecurityInitialHandler.java:76)
      at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
      at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
      at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
      at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
      at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:282)
      at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:261)
      at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:80)
      at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:172)
      at io.undertow.server.Connectors.executeRootHandler(Connectors.java:199)
      at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:774)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
      at java.lang.Thread.run(Thread.java:745)
  Caused by: java.lang.RuntimeException: Could find no Content-Disposition header within part
      at org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInputImpl.extractPart(MultipartFormDataInputImpl.java:68)
      at org.jboss.resteasy.plugins.providers.multipart.MultipartInputImpl.extractParts(MultipartInputImpl.java:229)
      at org.jboss.resteasy.plugins.providers.multipart.MultipartInputImpl.parse(MultipartInputImpl.java:198)
      at org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataReader.readFrom(MultipartFormDataReader.java:52)
      at org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataReader.readFrom(MultipartFormDataReader.java:20)
      at org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataReader$Proxy$_$$_WeldClientProxy.readFrom(Unknown Source)
      at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.readFrom(AbstractReaderInterceptorContext.java:59)
      at org.jboss.resteasy.core.interception.ServerReaderInterceptorContext.readFrom(ServerReaderInterceptorContext.java:62)
      at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:51)
      at org.jboss.resteasy.security.doseta.DigitalVerificationInterceptor.aroundReadFrom(DigitalVerificationInterceptor.java:32)
      at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:53)
      at org.jboss.resteasy.plugins.interceptors.encoding.GZIPDecodingInterceptor.aroundReadFrom(GZIPDecodingInterceptor.java:59)
      at org.jboss.resteasy.plugins.interceptors.encoding.GZIPDecodingInterceptor$Proxy$_$$_WeldClientProxy.aroundReadFrom(Unknown Source)
      at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:53)
      at org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.java:150)
      ... 38 more
  </code></pre>
  
  <p>Also; I found some WARN messages regarding <code>WFLYWELD0052: (...) Package-private access will not work.</code> So I fixed these by adding <code>core</code> and <code>spi</code> as dependencies in the warned modules, as <a href="https://issues.jboss.org/browse/WFLY-3812" rel="nofollow">suggested in this issue</a>. It removed the warning messages, but did not fix the problem. </p>
  
  <h3>EDIT:</h3>
  
  <p>Here are the logs from an upload request by the android application, where I'm printing the headers sent with the request:</p>
  
  <pre><code>(default task-42) Header: [Accept]
  (default task-42) Value: [multipart/form-data]
  (default task-42) Header: [Cache-Control]
  (default task-42) Value: [no-cache]
  (default task-42) Header: [Connection]
  (default task-42) Value: [Keep-Alive]
  (default task-42) Header: [User-Agent]
  (default task-42) Value: [Dalvik/2.1.0 (Linux; U; Android 5.0.2; SM-A500FU Build/LRX22G)]
  (default task-42) Header: [Host]
  (default task-42) Value: [***.**.***.***:8080]
  (default task-42) Header: [Accept-Encoding]
  (default task-42) Value: [gzip]
  (default task-42) Header: [Content-Length]
  (default task-42) Value: [0]
  (default task-42) Header: [Content-Type]
  (default task-42) Value: [multipart/form-data;boundary=*****]
  (default task-42)  :: Source :: [(*.***.**.**) null@*.***.**.**:28686]
  (default task-42)  :: User :: [unknown]
  (default task-42)  :: Boundary :: [/upload/api/file/] - [POST]
  (default task-42) Unexpected end of headers detected. Higher level boundary detected or EOF reached.
  (default task-42) Invalid header encountered
  (default task-42) Body part ended prematurely. Boundary detected in header or EOF reached.
  </code></pre>
  
  <p>When uploading through Postman, I'm able to access the end point, as expected - without adding more than the file (perhaps Postman creates the content-disposition tags automatically?).</p>
  
  <h3>EDIT 2:</h3>
  
  <p>I have updated the Android code with the reason of error (see <code>Edit 2</code>-comment). It was actually a log statement that caused the incorrect behaviour. I'll add an answer to explain why!</p>