Author: nbelaevski
Date: 2009-10-30 10:49:48 -0400 (Fri, 30 Oct 2009)
New Revision: 15785
Added:
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/ByteSequenceMatcher.java
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/FileParam.java
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/Param.java
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/ValueParam.java
Modified:
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/MultipartRequest.java
Log:
https://jira.jboss.org/jira/browse/RF-7927
Added:
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/ByteSequenceMatcher.java
===================================================================
---
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/ByteSequenceMatcher.java
(rev 0)
+++
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/ByteSequenceMatcher.java 2009-10-30
14:49:48 UTC (rev 15785)
@@ -0,0 +1,185 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat, Inc. and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This 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 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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 this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+
+package org.ajax4jsf.request;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ByteSequenceMatcher {
+
+ public interface BytesHandler {
+
+ public void handle(byte[] bytes, int length) throws IOException;
+
+ }
+
+ private static final int ZERO_READS_NUMBER = 20;
+
+ private byte[] buffer;
+
+ private int readLength = 0;
+
+ private int zeroReadCounter = ZERO_READS_NUMBER;
+
+ private boolean bufferEOF = false;
+
+ private boolean isEOF = false;
+
+ private boolean isMatched = false;
+
+ private InputStream inputStream;
+
+ private BytesHandler bytesHandler;
+
+ public ByteSequenceMatcher(InputStream inputStream, int bufferSize) {
+ this.inputStream = inputStream;
+ this.buffer = new byte[bufferSize];
+ }
+
+ public BytesHandler getBytesHandler() {
+ return bytesHandler;
+ }
+
+ public void setBytesHandler(BytesHandler bytesHandler) {
+ this.bytesHandler = bytesHandler;
+ }
+
+ protected void prefillBuffer(int usedLength) throws IOException {
+ if (usedLength > readLength) {
+ throw new IllegalArgumentException();
+ }
+
+ if (usedLength != readLength && usedLength != 0) {
+ System.arraycopy(buffer, usedLength, buffer, 0, readLength - usedLength);
+ }
+
+ readLength -= usedLength;
+
+ int remaining;
+
+ while (!bufferEOF && (remaining = (buffer.length - readLength)) > 0) {
+ int read = inputStream.read(buffer, readLength, remaining);
+
+ if (read > 0) {
+ readLength += read;
+ } else if (read == 0) {
+ --zeroReadCounter;
+
+ if (zeroReadCounter == 0) {
+ throw new IllegalStateException("Maximum number of zero reads reached");
+ }
+ } else if (read < 0) {
+ bufferEOF = true;
+ }
+ }
+ }
+
+ private boolean match(int startOffset, byte[]...sequences) {
+ int index = startOffset;
+
+ for (byte[] bs : sequences) {
+ for (byte b : bs) {
+
+ if (index >= readLength) {
+ return false;
+ }
+
+ if (buffer[index] != b) {
+ return false;
+ }
+
+ index++;
+ }
+ }
+
+ return true;
+ }
+
+ public void findSequence(int limit, byte[]... sequences) throws IOException {
+ isMatched = false;
+
+ int userLimit = limit;
+ if (userLimit <= 0) {
+ userLimit = Integer.MAX_VALUE;
+ }
+
+ prefillBuffer(0);
+
+ int sequencesLength = 0;
+ for (byte[] bs : sequences) {
+ sequencesLength += bs.length;
+ }
+
+ int i = 0;
+
+ while (!isMatched && i <= readLength - sequencesLength) {
+ if (match(i, sequences)) {
+ isMatched = true;
+ bytesHandler.handle(buffer, i);
+ prefillBuffer(i + sequencesLength);
+ } else {
+ int sequenceLimit = readLength - sequencesLength + 1;
+ int realLimit;
+
+ if (sequenceLimit < userLimit) {
+ realLimit = sequenceLimit;
+ } else {
+ realLimit = userLimit;
+ }
+
+ if (realLimit > 0 && i == realLimit - 1) {
+ //report limit
+ bytesHandler.handle(buffer, realLimit);
+ prefillBuffer(realLimit);
+
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+ }
+
+ if (!isMatched) {
+ if (readLength > 0) {
+ bytesHandler.handle(buffer, readLength);
+ prefillBuffer(readLength);
+ }
+ }
+
+ if (this.readLength == 0) {
+ this.isEOF = true;
+ }
+ }
+
+ public boolean isEOF() {
+ return isEOF;
+ }
+
+ public boolean isMatched() {
+ return isMatched;
+ }
+
+ public boolean isMatchedAndNotEOF() {
+ return isMatched() && !isEOF();
+ }
+}
Added:
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/FileParam.java
===================================================================
---
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/FileParam.java
(rev 0)
+++
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/FileParam.java 2009-10-30
14:49:48 UTC (rev 15785)
@@ -0,0 +1,205 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat, Inc. and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This 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 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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 this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+
+package org.ajax4jsf.request;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.rmi.server.UID;
+
+import org.ajax4jsf.exception.FileUploadException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+class FileParam extends Param {
+
+ private static final Log logger = LogFactory.getLog(FileParam.class);
+
+ private String filename;
+ private String contentType;
+ private int fileSize;
+
+ private ByteArrayOutputStream bOut = null;
+ private FileOutputStream fOut = null;
+ private File tempFile = null;
+
+ public FileParam(String name) {
+ super(name);
+ }
+
+ public Object getFile() {
+ if (null != tempFile) {
+ return tempFile;
+ } else if (null != bOut) {
+ return bOut.toByteArray();
+ }
+ return null;
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public void setFilename(String filename) {
+ this.filename = filename;
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ public int getFileSize() {
+ return fileSize;
+ }
+
+ public File createTempFile() {
+ try {
+ tempFile = File.createTempFile(new UID().toString().replace(
+ ":", "-"), ".upload");
+ fOut = new FileOutputStream(tempFile);
+ } catch (IOException ex) {
+ if (fOut != null) {
+ try {
+ fOut.close();
+ } catch (IOException e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+
+ throw new FileUploadException("Could not create temporary file");
+ }
+ return tempFile;
+ }
+
+ public void deleteFile() {
+ try {
+ if (fOut != null) {
+ fOut.close();
+ }
+ if (tempFile != null) {
+ tempFile.delete();
+ }
+ } catch (Exception e) {
+ throw new FileUploadException("Could not delete temporary file");
+ }
+ }
+
+ public byte[] getData() {
+ if (bOut != null) {
+ return bOut.toByteArray();
+ } else if (tempFile != null) {
+ if (tempFile.exists()) {
+ FileInputStream fIn = null;
+ try {
+ long fileLength = tempFile.length();
+ if (fileLength > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("File content is too long to be allocated
as byte[]");
+ }
+
+ fIn = new FileInputStream(tempFile);
+
+ byte[] fileData = new byte[(int)fileLength];
+ int totalRead = 0;
+ int read = 0;
+ do {
+ read = fIn.read(fileData, totalRead, fileData.length - totalRead);
+ if (read > 0) {
+ totalRead += read;
+ }
+ } while (read > 0);
+
+ return fileData;
+ } catch (IOException ex) { /* too bad? */
+ logger.error(ex.getMessage(), ex);
+ } finally {
+ if (fIn != null) {
+ try {
+ fIn.close();
+ } catch (IOException e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public InputStream getInputStream() {
+ if (bOut != null) {
+ return new ByteArrayInputStream(bOut.toByteArray());
+ } else if (tempFile != null) {
+ try {
+ return new FileInputStream(tempFile) {
+ @Override
+ public void close() throws IOException {
+ super.close();
+ tempFile.delete();
+ }
+ };
+ } catch (FileNotFoundException ex) {
+ logger.error(ex.getMessage(), ex);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public void complete() throws IOException {
+ if (fOut != null) {
+ try {
+ fOut.close();
+ } catch (IOException ex) {
+ logger.error(ex.getMessage(), ex);
+ }
+ fOut = null;
+ }
+ }
+
+ public void handle(byte[] bytes, int length)
+ throws IOException {
+ // read += length;
+ if (fOut != null) {
+ fOut.write(bytes, 0, length);
+ fOut.flush();
+ } else {
+ if (bOut == null) {
+ bOut = new ByteArrayOutputStream();
+ }
+ bOut.write(bytes, 0, length);
+ }
+
+ fileSize += length;
+ }
+}
\ No newline at end of file
Modified:
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/MultipartRequest.java
===================================================================
---
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/MultipartRequest.java 2009-10-30
05:08:36 UTC (rev 15784)
+++
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/MultipartRequest.java 2009-10-30
14:49:48 UTC (rev 15785)
@@ -21,17 +21,13 @@
package org.ajax4jsf.request;
-import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
+import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
-import java.rmi.server.UID;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
@@ -48,7 +44,10 @@
import javax.servlet.http.HttpServletRequestWrapper;
import org.ajax4jsf.exception.FileUploadException;
+import org.ajax4jsf.request.ByteSequenceMatcher.BytesHandler;
import org.ajax4jsf.webapp.BaseXMLFilter;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.richfaces.component.FileUploadConstants;
import org.richfaces.model.UploadItem;
@@ -63,8 +62,11 @@
private static final String PARAM_CONTENT_TYPE = "Content-Type";
private static final int BUFFER_SIZE = 2048;
- private static final int CHUNK_SIZE = 512;
+ private static final int CHUNK_SIZE = 1024;
+ private static final int MAX_HEADER_SIZE = 32768;
+ private static final Log logger = LogFactory.getLog(MultipartRequest.class);
+
private boolean createTempFiles;
private String uid;
@@ -74,242 +76,33 @@
private Integer contentLength = 0;
private int bytesRead = 0;
-
- private int read = 0;
//we shouldn't allow to stop until request reaches PhaseListener because of
portlets
private volatile boolean canStop = false;
-
+
private Map<String, Param> parameters = null;
private Map<String, Object> percentMap = null;
-
+
private Map<String, Integer> requestSizeMap = null;
-
+
private Map<String, String> requestKeysMap = null;
-
+
private String requestKey = null;
-
+
private MultipartRequestRegistry requestRegistry;
-
+
private List<String> keys = new ArrayList<String>();
- private enum ReadState {
- BOUNDARY, HEADERS, DATA
- }
+ private byte[] boundaryMarker;
+ private ByteSequenceMatcher sequenceMatcher;
+
private static final byte CR = 0x0d;
private static final byte LF = 0x0a;
private static final byte[] CR_LF = { CR, LF };
+ private static final byte[] HYPHENS = { 0x2d, 0x2d }; //'--'
- private abstract class Param {
- private String name;
-
- public Param(String name) {
- this.name = name;
- }
-
- public String getName() {
- return name;
- }
-
- public abstract void appendData(byte[] data, int start, int length)
- throws IOException;
-
- }
-
- private class ValueParam extends Param {
- private Object value = null;
- private ByteArrayOutputStream buf = new ByteArrayOutputStream();
-
- public ValueParam(String name) {
- super(name);
- }
-
- @Override
- public void appendData(byte[] data, int start, int length)
- throws IOException {
- // read += length;
- buf.write(data, start, length);
- }
-
- public void complete() throws UnsupportedEncodingException {
- String val = encoding == null ? new String(buf.toByteArray())
- : new String(buf.toByteArray(), encoding);
- if (value == null) {
- value = val;
- } else {
- if (!(value instanceof List)) {
- List<String> v = new ArrayList<String>();
- v.add((String) value);
- value = v;
- }
-
- ((List) value).add(val);
- }
- buf.reset();
- }
-
- public Object getValue() {
- return value;
- }
- }
-
- private class FileParam extends Param {
- private String filename;
- private String contentType;
- private int fileSize;
-
- private ByteArrayOutputStream bOut = null;
- private FileOutputStream fOut = null;
- private File tempFile = null;
-
- public FileParam(String name) {
- super(name);
- keys.add(name);
- }
-
- public Object getFile() {
- if (null != tempFile) {
- if (fOut != null) {
- try {
- fOut.close();
- } catch (IOException ex) {
- }
- fOut = null;
- }
- return tempFile;
- } else if (null != bOut) {
- return bOut.toByteArray();
- }
- return null;
- }
-
- public String getFilename() {
- return filename;
- }
-
- public void setFilename(String filename) {
- this.filename = filename;
- }
-
- public String getContentType() {
- return contentType;
- }
-
- public void setContentType(String contentType) {
- this.contentType = contentType;
- }
-
- public int getFileSize() {
- return fileSize;
- }
-
- public File createTempFile() {
- try {
-
- tempFile = File.createTempFile(new UID().toString().replace(
- ":", "-"), ".upload");
- // tempFile.deleteOnExit();
- fOut = new FileOutputStream(tempFile);
- } catch (IOException ex) {
- throw new FileUploadException("Could not create temporary file");
- }
- return tempFile;
- }
-
- public void deleteFile() {
- try {
- if (fOut != null) {
- fOut.close();
- if (tempFile != null) {
- tempFile.delete();
- }
- }
- } catch (Exception e) {
- throw new FileUploadException("Could not delete temporary file");
- }
- }
-
- @Override
- public void appendData(byte[] data, int start, int length)
- throws IOException {
- // read += length;
- if (fOut != null) {
- fOut.write(data, start, length);
- fOut.flush();
- } else {
- if (bOut == null)
- bOut = new ByteArrayOutputStream();
- bOut.write(data, start, length);
- }
-
- fileSize += length;
- }
-
- public byte[] getData() {
- if (fOut != null) {
- try {
- fOut.close();
- } catch (IOException ex) {
- }
- fOut = null;
- }
-
- if (bOut != null) {
- return bOut.toByteArray();
- } else if (tempFile != null) {
- if (tempFile.exists()) {
- try {
- FileInputStream fIn = new FileInputStream(tempFile);
- ByteArrayOutputStream bOut = new ByteArrayOutputStream();
- byte[] buf = new byte[512];
- int read = fIn.read(buf);
- while (read != -1) {
- bOut.write(buf, 0, read);
- read = fIn.read(buf);
- }
- bOut.flush();
-
- fIn.close();
- tempFile.delete();
- return bOut.toByteArray();
- } catch (IOException ex) { /* too bad? */
- }
- }
- }
-
- return null;
- }
-
- public InputStream getInputStream() {
- if (fOut != null) {
- try {
- fOut.close();
- } catch (IOException ex) {
- }
- fOut = null;
- }
-
- if (bOut != null) {
- return new ByteArrayInputStream(bOut.toByteArray());
- } else if (tempFile != null) {
- try {
- return new FileInputStream(tempFile) {
- @Override
- public void close() throws IOException {
- super.close();
- tempFile.delete();
- }
- };
- } catch (FileNotFoundException ex) {
- }
- }
-
- return null;
- }
- }
-
private boolean shouldStop = false;
private boolean canceled;
@@ -330,6 +123,43 @@
}
}
+ private class ControlledProgressInputStream extends FilterInputStream {
+
+ protected ControlledProgressInputStream(InputStream in) {
+ super(in);
+ }
+
+ @Override
+ public int read() throws IOException {
+ int read = super.read();
+ if (read >= 0) {
+ bytesRead++;
+ fillProgressInfo();
+ }
+ return read;
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ int read = super.read(b);
+ if (read > 0) {
+ bytesRead += read;
+ fillProgressInfo();
+ }
+ return read;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int read = super.read(b, off, len);
+ if (read > 0) {
+ bytesRead += read;
+ fillProgressInfo();
+ }
+ return read;
+ }
+ }
+
private String decodeFileName(String name) {
String fileName = null;
@@ -361,7 +191,7 @@
public void cancel() {
this.canceled = true;
-
+
if (parameters != null) {
Iterator<Param> it = parameters.values().iterator();
while (it.hasNext()) {
@@ -373,191 +203,145 @@
}
}
- private byte[] boundaryMarker;
-
- private byte[] buffer;
-
- private ReadState readState;
-
- private InputStream input;
-
- private int pos = 0;
+ private void readNext() throws IOException {
+ Param p = readHeader();
+ if (p != null) {
+ try {
+ readData(p);
+ } finally {
+ try {
+ p.complete();
+ } catch (IOException e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ }
+ }
- private int zeroReadAttempts = 20; // 20 attempts to read not-readable data
-
- private void fillBuffer() throws IOException {
- if (pos < read) {
- // move the bytes that weren't read to the start of
- // the
- // buffer
- int bytesNotRead = read - pos;
-
- if (bytesNotRead != buffer.length) {
- System.arraycopy(buffer, pos, buffer, 0,
- bytesNotRead);
- read = input.read(buffer, bytesNotRead,
- buffer.length - bytesNotRead);
-
- if (read != 0 || --zeroReadAttempts != 0) {
- if (read > 0) {
- bytesRead += read;
- }
+ private Param createParam(Map<String, String> headers) {
+ Param param = null;
+ String paramName = headers.get(PARAM_NAME);
+ if (paramName != null) {
+ if (headers.containsKey(PARAM_FILENAME)) {
+ FileParam fp = new FileParam(paramName);
+ this.keys.add(paramName);
- read += bytesNotRead;
- } else {
- //read is already zero
- //read = 0;
+ if (createTempFiles) {
+ fp.createTempFile();
}
+ fp.setContentType(headers.get(PARAM_CONTENT_TYPE));
+ fp.setFilename(decodeFileName(headers.get(PARAM_FILENAME)));
+ param = fp;
} else {
- read = bytesNotRead;
+ if (parameters.containsKey(paramName)) {
+ param = parameters.get(paramName);
+ } else {
+ param = new ValueParam(paramName, encoding);
+ }
}
- } else {
- read = input.read(buffer);
- if (read > 0) {
- bytesRead += read;
+ if (!parameters.containsKey(paramName)) {
+ parameters.put(paramName, param);
}
}
-
- fillProgressInfo();
- pos = 0;
+
+ return param;
}
-
- private void readNext() throws IOException {
- Param p = readHeader();
- readData(p);
- }
- private void readData(Param p) throws IOException {
- int localRead = this.read;
-
- while (localRead > 0) {
- for (int i = this.pos; i < localRead; i++) {
- // If we've encountered another boundary...
- if (checkSequence(buffer, i - boundaryMarker.length
- - CR_LF.length, CR_LF)
- && checkSequence(buffer, i, boundaryMarker)) {
- // Write any data before the boundary (that
- // hasn't
- // already been written) to the param
- if (pos < i - boundaryMarker.length
- - CR_LF.length - 1) {
- p.appendData(buffer, pos, i - pos
- - boundaryMarker.length
- - CR_LF.length - 1);
- }
+ private boolean initialized = false;
- if (p instanceof ValueParam)
- ((ValueParam) p).complete();
+ private static final BytesHandler NOOP_HANDLER = new BytesHandler() {
+ public void handle(byte[] bytes, int length) {
+ //do nothing
+ }
+ };
- if (checkSequence(buffer, i + CR_LF.length,
- CR_LF)) {
- i += CR_LF.length;
- pos = i + 1;
- } else {
- pos = i;
- }
+ private class HeadersHandler implements BytesHandler {
- readState = ReadState.HEADERS;
- break;
- }
- // Otherwise write whatever data we have to the
- // param
- else if (i > (pos + boundaryMarker.length
- + CHUNK_SIZE + CR_LF.length)) {
- p.appendData(buffer, pos, CHUNK_SIZE);
- pos += CHUNK_SIZE;
+ private ByteArrayOutputStream baos = new ByteArrayOutputStream(BUFFER_SIZE);
+ public void handle(byte[] bytes, int length) throws IOException {
+ if (length != 0) {
+ if (baos.size() + length > MAX_HEADER_SIZE) {
+ throw new IOException("Header section is too big");
}
+
+ baos.write(bytes, 0, length);
}
-
- if (ReadState.DATA.equals(readState)) {
- fillBuffer();
- localRead = this.read;
+ }
+
+ public boolean dataEquals(byte[] bytes) {
+ return (baos.size() == bytes.length) && Arrays.equals(HYPHENS,
baos.toByteArray());
+ }
+
+ public String asString() throws UnsupportedEncodingException {
+ if (encoding != null) {
+ return baos.toString(encoding);
} else {
- break;
+ return baos.toString();
}
}
+
+ public void reset() {
+ baos.reset();
+ }
+
}
-
+
+ private HeadersHandler headersHandler = null;
+
private Param readHeader() throws IOException {
- Param p = null;
+ if (sequenceMatcher.isEOF()) {
+ return null;
+ }
- Map<String, String> headers = new HashMap<String, String>();
+ if (headersHandler == null) {
+ headersHandler = new HeadersHandler();
+ } else {
+ headersHandler.reset();
+ }
- int localRead = this.read;
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
-
- while (localRead > 0) {
- for (int i = this.pos; i < localRead; i++) {
- if (checkSequence(buffer, i, CR_LF)) {
- baos.write(buffer, pos, i - pos - 1);
+ sequenceMatcher.setBytesHandler(headersHandler);
+ sequenceMatcher.findSequence(-1, CR_LF);
- String param = (encoding == null) ?
- new String(baos.toByteArray()) :
- new String(baos.toByteArray(), encoding);
+ if (sequenceMatcher.isMatchedAndNotEOF() &&
!headersHandler.dataEquals(HYPHENS)) {
+ headersHandler.reset();
+
+ sequenceMatcher.findSequence(-1, CR_LF, CR_LF);
+
+ if (!sequenceMatcher.isMatchedAndNotEOF()) {
+ throw new IOException("Request header cannot be read");
+ }
- parseParams(param, "; ", headers);
-
- if (checkSequence(buffer, i + CR_LF.length,
- CR_LF)) {
- readState = ReadState.DATA;
- i += CR_LF.length;
- pos = i + 1;
-
- String paramName = headers.get(PARAM_NAME);
- if (paramName != null) {
- if (headers.containsKey(PARAM_FILENAME)) {
- FileParam fp = new FileParam(
- paramName);
- if (createTempFiles)
- fp.createTempFile();
- fp.setContentType(headers
- .get(PARAM_CONTENT_TYPE));
- fp
- .setFilename(decodeFileName(headers
- .get(PARAM_FILENAME)));
- p = fp;
- } else {
- if (parameters
- .containsKey(paramName)) {
- p = parameters.get(paramName);
- } else {
- p = new ValueParam(paramName);
- }
- }
-
- if (!parameters.containsKey(paramName)) {
- parameters.put(paramName, p);
- }
- }
-
- headers.clear();
- baos.reset();
-
- break;
- } else {
- pos = i + 1;
- baos.reset();
- }
- }
+ String headersString = headersHandler.asString();
+ Map<String, String> headers = new HashMap<String, String>();
+ String[] split = headersString.split("\r\n");
+ for (String headerString : split) {
+ parseParams(headerString, "; ", headers);
}
- if (ReadState.HEADERS.equals(readState)) {
- baos.write(buffer, pos, read - pos);
- pos = read;
- fillBuffer();
- localRead = this.read;
- } else {
- break;
- }
+ return createParam(headers);
}
- return p;
+
+ return null;
}
- boolean initialized = false;
-
+ private void readProlog() throws IOException {
+ sequenceMatcher.setBytesHandler(NOOP_HANDLER);
+ sequenceMatcher.findSequence(-1, HYPHENS, boundaryMarker);
+ if (!sequenceMatcher.isMatchedAndNotEOF()) {
+ throw new IOException("Request prolog cannot be read");
+ }
+ }
+
+ private void readData(final Param param) throws IOException {
+ sequenceMatcher.setBytesHandler(param);
+ sequenceMatcher.findSequence(CHUNK_SIZE, CR_LF, HYPHENS, boundaryMarker);
+ if (!this.sequenceMatcher.isMatchedAndNotEOF()) {
+ throw new IOException("Request data cannot be read");
+ }
+ }
+
private void initialize() throws IOException {
if (!initialized) {
initialized = true;
@@ -567,61 +351,39 @@
throw new FileUploadException("The request was rejected because "
+ "no multipart boundary was found");
}
-
+
+ if (HYPHENS.length + boundaryMarker.length + CHUNK_SIZE + CR_LF.length >
BUFFER_SIZE) {
+ throw new FileUploadException("Boundary marker is too long");
+ }
+
this.encoding = getCharacterEncoding();
this.parameters = new HashMap<String, Param>();
- this.buffer = new byte[BUFFER_SIZE];
+ InputStream input = new ControlledProgressInputStream(getInputStream());
- this.readState = ReadState.BOUNDARY;
+ this.sequenceMatcher = new ByteSequenceMatcher(input, BUFFER_SIZE);
- this.input = getInputStream();
-
setupProgressData();
-
- fillBuffer();
- int localRead = this.read;
-
- while (localRead > 0) {
- for (int i = 0; i < localRead; i++) {
- if (checkSequence(buffer, i, boundaryMarker)
- && checkSequence(buffer, i + 2, CR_LF)) {
- readState = ReadState.HEADERS;
- i += 2;
- pos = i + 1;
-
- break;
- }
- }
-
- if (ReadState.BOUNDARY.equals(readState)) {
- pos = read - (boundaryMarker.length + CR_LF.length) + 1;
- fillBuffer();
- localRead = this.read;
- } else {
- break;
- }
- }
-
+
+ readProlog();
}
}
-
+
public void parseRequest() {
canStop = true;
-
+
setupProgressData();
-
+
try {
initialize();
-
- while (read > 0) {
+
+ while (!sequenceMatcher.isEOF()) {
readNext();
}
-
} catch (IOException e) {
this.cancel();
-
+
if (!this.shouldStop) {
throw new FileUploadException("IO Error parsing multipart request", e);
}
@@ -629,9 +391,10 @@
canStop = false;
}
}
-
+
public static MultipartRequest lookupRequest(FacesContext context, String uploadId) {
Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
+ @SuppressWarnings("unchecked")
Map<String, String> requestKeys = (Map<String, String>)
sessionMap.get(FileUploadConstants.REQUEST_KEYS_BEAN_NAME);
if (requestKeys != null) {
String requestKey = requestKeys.get(uploadId);
@@ -645,10 +408,10 @@
}
}
}
-
+
return null;
}
-
+
@SuppressWarnings("unchecked")
private void setupProgressData() {
if (percentMap == null || requestSizeMap == null || requestKeysMap == null) {
@@ -676,17 +439,17 @@
sessionMap.put(FileUploadConstants.REQUEST_SIZE_BEAN_NAME, requestSizeMap);
}
}
-
+
if (requestKeysMap == null) {
requestKeysMap = (Map<String, String>)
sessionMap.get(FileUploadConstants.REQUEST_KEYS_BEAN_NAME);
if (requestKeysMap == null) {
requestKeysMap = new ConcurrentHashMap<String, String>();
sessionMap.put(FileUploadConstants.REQUEST_KEYS_BEAN_NAME, requestKeysMap);
}
-
+
}
}
-
+
percentMap.put(uploadId, Double.valueOf(0));
requestSizeMap.put(uploadId, getSize());
@@ -703,7 +466,7 @@
private void fillProgressInfo() {
setupProgressData();
-
+
if (percentMap != null) {
Double percent = (Double) (100.0 * this.bytesRead / this.contentLength);
percentMap.put(uid, percent);
@@ -715,8 +478,9 @@
Map<String, String> params = parseParams(contentType, ";");
String boundaryStr = (String) params.get("boundary");
- if (boundaryStr == null)
+ if (boundaryStr == null) {
return null;
+ }
try {
return boundaryStr.getBytes("ISO-8859-1");
@@ -725,33 +489,11 @@
}
}
- /**
- * Checks if a specified sequence of bytes ends at a specific position
- * within a byte array.
- *
- * @param data
- * @param pos
- * @param seq
- * @return boolean indicating if the sequence was found at the specified
- * position
- */
- private boolean checkSequence(byte[] data, int pos, byte[] seq) {
- if (pos - seq.length < -1 || pos >= data.length)
- return false;
-
- for (int i = 0; i < seq.length; i++) {
- if (data[(pos - seq.length) + i + 1] != seq[i])
- return false;
- }
-
- return true;
- }
-
private static final Pattern PARAM_VALUE_PATTERN = Pattern
- .compile("^\\s*([^\\s=]+)\\s*[=:]\\s*(.+)\\s*$");
+ .compile("^\\s*([^\\s=]+)\\s*[=:]\\s*(.+)\\s*$");
private static final Pattern FILE_NAME_PATTERN = Pattern
- .compile(".*filename=\"(.*)\"");
+ .compile(".*filename=\"(.*)\"");
private Map<String, String> parseParams(String paramStr, String separator) {
Map<String, String> paramMap = new HashMap<String, String>();
@@ -799,13 +541,13 @@
if (parameters != null) {
param = parameters.get(name);
}
-
+
if (param == null) {
if (!canceled) {
try {
initialize();
- while (param == null && read > 0) {
+ while (param == null && !sequenceMatcher.isEOF()) {
readNext();
param = parameters.get(name);
}
@@ -815,7 +557,7 @@
}
}
}
-
+
return param;
}
@@ -824,9 +566,11 @@
}
@Override
+ @SuppressWarnings("unchecked")
public Enumeration getParameterNames() {
- if (parameters == null)
+ if (parameters == null) {
parseRequest();
+ }
return Collections.enumeration(parameters.keySet());
}
@@ -872,8 +616,9 @@
Param p = getParam(name);
if (p != null && p instanceof ValueParam) {
ValueParam vp = (ValueParam) p;
- if (vp.getValue() instanceof String)
+ if (vp.getValue() instanceof String) {
return (String) vp.getValue();
+ }
} else if (p != null && p instanceof FileParam) {
return "---BINARY DATA---";
} else {
@@ -886,12 +631,13 @@
@Override
public String[] getParameterValues(String name) {
parseRequest();
-
+
Param p = getParam(name);
if (p != null && p instanceof ValueParam) {
ValueParam vp = (ValueParam) p;
- if (vp.getValue() instanceof List) {
- List vals = (List) vp.getValue();
+ if (vp.getValue() instanceof List<?>) {
+ @SuppressWarnings("unchecked")
+ List<String> vals = (List<String>) vp.getValue();
String[] values = new String[vals.size()];
vals.toArray(values);
return values;
@@ -904,12 +650,13 @@
}
@Override
+ @SuppressWarnings("unchecked")
public Map getParameterMap() {
- if (parameters == null)
+ if (parameters == null) {
parseRequest();
+ }
- Map<String, Object> params = new HashMap<String, Object>(super
- .getParameterMap());
+ Map<String, Object> params = new HashMap<String,
Object>(super.getParameterMap());
for (String name : parameters.keySet()) {
Param p = parameters.get(name);
@@ -925,7 +672,7 @@
return params;
}
-
+
public List<UploadItem> getUploadItems () {
List <UploadItem> uploadItems = new ArrayList<UploadItem>();
for (String k : keys) {
@@ -933,16 +680,16 @@
}
return uploadItems;
}
-
+
public boolean isFormUpload() {
return "_richfaces_form_upload".equals(uid);
}
-
+
@Override
public String getHeader(String name) {
if (!"Accept".equals(name)) {
return super.getHeader(name);
- }else {
+ } else {
return BaseXMLFilter.TEXT_HTML;
}
}
@@ -952,16 +699,16 @@
shouldStop = true;
}
}
-
+
public boolean isStopped() {
return this.shouldStop;
}
-
+
public boolean isDone() {
return !(this.shouldStop && (this.canceled ||
this.contentLength != null && this.contentLength.intValue() !=
this.bytesRead));
}
-
+
@Override
public String getContentType() {
return "application/x-www-form-urlencoded";
@@ -970,18 +717,18 @@
protected String getUploadId() {
return uid;
}
-
+
public void clearRequestData() {
String uploadId = getUploadId();
-
+
if (percentMap != null) {
percentMap.remove(uploadId);
}
-
+
if (requestSizeMap != null) {
requestSizeMap.remove(uploadId);
}
-
+
if (requestKeysMap != null) {
requestKeysMap.remove(uploadId);
}
Added:
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/Param.java
===================================================================
--- branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/Param.java
(rev 0)
+++
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/Param.java 2009-10-30
14:49:48 UTC (rev 15785)
@@ -0,0 +1,41 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat, Inc. and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This 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 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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 this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+
+package org.ajax4jsf.request;
+
+import java.io.IOException;
+
+import org.ajax4jsf.request.ByteSequenceMatcher.BytesHandler;
+
+abstract class Param implements BytesHandler {
+ private String name;
+
+ public Param(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public abstract void complete() throws IOException;
+}
\ No newline at end of file
Added:
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/ValueParam.java
===================================================================
---
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/ValueParam.java
(rev 0)
+++
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/request/ValueParam.java 2009-10-30
14:49:48 UTC (rev 15785)
@@ -0,0 +1,68 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat, Inc. and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This 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 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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 this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+
+package org.ajax4jsf.request;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+class ValueParam extends Param {
+
+ private Object value = null;
+ private ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ private String encoding;
+
+ public ValueParam(String name, String encoding) {
+ super(name);
+ this.encoding = encoding;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void complete() throws IOException {
+ String val = this.encoding == null ? new String(buf.toByteArray())
+ : new String(buf.toByteArray(), this.encoding);
+ if (value == null) {
+ value = val;
+ } else {
+ if (!(value instanceof List<?>)) {
+ List<String> v = new ArrayList<String>();
+ v.add((String) value);
+ value = v;
+ }
+
+ ((List<String>) value).add(val);
+ }
+ buf.reset();
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void handle(byte[] bytes, int length)
+ throws IOException {
+ // read += length;
+ buf.write(bytes, 0, length);
+ }
+}
\ No newline at end of file