ShareX/ShareX.UploadersLib/BaseUploaders/Uploader.cs

589 lines
21 KiB
C#
Raw Normal View History

2015-06-08 01:34:33 +12:00
#region License Information (GPL v3)
/*
ShareX - A program that allows you to take screenshots and share any file type
2019-01-02 20:43:52 +13:00
Copyright (c) 2007-2019 ShareX Team
2015-06-08 01:34:33 +12:00
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Optionally you can also view the license at <http://www.gnu.org/licenses/>.
*/
#endregion License Information (GPL v3)
using ShareX.HelpersLib;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Text;
namespace ShareX.UploadersLib
{
public class Uploader
{
public delegate void ProgressEventHandler(ProgressManager progress);
public event ProgressEventHandler ProgressChanged;
2015-12-10 07:32:26 +13:00
public event Action<string> EarlyURLCopyRequested;
2015-06-08 01:34:33 +12:00
public bool IsUploading { get; protected set; }
public List<string> Errors { get; private set; } = new List<string>();
2016-12-23 21:51:13 +13:00
public bool IsError => !StopUploadRequested && Errors != null && Errors.Count > 0;
public int BufferSize { get; set; } = 8192;
public bool VerboseLogs { get; set; }
public string VerboseLogsPath { get; set; }
2015-06-08 01:34:33 +12:00
2016-12-23 21:51:13 +13:00
protected bool StopUploadRequested { get; set; }
protected bool AllowReportProgress { get; set; } = true;
2017-02-03 19:04:39 +13:00
protected bool ReturnResponseOnError { get; set; }
2016-12-23 21:21:37 +13:00
2015-06-08 01:34:33 +12:00
private HttpWebRequest currentRequest;
private Logger verboseLogger;
2015-06-08 01:34:33 +12:00
public static void UpdateServicePointManager()
2015-06-08 01:34:33 +12:00
{
ServicePointManager.DefaultConnectionLimit = 25;
ServicePointManager.Expect100Continue = false;
ServicePointManager.UseNagleAlgorithm = false;
}
protected void OnProgressChanged(ProgressManager progress)
{
if (ProgressChanged != null)
{
ProgressChanged(progress);
}
}
2015-12-10 07:32:26 +13:00
protected void OnEarlyURLCopyRequested(string url)
{
if (EarlyURLCopyRequested != null && !string.IsNullOrEmpty(url))
{
EarlyURLCopyRequested(url);
}
}
2015-06-08 01:34:33 +12:00
public string ToErrorString()
{
if (IsError)
{
return string.Join(Environment.NewLine, Errors);
}
2016-05-25 06:15:45 +12:00
return "";
2015-06-08 01:34:33 +12:00
}
public virtual void StopUpload()
{
if (IsUploading)
{
StopUploadRequested = true;
if (currentRequest != null)
{
try
{
currentRequest.Abort();
}
catch (Exception e)
{
DebugHelper.WriteException(e);
}
}
}
}
2016-12-23 20:24:38 +13:00
protected string SendRequest(HttpMethod method, string url, Dictionary<string, string> args = null, NameValueCollection headers = null,
2015-06-08 01:34:33 +12:00
CookieCollection cookies = null, ResponseType responseType = ResponseType.Text)
{
return SendRequest(method, url, (Stream)null, null, args, headers, cookies, responseType);
}
protected string SendRequest(HttpMethod method, string url, Stream data, string contentType = null, Dictionary<string, string> args = null, NameValueCollection headers = null,
CookieCollection cookies = null, ResponseType responseType = ResponseType.Text)
{
using (HttpWebResponse webResponse = GetResponse(method, url, data, contentType, args, headers, cookies))
2015-06-08 01:34:33 +12:00
{
2018-10-18 05:06:06 +13:00
string response = UploadHelpers.ResponseToString(webResponse, responseType);
if (VerboseLogs && !string.IsNullOrEmpty(VerboseLogsPath))
{
WriteVerboseLog(url, args, headers, response);
}
return response;
2015-06-08 01:34:33 +12:00
}
}
2016-12-23 20:55:16 +13:00
protected string SendRequest(HttpMethod method, string url, string content, string contentType = null, Dictionary<string, string> args = null, NameValueCollection headers = null,
2015-06-08 01:34:33 +12:00
CookieCollection cookies = null, ResponseType responseType = ResponseType.Text)
{
byte[] data = Encoding.UTF8.GetBytes(content);
using (MemoryStream ms = new MemoryStream())
{
ms.Write(data, 0, data.Length);
2016-12-23 20:55:16 +13:00
return SendRequest(method, url, ms, contentType, args, headers, cookies, responseType);
}
}
internal string SendRequestURLEncoded(HttpMethod method, string url, Dictionary<string, string> args, NameValueCollection headers = null, CookieCollection cookies = null,
2016-12-23 21:21:37 +13:00
ResponseType responseType = ResponseType.Text)
2015-06-08 01:34:33 +12:00
{
string query = URLHelpers.CreateQueryString(args);
2015-06-08 01:34:33 +12:00
return SendRequest(method, url, query, UploadHelpers.ContentTypeURLEncoded, null, headers, cookies, responseType);
2015-06-08 01:34:33 +12:00
}
2016-12-23 21:51:13 +13:00
protected bool SendRequestDownload(HttpMethod method, string url, Stream downloadStream, Dictionary<string, string> args = null,
NameValueCollection headers = null, CookieCollection cookies = null, string contentType = null)
{
using (HttpWebResponse response = GetResponse(method, url, null, contentType, args, headers, cookies))
{
if (response != null)
{
2016-12-27 06:23:29 +13:00
using (Stream responseStream = response.GetResponseStream())
{
responseStream.CopyStreamTo(downloadStream, BufferSize);
}
2016-12-23 21:51:13 +13:00
return true;
}
2015-06-08 01:34:33 +12:00
}
2016-12-23 21:51:13 +13:00
return false;
2015-06-08 01:34:33 +12:00
}
2018-04-11 16:30:09 +12:00
protected string SendRequestMultiPart(string url, Dictionary<string, string> args, NameValueCollection headers = null, CookieCollection cookies = null,
ResponseType responseType = ResponseType.Text, HttpMethod method = HttpMethod.POST)
2015-06-08 01:34:33 +12:00
{
2018-10-18 05:06:06 +13:00
string boundary = UploadHelpers.CreateBoundary();
string contentType = UploadHelpers.ContentTypeMultipartFormData + "; boundary=" + boundary;
byte[] data = UploadHelpers.MakeInputContent(boundary, args);
2015-06-08 01:34:33 +12:00
using (MemoryStream stream = new MemoryStream())
{
stream.Write(data, 0, data.Length);
2016-12-23 21:21:37 +13:00
using (HttpWebResponse webResponse = GetResponse(method, url, stream, contentType, null, headers, cookies))
2015-06-08 01:34:33 +12:00
{
2018-10-18 05:06:06 +13:00
string response = UploadHelpers.ResponseToString(webResponse, responseType);
if (VerboseLogs && !string.IsNullOrEmpty(VerboseLogsPath))
{
WriteVerboseLog(url, args, headers, response);
}
return response;
2016-06-29 12:50:16 +12:00
}
2015-06-08 01:34:33 +12:00
}
}
2018-11-29 04:15:58 +13:00
protected UploadResult SendRequestFile(string url, Stream data, string fileName, string fileFormName, Dictionary<string, string> args = null,
2015-06-08 01:34:33 +12:00
NameValueCollection headers = null, CookieCollection cookies = null, ResponseType responseType = ResponseType.Text, HttpMethod method = HttpMethod.POST,
2018-10-18 05:06:06 +13:00
string contentType = UploadHelpers.ContentTypeMultipartFormData, string metadata = null)
2015-06-08 01:34:33 +12:00
{
UploadResult result = new UploadResult();
IsUploading = true;
StopUploadRequested = false;
try
{
2018-10-18 05:06:06 +13:00
string boundary = UploadHelpers.CreateBoundary();
2016-06-29 12:50:16 +12:00
contentType += "; boundary=" + boundary;
2015-06-08 01:34:33 +12:00
2018-10-18 05:06:06 +13:00
byte[] bytesArguments = UploadHelpers.MakeInputContent(boundary, args, false);
2015-06-08 01:34:33 +12:00
byte[] bytesDataOpen;
byte[] bytesDataDatafile = { };
if (metadata != null)
{
2018-10-18 05:06:06 +13:00
bytesDataOpen = UploadHelpers.MakeFileInputContentOpen(boundary, fileFormName, fileName, metadata);
bytesDataDatafile = UploadHelpers.MakeFileInputContentOpen(boundary, fileFormName, fileName, null);
2015-06-08 01:34:33 +12:00
}
else
{
2018-10-18 05:06:06 +13:00
bytesDataOpen = UploadHelpers.MakeFileInputContentOpen(boundary, fileFormName, fileName);
2015-06-08 01:34:33 +12:00
}
2018-10-18 05:06:06 +13:00
byte[] bytesDataClose = UploadHelpers.MakeFileInputContentClose(boundary);
2015-06-08 01:34:33 +12:00
2016-12-23 20:24:38 +13:00
long contentLength = bytesArguments.Length + bytesDataOpen.Length + bytesDataDatafile.Length + data.Length + bytesDataClose.Length;
2018-10-18 05:06:06 +13:00
HttpWebRequest request = UploadHelpers.CreateWebRequest(method, url, headers, cookies, contentType, contentLength);
currentRequest = request;
2015-06-08 01:34:33 +12:00
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(bytesArguments, 0, bytesArguments.Length);
requestStream.Write(bytesDataOpen, 0, bytesDataOpen.Length);
requestStream.Write(bytesDataDatafile, 0, bytesDataDatafile.Length);
2016-12-23 20:24:38 +13:00
if (!TransferData(data, requestStream)) return null;
2015-06-08 01:34:33 +12:00
requestStream.Write(bytesDataClose, 0, bytesDataClose.Length);
}
2016-12-27 06:23:29 +13:00
using (WebResponse response = request.GetResponse())
{
2018-10-18 05:06:06 +13:00
result.Response = UploadHelpers.ResponseToString(response, responseType);
2016-12-27 06:23:29 +13:00
}
result.IsSuccess = true;
}
catch (Exception e)
{
if (!StopUploadRequested)
{
string response = AddWebError(e, url);
if (ReturnResponseOnError && e is WebException)
{
result.Response = response;
}
}
}
finally
{
currentRequest = null;
IsUploading = false;
if (VerboseLogs && !string.IsNullOrEmpty(VerboseLogsPath))
{
WriteVerboseLog(url, args, headers, result.Response);
}
}
return result;
}
2018-06-15 22:12:23 +12:00
protected UploadResult SendRequestFileRange(string url, Stream data, string fileName, long contentPosition = 0, long contentLength = -1,
Dictionary<string, string> args = null, NameValueCollection headers = null, CookieCollection cookies = null, ResponseType responseType = ResponseType.Text,
HttpMethod method = HttpMethod.PUT)
{
UploadResult result = new UploadResult();
IsUploading = true;
StopUploadRequested = false;
try
{
url = URLHelpers.CreateQueryString(url, args);
if (contentLength == -1)
{
contentLength = data.Length;
}
contentLength = Math.Min(contentLength, data.Length - contentPosition);
2018-10-18 05:06:06 +13:00
string contentType = UploadHelpers.GetMimeType(fileName);
if (headers == null)
{
headers = new NameValueCollection();
}
long startByte = contentPosition;
long endByte = startByte + contentLength - 1;
long dataLength = data.Length;
headers.Add("Content-Range", $"bytes {startByte}-{endByte}/{dataLength}");
2018-10-18 05:06:06 +13:00
HttpWebRequest request = UploadHelpers.CreateWebRequest(method, url, headers, cookies, contentType, contentLength);
currentRequest = request;
using (Stream requestStream = request.GetRequestStream())
{
if (!TransferData(data, requestStream, contentPosition, contentLength))
{
return null;
}
}
using (WebResponse response = request.GetResponse())
{
2018-10-18 05:06:06 +13:00
result.Response = UploadHelpers.ResponseToString(response, responseType);
}
2015-06-08 01:34:33 +12:00
result.IsSuccess = true;
}
catch (Exception e)
{
if (!StopUploadRequested)
{
string response = AddWebError(e, url);
2017-02-03 19:04:39 +13:00
if (ReturnResponseOnError && e is WebException)
{
result.Response = response;
}
2015-06-08 01:34:33 +12:00
}
}
finally
{
currentRequest = null;
IsUploading = false;
if (VerboseLogs && !string.IsNullOrEmpty(VerboseLogsPath))
{
WriteVerboseLog(url, args, headers, result.Response);
}
2015-06-08 01:34:33 +12:00
}
return result;
}
2018-06-15 22:12:23 +12:00
protected HttpWebResponse GetResponse(HttpMethod method, string url, Stream data = null, string contentType = null, Dictionary<string, string> args = null,
NameValueCollection headers = null, CookieCollection cookies = null, bool allowNon2xxResponses = false)
{
IsUploading = true;
StopUploadRequested = false;
try
{
url = URLHelpers.CreateQueryString(url, args);
long contentLength = 0;
if (data != null)
{
contentLength = data.Length;
}
2018-10-18 05:06:06 +13:00
HttpWebRequest request = UploadHelpers.CreateWebRequest(method, url, headers, cookies, contentType, contentLength);
currentRequest = request;
if (contentLength > 0)
{
using (Stream requestStream = request.GetRequestStream())
{
if (!TransferData(data, requestStream))
{
return null;
}
}
}
return (HttpWebResponse)request.GetResponse();
}
catch (WebException we) when (we.Response != null && allowNon2xxResponses)
{
// if we.Response != null, then the request was successful, but
// returned a non-200 status code
return we.Response as HttpWebResponse;
}
catch (Exception e)
{
if (!StopUploadRequested)
{
AddWebError(e, url);
}
}
finally
{
currentRequest = null;
IsUploading = false;
}
return null;
}
2015-06-08 01:34:33 +12:00
#region Helper methods
protected bool TransferData(Stream dataStream, Stream requestStream, long dataPosition = 0, long dataLength = -1)
2015-06-08 01:34:33 +12:00
{
if (dataPosition >= dataStream.Length)
{
return true;
}
2018-05-29 22:08:33 +12:00
2015-06-08 01:34:33 +12:00
if (dataStream.CanSeek)
{
dataStream.Position = dataPosition;
}
if (dataLength == -1)
{
dataLength = dataStream.Length;
2015-06-08 01:34:33 +12:00
}
dataLength = Math.Min(dataLength, dataStream.Length - dataPosition);
2015-06-08 01:34:33 +12:00
ProgressManager progress = new ProgressManager(dataStream.Length, dataPosition);
int length = (int)Math.Min(BufferSize, dataLength);
2015-06-08 01:34:33 +12:00
byte[] buffer = new byte[length];
int bytesRead;
long bytesRemaining = dataLength;
2015-06-08 01:34:33 +12:00
while (!StopUploadRequested && (bytesRead = dataStream.Read(buffer, 0, length)) > 0)
{
requestStream.Write(buffer, 0, bytesRead);
bytesRemaining -= bytesRead;
length = (int)Math.Min(buffer.Length, bytesRemaining);
2015-06-08 01:34:33 +12:00
if (AllowReportProgress && progress.UpdateProgress(bytesRead))
{
OnProgressChanged(progress);
}
}
return !StopUploadRequested;
}
private string AddWebError(Exception e, string url)
2015-06-08 01:34:33 +12:00
{
string response = null;
if (Errors != null && e != null)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Message:");
sb.AppendLine(e.Message);
if (!string.IsNullOrEmpty(url))
{
sb.AppendLine();
sb.AppendLine("Request URL:");
sb.AppendLine(URLHelpers.RemoveQueryString(url));
}
2015-06-08 01:34:33 +12:00
2018-10-02 02:44:54 +13:00
if (e is WebException webException)
2015-06-08 01:34:33 +12:00
{
try
{
WebResponse res = webException.Response;
2018-10-02 02:44:54 +13:00
using (res)
2015-06-08 01:34:33 +12:00
{
2018-10-18 05:06:06 +13:00
response = UploadHelpers.ResponseToString(res);
2018-10-02 02:44:54 +13:00
if (!string.IsNullOrEmpty(response))
{
sb.AppendLine();
sb.AppendLine("Response:");
sb.AppendLine(response);
}
2015-06-08 01:34:33 +12:00
}
}
2018-10-02 02:44:54 +13:00
catch (Exception nested)
2015-06-08 01:34:33 +12:00
{
2018-10-02 02:44:54 +13:00
DebugHelper.WriteException(nested, "AddWebError() WebException handler");
2015-06-08 01:34:33 +12:00
}
}
sb.AppendLine();
sb.AppendLine("Stack trace:");
sb.AppendLine(e.StackTrace);
2015-06-08 01:34:33 +12:00
string errorText = sb.ToString().Trim();
2015-06-08 01:34:33 +12:00
Errors.Add(errorText);
DebugHelper.WriteLine("Error:\r\n" + errorText);
2015-06-08 01:34:33 +12:00
}
return response;
}
private void WriteVerboseLog(string url, Dictionary<string, string> args, NameValueCollection headers, string response)
{
if (verboseLogger == null)
{
verboseLogger = new Logger(VerboseLogsPath)
{
MessageFormat = "Date: {0:yyyy-MM-dd HH:mm:ss.fff}\r\n{1}",
2017-10-17 06:22:28 +13:00
StringWrite = false
};
}
StringBuilder sb = new StringBuilder();
sb.AppendLine("URL: " + (url ?? ""));
if (args != null && args.Count > 0)
{
sb.AppendLine("Arguments:");
foreach (KeyValuePair<string, string> arg in args)
{
sb.AppendLine($" Name: {arg.Key}, Value: {arg.Value}");
}
}
if (headers != null && headers.Count > 0)
{
sb.AppendLine("Headers:");
foreach (string key in headers)
{
string value = headers[key];
sb.AppendLine($" Name: {key}, Value: {value}");
}
}
2017-10-17 06:22:28 +13:00
sb.AppendLine("Response:");
if (!string.IsNullOrEmpty(response))
{
sb.AppendLine(response);
}
sb.Append(new string('-', 30));
verboseLogger.WriteLine(sb.ToString());
}
2015-06-08 01:34:33 +12:00
#endregion Helper methods
#region OAuth methods
protected string GetAuthorizationURL(string requestTokenURL, string authorizeURL, OAuthInfo authInfo,
Dictionary<string, string> customParameters = null, HttpMethod httpMethod = HttpMethod.GET)
{
string url = OAuthManager.GenerateQuery(requestTokenURL, customParameters, httpMethod, authInfo);
string response = SendRequest(httpMethod, url);
if (!string.IsNullOrEmpty(response))
{
return OAuthManager.GetAuthorizationURL(response, authInfo, authorizeURL);
}
return null;
}
protected bool GetAccessToken(string accessTokenURL, OAuthInfo authInfo, HttpMethod httpMethod = HttpMethod.GET)
{
return GetAccessTokenEx(accessTokenURL, authInfo, httpMethod) != null;
}
protected NameValueCollection GetAccessTokenEx(string accessTokenURL, OAuthInfo authInfo, HttpMethod httpMethod = HttpMethod.GET)
{
if (string.IsNullOrEmpty(authInfo.AuthToken) || string.IsNullOrEmpty(authInfo.AuthSecret))
{
throw new Exception("Auth infos missing. Open Authorization URL first.");
}
string url = OAuthManager.GenerateQuery(accessTokenURL, null, httpMethod, authInfo);
string response = SendRequest(httpMethod, url);
if (!string.IsNullOrEmpty(response))
{
return OAuthManager.ParseAccessTokenResponse(response, authInfo);
}
return null;
}
#endregion OAuth methods
}
2013-11-03 23:53:49 +13:00
}