Author: bago
Date: Sat Jun 21 07:39:55 2008
New Revision: 670206
URL: http://svn.apache.org/viewvc?rev=670206&view=rev
Log:
Rewritten CRLFOutputStream and ExtraDotOutputStream to support buffered
operations.
The new algorythm also take care of streams ending with "\r" (previosly they
was not replaced unless we called a specific method).
Modified:
james/server/trunk/core-library/src/main/java/org/apache/james/util/CRLFOutputStream.java
james/server/trunk/core-library/src/main/java/org/apache/james/util/ExtraDotOutputStream.java
james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/CRLFOutputStreamTest.java
james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/ExtraDotOutputStreamTest.java
Modified:
james/server/trunk/core-library/src/main/java/org/apache/james/util/CRLFOutputStream.java
URL:
http://svn.apache.org/viewvc/james/server/trunk/core-library/src/main/java/org/apache/james/util/CRLFOutputStream.java?rev=670206&r1=670205&r2=670206&view=diff
==============================================================================
---
james/server/trunk/core-library/src/main/java/org/apache/james/util/CRLFOutputStream.java
(original)
+++
james/server/trunk/core-library/src/main/java/org/apache/james/util/CRLFOutputStream.java
Sat Jun 21 07:39:55 2008
@@ -17,8 +17,6 @@
* under the License. *
****************************************************************/
-
-
package org.apache.james.util;
import java.io.FilterOutputStream;
@@ -28,64 +26,136 @@
/**
* A Filter for use with SMTP or other protocols in which lines must end with
* CRLF. Converts every "isolated" occourency of \r or \n with \r\n
+ *
+ * RFC 2821 #2.3.7 mandates that line termination is CRLF, and that CR and LF
+ * must not be transmitted except in that pairing. If we get a naked LF,
convert
+ * to CRLF.
+ *
*/
public class CRLFOutputStream extends FilterOutputStream {
/**
* Counter for number of last (0A or 0D).
*/
- protected int countLast0A0D;
+ protected int statusLast;
+
+ protected final static int LAST_WAS_OTHER = 0;
+
+ protected final static int LAST_WAS_CR = 1;
+
+ protected final static int LAST_WAS_LF = 2;
+
+ protected boolean startOfLine = true;
/**
* Constructor that wraps an OutputStream.
- *
- * @param out the OutputStream to be wrapped
+ *
+ * @param out
+ * the OutputStream to be wrapped
*/
public CRLFOutputStream(OutputStream out) {
super(out);
- countLast0A0D = 2; // we already assume a CRLF at beginning (otherwise
TOP would not work correctly !)
+ statusLast = LAST_WAS_LF; // we already assume a CRLF at beginning
+ // (otherwise TOP would not work correctly
+ // !)
}
/**
- * Writes a byte to the stream
- * Fixes any naked CR or LF to the RFC 2821 mandated CFLF
- * pairing.
- *
- * @param b the byte to write
- *
- * @throws IOException if an error occurs writing the byte
+ * Writes a byte to the stream Fixes any naked CR or LF to the RFC 2821
+ * mandated CFLF pairing.
+ *
+ * @param b
+ * the byte to write
+ *
+ * @throws IOException
+ * if an error occurs writing the byte
*/
public void write(int b) throws IOException {
switch (b) {
case '\r':
- if (countLast0A0D == 1) out.write('\n'); // two CR in a row,
so insert an LF first
- countLast0A0D = 1;
+ out.write('\r');
+ out.write('\n');
+ startOfLine = true;
+ statusLast = LAST_WAS_CR;
break;
case '\n':
- /* RFC 2821 #2.3.7 mandates that line termination is
- * CRLF, and that CR and LF must not be transmitted
- * except in that pairing. If we get a naked LF,
- * convert to CRLF.
- */
- if (countLast0A0D != 1) out.write('\r');
- countLast0A0D = 2;
+ if (statusLast != LAST_WAS_CR) {
+ out.write('\r');
+ out.write('\n');
+ startOfLine = true;
+ }
+ statusLast = LAST_WAS_LF;
break;
default:
- // we're no longer at the start of a line
- countLast0A0D = 0;
+ // we're no longer at the start of a line
+ out.write(b);
+ startOfLine = false;
+ statusLast = LAST_WAS_OTHER;
break;
}
- out.write(b);
}
/**
+ * Provides an extension point for ExtraDotOutputStream to be able to add
dots
+ * at the beginning of new lines.
+ *
+ * @see java.io.FilterOutputStream#write(byte[], int, int)
+ */
+ protected void writeChunk(byte buffer[], int offset, int length) throws
IOException {
+ out.write(buffer, offset, length);
+ }
+
+ /**
+ * @see java.io.FilterOutputStream#write(byte[], int, int)
+ */
+ public synchronized void write(byte buffer[], int offset, int length)
+ throws IOException {
+ /* optimized */
+ int lineStart = offset;
+ for (int i = offset; i < length + offset; i++) {
+ switch(buffer[i]) {
+ case '\r':
+ // CR case. Write down the last line
+ // and position the new lineStart at the next char
+ writeChunk(buffer, lineStart, i - lineStart);
+ out.write('\r');
+ out.write('\n');
+ startOfLine = true;
+ lineStart = i + 1;
+ statusLast = LAST_WAS_CR;
+ break;
+ case '\n':
+ if (statusLast != LAST_WAS_CR) {
+ writeChunk(buffer, lineStart, i - lineStart);
+ out.write('\r');
+ out.write('\n');
+ startOfLine = true;
+ }
+ lineStart = i + 1;
+ statusLast = LAST_WAS_LF;
+ break;
+ default:
+ statusLast = LAST_WAS_OTHER;
+ }
+ }
+ if (length + offset > lineStart) {
+ writeChunk(buffer, lineStart, length + offset - lineStart);
+ startOfLine = false;
+ }
+ }
+
+
+ /**
* Ensure that the stream is CRLF terminated.
*
- * @throws IOException if an error occurs writing the byte
+ * @throws IOException
+ * if an error occurs writing the byte
*/
public void checkCRLFTerminator() throws IOException {
- if (countLast0A0D != 2) {
- write('\n');
+ if (statusLast == LAST_WAS_OTHER) {
+ out.write('\r');
+ out.write('\n');
+ statusLast = LAST_WAS_CR;
}
}
}
Modified:
james/server/trunk/core-library/src/main/java/org/apache/james/util/ExtraDotOutputStream.java
URL:
http://svn.apache.org/viewvc/james/server/trunk/core-library/src/main/java/org/apache/james/util/ExtraDotOutputStream.java?rev=670206&r1=670205&r2=670206&view=diff
==============================================================================
---
james/server/trunk/core-library/src/main/java/org/apache/james/util/ExtraDotOutputStream.java
(original)
+++
james/server/trunk/core-library/src/main/java/org/apache/james/util/ExtraDotOutputStream.java
Sat Jun 21 07:39:55 2008
@@ -40,6 +40,20 @@
}
/**
+ * Overrides super writeChunk in order to add a "." if the previous chunk
ended with
+ * a new line and a new chunk starts with "."
+ *
+ * @see org.apache.james.util.CRLFOutputStream#writeChunk(byte[], int, int)
+ */
+ protected void writeChunk(byte buffer[], int offset, int length) throws
IOException {
+ if (length > 0 && buffer[offset] == '.' && startOfLine) {
+ // add extra dot (the first of the pair)
+ out.write('.');
+ }
+ super.writeChunk(buffer, offset, length);
+ }
+
+ /**
* Writes a byte to the stream, adding dots where appropriate.
* Also fixes any naked CR or LF to the RFC 2821 mandated CRLF
* pairing.
@@ -49,7 +63,7 @@
* @throws IOException if an error occurs writing the byte
*/
public void write(int b) throws IOException {
- if (b == '.' && countLast0A0D == 2) {
+ if (b == '.' && statusLast != LAST_WAS_OTHER) {
// add extra dot (the first of the pair)
out.write('.');
}
Modified:
james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/CRLFOutputStreamTest.java
URL:
http://svn.apache.org/viewvc/james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/CRLFOutputStreamTest.java?rev=670206&r1=670205&r2=670206&view=diff
==============================================================================
---
james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/CRLFOutputStreamTest.java
(original)
+++
james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/CRLFOutputStreamTest.java
Sat Jun 21 07:39:55 2008
@@ -58,8 +58,6 @@
assertEquals("Test\r\n",bOut.toString());
// if the stream ends with \r we should simply add the \n
os.write("A line with incomplete ending\r".getBytes());
- os.flush();
- assertEquals("Test\r\nA line with incomplete
ending\r",bOut.toString());
os.checkCRLFTerminator();
os.flush();
assertEquals("Test\r\nA line with incomplete
ending\r\n",bOut.toString());
@@ -68,5 +66,31 @@
os.flush();
assertEquals("Test\r\nA line with incomplete
ending\r\n",bOut.toString());
}
+
+ public void testMixedSizeChunks() throws IOException {
+ String[] data = new String[] {".","This is a test","","\r\n","of the
thing.\r","\nWe should not have much trouble.\r","\n",".","doubled?\r\n","or
not?\n.","double","d\nor not?\r","\n\r\n","\n\n\r","\r\r\n"};
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ OutputStream os = new CRLFOutputStream(bOut);
+ for(int i = 0; i < data.length; i++) {
+ os.write(data[i].getBytes());
+ os.flush();
+ }
+ String expected = ".This is a test\r\nof the thing.\r\nWe should not
have much trouble.\r\n.doubled?\r\nor not?\r\n.doubled\r\nor
not?\r\n\r\n\r\n\r\n\r\n\r\n\r\n";
+ assertEquals(expected,bOut.toString());
+ }
+
+
+ public void testBytePerByte() throws IOException {
+ String data = ".This is a test\r\nof the thing.\r\nWe should not have
much trouble.\r\n.doubled?\r\nor not?\n.doubled\nor not?\r\n\r\n\n\n\r\r\r\n";
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ OutputStream os = new CRLFOutputStream(bOut);
+ byte[] buffer = data.getBytes();
+ for (int i = 0; i < buffer.length; i++) {
+ os.write(buffer[i]);
+ }
+ os.flush();
+ String expected = ".This is a test\r\nof the thing.\r\nWe should not
have much trouble.\r\n.doubled?\r\nor not?\r\n.doubled\r\nor
not?\r\n\r\n\r\n\r\n\r\n\r\n\r\n";
+ assertEquals(expected,bOut.toString());
+ }
}
Modified:
james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/ExtraDotOutputStreamTest.java
URL:
http://svn.apache.org/viewvc/james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/ExtraDotOutputStreamTest.java?rev=670206&r1=670205&r2=670206&view=diff
==============================================================================
---
james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/ExtraDotOutputStreamTest.java
(original)
+++
james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/ExtraDotOutputStreamTest.java
Sat Jun 21 07:39:55 2008
@@ -59,7 +59,8 @@
// if the stream ends with \r we should simply add the \n
os.write("A line with incomplete ending\r".getBytes());
os.flush();
- assertEquals("Test\r\nA line with incomplete
ending\r",bOut.toString());
+ // no need to check this: this is an implementation detail
+ // assertEquals("Test\r\nA line with incomplete
ending\r",bOut.toString());
os.checkCRLFTerminator();
os.flush();
assertEquals("Test\r\nA line with incomplete
ending\r\n",bOut.toString());
@@ -69,4 +70,31 @@
assertEquals("Test\r\nA line with incomplete
ending\r\n",bOut.toString());
}
+
+ public void testMixedSizeChunks() throws IOException {
+ String[] data = new String[] {".","This is a test","","\r\n","of the
thing.\r","\nWe should not have much trouble.\r","\n",".","doubled?\r\n","or
not?\n.","double","d\nor not?\r","\n\r\n","\n\n\r","\r\r\n"};
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ OutputStream os = new ExtraDotOutputStream(bOut);
+ for(int i = 0; i < data.length; i++) {
+ os.write(data[i].getBytes());
+ os.flush();
+ }
+ String expected = "..This is a test\r\nof the thing.\r\nWe should not
have much trouble.\r\n..doubled?\r\nor not?\r\n..doubled\r\nor
not?\r\n\r\n\r\n\r\n\r\n\r\n\r\n";
+ assertEquals(expected,bOut.toString());
+ }
+
+
+ public void testBytePerByte() throws IOException {
+ String data = ".This is a test\r\nof the thing.\r\nWe should not have
much trouble.\r\n.doubled?\r\nor not?\n.doubled\nor not?\r\n\r\n\n\n\r\r\r\n";
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ OutputStream os = new ExtraDotOutputStream(bOut);
+ byte[] buffer = data.getBytes();
+ for (int i = 0; i < buffer.length; i++) {
+ os.write(buffer[i]);
+ }
+ os.flush();
+ String expected = "..This is a test\r\nof the thing.\r\nWe should not
have much trouble.\r\n..doubled?\r\nor not?\r\n..doubled\r\nor
not?\r\n\r\n\r\n\r\n\r\n\r\n\r\n";
+ assertEquals(expected,bOut.toString());
+ }
+
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]