This repository has been archived by the owner on Jul 2, 2023. It is now read-only.
forked from krasserm/grails-jaxrs
-
Notifications
You must be signed in to change notification settings - Fork 9
Fix issue with submitting multipart data #33
Closed
Closed
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
d918968
update config to use any available port in test environment.
astoia1 54e193c
Add sample PDF
astoia1 157eeca
Add dependencies for jersey-multipart and apache mime
astoia1 1e87c8d
TDD resource for sending multipart form data
astoia1 a202ad1
Add ReusableServletInputStream class
astoia1 1be6f64
Add ReusableHttpServletRequestWrapper
astoia1 29ec6f6
Add filter to wrap the request into one with a reusable stream.
astoia1 e5579a4
Add commons-io dependency
astoia1 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
...e/src/main/groovy/org/grails/plugins/jaxrs/filters/ReusableHttpRequestStreamFilter.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* Copyright 2009 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.grails.plugins.jaxrs.filters | ||
|
||
import groovy.transform.CompileStatic | ||
import javax.servlet.FilterChain | ||
import javax.servlet.ServletException | ||
import javax.servlet.http.HttpServletRequest | ||
import javax.servlet.http.HttpServletResponse | ||
import org.springframework.web.filter.OncePerRequestFilter | ||
import org.grails.plugins.jaxrs.servlet.http.ReusableStreamHttpServletRequestWrapper | ||
|
||
/** | ||
* | ||
* @author Alex Stoia | ||
*/ | ||
@CompileStatic | ||
class ReusableHttpRequestStreamFilter extends OncePerRequestFilter { | ||
|
||
/** | ||
* | ||
*/ | ||
@Override | ||
protected void doFilterInternal(HttpServletRequest request, | ||
HttpServletResponse response, FilterChain chain) throws ServletException, | ||
IOException { | ||
chain.doFilter(new ReusableStreamHttpServletRequestWrapper(request), response) | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
...s-core/src/main/groovy/org/grails/plugins/jaxrs/servlet/ReusableServletInputStream.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* | ||
* Copyright 2009 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.grails.plugins.jaxrs.servlet | ||
|
||
import groovy.transform.CompileStatic | ||
import javax.servlet.ServletInputStream | ||
import javax.servlet.ReadListener | ||
|
||
|
||
/** | ||
* | ||
* @author Alex Stoia | ||
*/ | ||
@CompileStatic | ||
class ReusableServletInputStream extends ServletInputStream { | ||
private BufferedInputStream wrappedStream | ||
private ServletInputStream originalStream | ||
|
||
ReusableServletInputStream(ServletInputStream originalStream) { | ||
super() | ||
this.originalStream = originalStream | ||
this.wrappedStream = new BufferedInputStream(originalStream) | ||
} | ||
|
||
|
||
@Override | ||
boolean isReady() { | ||
return originalStream.isReady() | ||
} | ||
@Override | ||
boolean isFinished() { | ||
return originalStream.isFinished() | ||
} | ||
@Override | ||
void setReadListener(ReadListener readListener) { | ||
originalStream.setReadListener(readListener) | ||
} | ||
|
||
@Override | ||
public int read() throws IOException { | ||
return wrappedStream.read() | ||
} | ||
@Override | ||
boolean markSupported() { | ||
return wrappedStream.markSupported() | ||
} | ||
@Override | ||
void mark(int readlimit) { | ||
wrappedStream.mark(readlimit) | ||
} | ||
@Override | ||
void reset() { | ||
wrappedStream.reset() | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
...oovy/org/grails/plugins/jaxrs/servlet/http/ReusableStreamHttpServletRequestWrapper.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/* | ||
* Copyright 2009 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.grails.plugins.jaxrs.servlet.http | ||
|
||
import groovy.transform.CompileStatic | ||
import javax.servlet.ServletInputStream | ||
import javax.servlet.http.HttpServletRequest | ||
import javax.servlet.http.HttpServletRequestWrapper | ||
import org.apache.commons.io.IOUtils | ||
import org.grails.plugins.jaxrs.servlet.ReusableServletInputStream | ||
|
||
/** | ||
* | ||
* @author Alex Stoia | ||
*/ | ||
@CompileStatic | ||
class ReusableStreamHttpServletRequestWrapper extends HttpServletRequestWrapper { | ||
private BufferedInputStream wrappedStream | ||
private ServletInputStream usedStream | ||
|
||
ReusableStreamHttpServletRequestWrapper(HttpServletRequest request) { | ||
super(request) | ||
ServletInputStream originalStream = request.getInputStream() | ||
usedStream = new ReusableServletInputStream(originalStream) | ||
usedStream.mark(Integer.MAX_VALUE) | ||
// read the stream contents, otherwise this will not be available in | ||
// the getInputStream method. Also tried reading one byte, works on | ||
// first request, fails on all subsequent ones. Got no idea why at | ||
// the moment | ||
IOUtils.toString(usedStream, 'UTF-8') | ||
} | ||
|
||
@Override | ||
public ServletInputStream getInputStream() { | ||
usedStream.reset() | ||
return usedStream | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
jaxrs-example/grails-app/resources/com/budjb/TestMultipartResource.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package com.budjb | ||
import io.swagger.annotations.Api | ||
|
||
import javax.ws.rs.POST | ||
import javax.ws.rs.Path | ||
import javax.ws.rs.Consumes | ||
import javax.ws.rs.Produces | ||
import javax.ws.rs.core.Response | ||
import javax.ws.rs.core.MediaType | ||
|
||
import com.sun.jersey.multipart.FormDataParam | ||
import com.sun.jersey.multipart.FormDataBodyPart | ||
|
||
import grails.converters.JSON | ||
@Path('/api/testMultipart') | ||
@Api('test') | ||
class TestMultipartResource { | ||
@POST | ||
@Consumes(MediaType.MULTIPART_FORM_DATA ) | ||
@Produces(MediaType.APPLICATION_JSON ) | ||
@Path('/upload') | ||
Response getTestMultipartRepresentation( | ||
@FormDataParam('file') File file, | ||
@FormDataParam('file') FormDataBodyPart fileBodyPart) { | ||
// try with | ||
// curl -v -X POST -H "Content-Type: multipart/form-data" -F "file=@src/integration-test/groovy/com/budjb/resources/pdf-sample.pdf" http://localhost:8060/api/testMultipart/upload | ||
JSON ret = [ | ||
name: fileBodyPart.getContentDisposition().getFileName(), | ||
size: file.size(), | ||
mimeType: fileBodyPart.getMediaType().toString() | ||
] as JSON | ||
return Response.status(200).entity(ret).build() | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
jaxrs-example/src/integration-test/groovy/com/budjb/TestMultipartResourceSpec.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package com.budjb | ||
|
||
import org.apache.http.entity.mime.MultipartEntityBuilder | ||
import org.apache.http.entity.ContentType | ||
import grails.test.mixin.integration.Integration | ||
import org.grails.plugins.jaxrs.test.JaxrsIntegrationSpec | ||
import org.grails.plugins.jaxrs.test.JaxrsRequestProperties | ||
|
||
@Integration | ||
class TestMultipartResourceSpec extends JaxrsIntegrationSpec { | ||
def 'Ensure can send a multipart file to the resource'() { | ||
given: "A file to send" | ||
File file = new File('src/integration-test/groovy/com/budjb/resources/pdf-sample.pdf') | ||
|
||
when: "Sending a file to the web service" | ||
def response = makeRequest(new JaxrsRequestProperties(method: 'POST', | ||
uri: '/api/testMultipart/upload', | ||
headers: ['Content-Type' : ['multipart/form-data; boundary=fnord']], | ||
body: getMultipartBody(file))) | ||
def result = new groovy.json.JsonSlurper().parseText(response.bodyAsString) | ||
|
||
then: "The response is correct" | ||
200 == response.status | ||
'pdf-sample.pdf' == result.name | ||
result.size > 0 | ||
'application/pdf' == result.mimeType | ||
} | ||
/** | ||
* Return the list of additional resources to build the JAX-RS servlet with. | ||
* | ||
* @return | ||
*/ | ||
@Override | ||
List getResources() { | ||
return [] | ||
} | ||
|
||
|
||
private byte[] getMultipartBody(File file) { | ||
MultipartEntityBuilder e = new MultipartEntityBuilder() | ||
e.setBoundary("fnord") | ||
e.addBinaryBody("file", file, ContentType.create("application/pdf"), file.name) | ||
ByteArrayOutputStream baos = new ByteArrayOutputStream() | ||
e.build().writeTo(baos) | ||
return baos.toByteArray() | ||
} | ||
} |
Binary file added
BIN
+7.76 KB
jaxrs-example/src/integration-test/groovy/com/budjb/resources/pdf-sample.pdf
Binary file not shown.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is concerning to me because this ultimately will require all payloads to be read into memory for the duration of the request. This doesn't allow authors to efficiently pipe streams together, and can potentially cause large memory requirements, depending on the expected payload sizes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Bud.
Thanks for your review. This fix immediately unblocked me for using multipart requests in jersey, though I understand the problem. Will have a think on how to improve this
How did you get it to work for issue 21?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Bud.
I think I found a better solution which involves overwriting the MultiPartReaderServerSide to support the Grails requests. My question is, you think it is ok if jersey-multipart will be added as a dependency into the grails-jaxrs-jersey1 plugin? I see no reason not to.
Will push the new fix soon
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Taking a look at these PR's. Did this ever get the multipart library added?