- CMIS-331: Send Content-Disposition header when using AtomPub PUT for setContentStream
- CMIS-332: Support compression
- bugfixes
git-svn-id: https://svn.apache.org/repos/asf/chemistry/dotcmis/trunk@1082208 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/DotCMIS/binding/atompub/atompub.cs b/DotCMIS/binding/atompub/atompub.cs
index 02e5132..f3313a1 100644
--- a/DotCMIS/binding/atompub/atompub.cs
+++ b/DotCMIS/binding/atompub/atompub.cs
@@ -453,7 +453,7 @@
protected HttpUtils.Response Put(UrlBuilder url, string contentType, HttpUtils.Output writer)
{
- HttpUtils.Response resp = HttpUtils.InvokePUT(url, contentType, writer, Session);
+ HttpUtils.Response resp = HttpUtils.InvokePUT(url, contentType, null, writer, Session);
if ((int)resp.StatusCode < 200 || (int)resp.StatusCode > 299)
{
@@ -1945,7 +1945,7 @@
{
int b;
byte[] buffer = new byte[4096];
- while ((b = contentStream.Stream.Read(buffer, 0, buffer.Length)) > -1)
+ while ((b = contentStream.Stream.Read(buffer, 0, buffer.Length)) > 0)
{
stream.Write(buffer, 0, b);
}
@@ -1953,8 +1953,16 @@
contentStream.Stream.Close();
};
+ IDictionary<string, string> headers = null;
+ if (contentStream.FileName != null)
+ {
+ headers = new Dictionary<string, string>();
+ headers.Add(MimeHelper.ContentDisposition,
+ MimeHelper.EncodeContentDisposition(MimeHelper.DispositionAttachment, contentStream.FileName));
+ }
+
// send content
- HttpUtils.Response resp = HttpUtils.InvokePUT(url, contentStream.MimeType, output, Session);
+ HttpUtils.Response resp = HttpUtils.InvokePUT(url, contentStream.MimeType, headers, output, Session);
// check response code
if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.Created && resp.StatusCode != HttpStatusCode.NoContent)
diff --git a/DotCMIS/binding/http.cs b/DotCMIS/binding/http.cs
index aabd311..68baaa4 100644
--- a/DotCMIS/binding/http.cs
+++ b/DotCMIS/binding/http.cs
@@ -18,6 +18,7 @@
*/
using System;
using System.Diagnostics;
+using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
@@ -33,31 +34,31 @@
public static Response InvokeGET(UrlBuilder url, BindingSession session)
{
- return Invoke(url, "GET", null, null, session, null, null);
+ return Invoke(url, "GET", null, null, session, null, null, null);
}
public static Response InvokeGET(UrlBuilder url, BindingSession session, int? offset, int? length)
{
- return Invoke(url, "GET", null, null, session, offset, length);
+ return Invoke(url, "GET", null, null, session, offset, length, null);
}
public static Response InvokePOST(UrlBuilder url, String contentType, Output writer, BindingSession session)
{
- return Invoke(url, "POST", contentType, writer, session, null, null);
+ return Invoke(url, "POST", contentType, writer, session, null, null, null);
}
- public static Response InvokePUT(UrlBuilder url, String contentType, Output writer, BindingSession session)
+ public static Response InvokePUT(UrlBuilder url, String contentType, IDictionary<string, string> headers, Output writer, BindingSession session)
{
- return Invoke(url, "PUT", contentType, writer, session, null, null);
+ return Invoke(url, "PUT", contentType, writer, session, null, null, headers);
}
public static Response InvokeDELETE(UrlBuilder url, BindingSession session)
{
- return Invoke(url, "DELETE", null, null, session, null, null);
+ return Invoke(url, "DELETE", null, null, session, null, null, null);
}
private static Response Invoke(UrlBuilder url, String method, String contentType, Output writer, BindingSession session,
- int? offset, int? length)
+ int? offset, int? length, IDictionary<string, string> headers)
{
try
{
@@ -67,6 +68,7 @@
// create connection
HttpWebRequest conn = (HttpWebRequest)WebRequest.Create(url.Url);
conn.Method = method;
+ conn.UserAgent = "Apache Chemistry DotCMIS";
// set content type
if (contentType != null)
@@ -74,6 +76,15 @@
conn.ContentType = contentType;
}
+ // set additional headers
+ if (headers != null)
+ {
+ foreach (KeyValuePair<string, string> header in headers)
+ {
+ conn.Headers.Add(header.Key, header.Value);
+ }
+ }
+
// authenticate
AbstractAuthenticationProvider authProvider = session.GetAuthenticationProvider();
if (authProvider != null)
@@ -92,6 +103,13 @@
conn.AddRange(offset ?? 0);
}
+ // compression
+ string compressionFlag = session.GetValue(SessionParameter.Compression) as string;
+ if (compressionFlag != null && compressionFlag.ToLower().Equals("true"))
+ {
+ conn.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
+ }
+
// send data
if (writer != null)
{
@@ -255,4 +273,71 @@
return Url.ToString();
}
}
+
+ internal class MimeHelper
+ {
+ public const string ContentDisposition = "Content-Disposition";
+ public const string DispositionAttachment = "attachment";
+ public const string DispositionFilename = "filename";
+
+ private const string MIMESpecials = "()<>@,;:\\\"/[]?=" + "\t ";
+ private const string RFC2231Specials = "*'%" + MIMESpecials;
+ private static char[] HexDigits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ public static string EncodeContentDisposition(string disposition, string filename)
+ {
+ if (disposition == null)
+ {
+ disposition = DispositionAttachment;
+ }
+ return disposition + EncodeRFC2231(DispositionFilename, filename);
+ }
+
+ protected static string EncodeRFC2231(string key, string value)
+ {
+ StringBuilder buf = new StringBuilder();
+ bool encoded = EncodeRFC2231value(value, buf);
+ if (encoded)
+ {
+ return "; " + key + "*=" + buf.ToString();
+ }
+ else
+ {
+ return "; " + key + "=" + value;
+ }
+ }
+
+ protected static bool EncodeRFC2231value(string value, StringBuilder buf)
+ {
+ buf.Append("UTF-8");
+ buf.Append("''"); // no language
+ byte[] bytes;
+ try
+ {
+ bytes = UTF8Encoding.UTF8.GetBytes(value);
+ }
+ catch (Exception)
+ {
+ return true;
+ }
+
+ bool encoded = false;
+ for (int i = 0; i < bytes.Length; i++)
+ {
+ int ch = bytes[i] & 0xff;
+ if (ch <= 32 || ch >= 127 || RFC2231Specials.IndexOf((char)ch) != -1)
+ {
+ buf.Append('%');
+ buf.Append(HexDigits[ch >> 4]);
+ buf.Append(HexDigits[ch & 0xf]);
+ encoded = true;
+ }
+ else
+ {
+ buf.Append((char)ch);
+ }
+ }
+ return encoded;
+ }
+ }
}
diff --git a/DotCMIS/const.cs b/DotCMIS/const.cs
index 2c70fa2..0555cbd 100644
--- a/DotCMIS/const.cs
+++ b/DotCMIS/const.cs
@@ -55,6 +55,9 @@
// authentication provider
public const string AuthenticationProviderClass = "org.apache.chemistry.dotcmis.binding.auth.classname";
+ // compression flag
+ public const string Compression = "org.apache.chemistry.dotcmis.binding.compression";
+
// binding caches
public const string CacheSizeRepositories = "org.apache.chemistry.dotcmis.binding.cache.repositories.size";
public const string CacheSizeTypes = "org.apache.chemistry.dotcmis.binding.cache.types.size";
diff --git a/DotCMISUnitTest/SmokeTest.cs b/DotCMISUnitTest/SmokeTest.cs
index 7dfc352..e91761a 100644
--- a/DotCMISUnitTest/SmokeTest.cs
+++ b/DotCMISUnitTest/SmokeTest.cs
@@ -245,7 +245,18 @@
Assert.AreEqual(properties[PropertyIds.Name], nameProperty.Value);
Assert.AreEqual(properties[PropertyIds.Name], nameProperty.FirstValue);
- doc.Delete(true);
+
+ byte[] content2 = UTF8Encoding.UTF8.GetBytes("Hello Universe!");
+
+ ContentStream contentStream2 = new ContentStream();
+ contentStream2.FileName = properties[PropertyIds.Name] as string;
+ contentStream2.MimeType = "text/plain";
+ contentStream2.Length = content2.Length;
+ contentStream2.Stream = new MemoryStream(content2);
+
+ // doc.SetContentStream(contentStream2, true);
+
+ doc2.Delete(true);
try
{