Skip to content

Commit

Permalink
#41 fix "IOException: mark/reset not supported" when parsing http par…
Browse files Browse the repository at this point in the history
…ameters
  • Loading branch information
asolntsev committed Feb 4, 2022
1 parent 63c4ba2 commit 4ea9553
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 29 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## 1.9.4 (released 04.02.2022)
* #41 fix "IOException: mark/reset not supported" when parsing http parameters

## 1.9.3 (released 03.02.2022)
* upgrade to guice guice:5.1.0, hibernate-core:5.6.5.Final, commons-beanutils:1.9.4 etc.

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defaultTasks 'clean', 'check', 'publishToMavenLocal'
subprojects {

group='com.codeborne.replay'
version=project.properties['revision'] ?: '1.9.3'
version=project.properties['revision'] ?: '1.9.4'

apply plugin: 'java'
apply plugin: 'java-library'
Expand Down
23 changes: 19 additions & 4 deletions framework/src/play/data/parsing/TextParser.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
package play.data.parsing;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.exceptions.UnexpectedException;
import play.mvc.Http;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static org.apache.commons.io.IOUtils.toByteArray;

public class TextParser extends DataParser {
private static final Logger log = LoggerFactory.getLogger(TextParser.class);

@Override
public Map<String, String[]> parse(Http.Request request) {
Map<String, String[]> params = new HashMap<>();
try {
Map<String, String[]> params = new HashMap<>();
byte[] data = toByteArray(request.body);
params.put("body", new String[] {new String(data, request.encoding)});
request.body.reset();
return params;
} catch (Exception e) {
} catch (IOException e) {
throw new UnexpectedException(e);
}
resetBodyInputStreamIfPossible(request);
return params;
}

private void resetBodyInputStreamIfPossible(Http.Request request) {
try {
request.body.reset();
}
catch (IOException resetNotSupported) {
log.warn("Failed to reset request.body of type {}: {}",
resetNotSupported.getClass().getName(), resetNotSupported.toString());
}
}
}
36 changes: 16 additions & 20 deletions framework/src/play/server/FileChannelBuffer.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
package play.server;


import org.jboss.netty.buffer.*;

import java.io.*;
import org.jboss.netty.buffer.AbstractChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBufferFactory;
import org.jboss.netty.buffer.ChannelBufferIndexFinder;
import org.jboss.netty.buffer.WrappedChannelBuffer;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.GatheringByteChannel;
Expand All @@ -15,21 +23,12 @@
*/
public class FileChannelBuffer extends AbstractChannelBuffer implements WrappedChannelBuffer {

private final FileInputStream is;

private final InputStream is;

public FileChannelBuffer(File file) {
if (file == null) {
throw new NullPointerException("file");
}
try {
this.is = new FileInputStream(file);
} catch (Exception e) {
throw new RuntimeException(e);
}
public FileChannelBuffer(File file) throws FileNotFoundException {
this.is = new ResettableFileInputStream(file);
}


public InputStream getInputStream() {
return is;
}
Expand Down Expand Up @@ -113,16 +112,13 @@ public void setLong(int index, long value) {
}

@Override
public int setBytes(int index, InputStream in, int length)
throws IOException {
public int setBytes(int index, InputStream in, int length) {
throw new RuntimeException();
}

@Override
public int setBytes(int index, ScatteringByteChannel in, int length)
throws IOException {
public int setBytes(int index, ScatteringByteChannel in, int length) {
throw new RuntimeException();

}

@Override
Expand Down
34 changes: 34 additions & 0 deletions framework/src/play/server/ResettableFileInputStream.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package play.server;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import static java.util.Objects.requireNonNull;

class ResettableFileInputStream extends InputStream {
private final File file;
private InputStream in;

ResettableFileInputStream(File file) throws FileNotFoundException {
this.file = requireNonNull(file);
reset();
}

@Override
public int read() throws IOException {
return in.read();
}

@Override
public final synchronized void reset() throws FileNotFoundException {
in = new FileInputStream(file);
}

@Override
public void close() throws IOException {
in.close();
}
}
40 changes: 36 additions & 4 deletions framework/test/play/data/parsing/TextParserTest.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
package play.data.parsing;

import org.apache.commons.io.FileUtils;
import org.junit.Test;
import play.mvc.Http;
import play.server.FileChannelBuffer;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.commons.io.IOUtils.toByteArray;
import static org.assertj.core.api.Assertions.assertThat;

public class TextParserTest {
TextParser parser = new TextParser();
private final TextParser parser = new TextParser();

@Test
public void parsesRequestBodyAsText() throws IOException {
Http.Request request = new Http.Request();
request.encoding = UTF_8;
request.body = new ByteArrayInputStream("Don't reset me please".getBytes(UTF_8));
Http.Request request = givenRequest(new ByteArrayInputStream("Don't reset me please".getBytes(UTF_8)));

assertThat(parser.parse(request).get("body"))
.as("returns request body as <'body', body> map")
Expand All @@ -27,4 +30,33 @@ public void parsesRequestBodyAsText() throws IOException {
.as("Important: request body should not be reset - some controllers might need to read it")
.isEqualTo("Don't reset me please".getBytes(UTF_8));
}

@Test
public void fileChannelBuffer_supports_reset() throws IOException {
File tempFile = File.createTempFile("replay", "test");
FileUtils.write(tempFile, "Don't reset me please", UTF_8);

Http.Request request = givenRequest(new FileChannelBuffer(tempFile).getInputStream());

assertThat(parser.parse(request).get("body")).isEqualTo(new String[] {"Don't reset me please"});
assertThat(toByteArray(request.body)).isEqualTo("Don't reset me please".getBytes(UTF_8));
}

@Test
public void shouldNotFail_ifInputStreamDoesNotSupportReset() throws IOException {
File tempFile = File.createTempFile("replay", "test");
FileUtils.write(tempFile, "Don't reset me please", UTF_8);

Http.Request request = givenRequest(new FileInputStream(tempFile));

assertThat(parser.parse(request).get("body")).isEqualTo(new String[] {"Don't reset me please"});
assertThat(toByteArray(request.body)).isEqualTo("".getBytes(UTF_8));
}

private Http.Request givenRequest(InputStream in) {
Http.Request request = new Http.Request();
request.encoding = UTF_8;
request.body = in;
return request;
}
}
27 changes: 27 additions & 0 deletions framework/test/play/server/ResettableFileInputStreamTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package play.server;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.junit.Test;

import java.io.File;
import java.io.IOException;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;

public class ResettableFileInputStreamTest {
@Test
public void canResetInputStream() throws IOException {
File file = File.createTempFile("replay-resettable-file-input-stream", "test");
FileUtils.write(file, "zebra", UTF_8);

ResettableFileInputStream in = new ResettableFileInputStream(file);

assertThat(IOUtils.toString(in, UTF_8)).isEqualTo("zebra");
assertThat(IOUtils.toString(in, UTF_8)).isEqualTo("");

in.reset();
assertThat(IOUtils.toString(in, UTF_8)).isEqualTo("zebra");
}
}

0 comments on commit 4ea9553

Please sign in to comment.