📄 transferstuff.cs
字号:
/************************************************************
TransferStuff.cs
Contains any functions dealing directly with HTTP (and FTP
when it's implemented) requests.
Revision history:
November 14, 2000, by Scott Glasgow
Changed the event handlers to work under .NET Beta 1
November 17 2000 by Joe Hardy
- All code was split into modules from downloader.cs (now obsolete)
November 25, 2000 by Joe Hardy
- Replaces any spaces in the URL, to be used in the HTTP request, with %20
November 28, 2000 by Joe Hardy
- Implemented proxy support
January 20, 2001 by Joe Hardy
- Man, where did the last couple of months go? :) Rewired
the code so that the calling function wont need to know
anything about the protocol that will be used - it just
feeds in a URL and picks up the data coming out the
binaryreader. Done in preparation for implementation of FTP.
January 22-24, 2001 by Joe Hardy
- FTP specific URL parsing (username/password detected etc)
- Started work on FTP support.
January 26, 2001 by Joe Hardy
- More FTP stuff including returning file size, resume support
and various other tweaks.
Late June - 6 July, 2000 by Joe Hardy
- Cut all the code across to work with beta 2
************************************************************/
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Security.Cryptography;
public enum Protocols {
Http=0,
Ftp=1
}
public enum HttpErrorType {
Success=0,
NoData=1,
NotFound=2,
Forbidden=3,
MiscClientErr=4,
MiscServerErr=5,
UnknownErr=6,
Redirect=7,
AuthRequired=8
}
public class UrlProperties {
public string hostName;
public int portNo=80;
public string docPath;
public Protocols protocol=Protocols.Http;
public string fileName;
public string ftpusername;
public string ftppwd;
}
public class HttpResponseData {
public HttpErrorType errorType;
public int contentLength;
public string WwwAuthenticate;
public string Location;
}
public class NewTransfer {
public BinaryReader br;
// these two instances of FtpFunctions and HttpFunctions are only declared here temporarily
HttpFunctions hf;
FtpFunctions ff;
public long GetUrl(string url, long startfrom) {
UrlProperties up = SplitUrl(url);
if (up.protocol == Protocols.Http || AppSettings.GetSetting("UseProxy") == "True") {
hf = new HttpFunctions();
long filesize = hf.GetUrl(up, startfrom).contentLength;
this.br = hf.br;
return filesize;
}
else {
// kick off a connection to the FTP server and get the file
ff = new FtpFunctions();
long filesize = ff.GetUrl(up, startfrom);
this.br = ff.br;
return filesize;
}
}
// extracts host name, port no and document path from a URL
public static UrlProperties SplitUrl(string url) {
int i=0;
int startstring=0;
UrlProperties urlProperties = new UrlProperties();
int length=url.Length;
if (url.Substring(0, 7) == "http://") {
urlProperties.protocol=Protocols.Http;
startstring=i=7;
}
else if (url.Substring(0, 6) == "ftp://") {
urlProperties.protocol=Protocols.Ftp;
urlProperties.portNo=21;
startstring=i=6;
// look for auth details in the url (usr:pwd@hostname format)
i++;
while (url.Substring(i, 1) != "@" && url.Substring(i, 1) != "/") i++;
if (url.Substring(i, 1) == "@") { // ok, we seem to have found the end of some auth details. now step backwards until we find a colon (the beginning of the pwd)
int end=i-1;
int begin=i-1;
while (url.Substring(begin, 1) != ":" && url.Substring(begin, 1) != "/") begin--;
if (url.Substring(begin, 1) == ":") { // cool, we found it, pick out the password and go on to get the username
urlProperties.ftppwd = url.Substring(begin+1,end-begin);
end=begin-1;
while (url.Substring(begin, 1) != "/") begin--;
urlProperties.ftpusername = url.Substring(begin+1,end-begin);
}
startstring=i+1;
}
else startstring=i=6;
}
while (url.Substring(i, 1) != "/" && url.Substring(i, 1) != ":") i++;
urlProperties.hostName=url.Substring(startstring, i-startstring);
if (url.Substring(i, 1) == ":") {
i++;
startstring=i;
while (url.Substring(i, 1) != "/") i++;
try {
urlProperties.portNo=Int32.Parse(url.Substring(startstring, i-startstring));
} catch {
}
}
if (i++ < length) {
startstring=i;
while (i < length && url.Substring(i, 1) != "#") i++;
urlProperties.docPath=url.Substring(startstring, i-startstring);
}
i=url.Length-1;
while (url.Substring(i, 1) != "/") i--;
i++;
if (url.Length-i > 1) {
urlProperties.fileName = url.Substring(i, url.Length-i);
}
else {
urlProperties.fileName = "index.htm";
}
return urlProperties;
}
public void KillMe() {
if (hf != null) hf.KillMe();
if (ff != null) ff.KillMe();
}
// destructor
~NewTransfer() {
KillMe();
}
}
public class HttpFunctions {
public Socket connection;
public NetworkStream ns;
public BinaryReader br;
public string authstring=null;
// sends off an HTTP request and returns a stream containing the response
public HttpResponseData GetUrl(UrlProperties urlProperties, long startfrom) {
Encoding ASCII = Encoding.ASCII;
bool useproxy = (AppSettings.GetSetting("UseProxy") == "True");
string proxyhost = (useproxy ? AppSettings.GetSetting("ProxyHostname") : null);
int proxyport = (useproxy ? Int32.Parse(AppSettings.GetSetting("ProxyPort")) : 0);
HttpResponseData responseData=null;
while (true) {
string s = "GET " + (useproxy ? (urlProperties.protocol == Protocols.Http ? "http://" : "ftp://") + urlProperties.hostName : null) + "/" + (new StringBuilder(urlProperties.docPath)).Replace(" ", "%20").ToString() + " HTTP/1.1\r\n" +
"Host: " + urlProperties.hostName + "\r\n" +
"Range: bytes=" + startfrom + "-\r\n" +
(authstring!=null ? "Authorization: Basic " + authstring + "\r\n" : "") +
"Connection: close\r\n" +
"User-Agent: UnrealDownload v0.3 - http://www.pconsulting.com.au/unrealdownload/\r\n" +
"Accept: */*\r\n\r\n";
byte[] b = ASCII.GetBytes(s);
byte[] returned = new byte[256];
connection = new Socket(0, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ep = new IPEndPoint(Dns.Resolve((useproxy ? proxyhost : urlProperties.hostName)).AddressList[0], (useproxy ? proxyport : urlProperties.portNo));
MessageBox.Show(ep.Address.ToString());
connection.Connect(ep);
// send the data to the server
connection.Send(b, b.Length, 0);
// used for concatenating each byte returned from the HTTP response
StringBuilder sb = new StringBuilder();
// holds the number of bytes read when calling the BinaryStream's Read() method
int bytesread=1;
// create the stream objects
ns = new NetworkStream(connection);
br = new BinaryReader(ns);
// The following loop starts retrieving the data received byte by byte until it
// hits two CR/LF pairs. After that it parses the headers to verify that the
// HTTP request went through OK (by parsing the response code) and retrieves some
// data that we'll need along the way (such as the Content-length value)
while (bytesread > 0) {
// move the byte we read on the last iteration to the first byte in the array
b[0]=b[1];
// read another byte into the second position in the array
bytesread=br.Read(b, 1, 1);
// check whether the last byte was a linefeed and the current byte is a carriage
// return. if so, we've read all the headers, so break.
if (b[0]==10 && b[1]==13)
break;
// append last read byte to string builder.
sb.Append(ASCII.GetChars(b, 1, 1));
}
bytesread=br.Read(b, 1, 1); // read one more byte (final LF that we didn't catch)
// parse the headers. return variable is that of the HttpErrorType enumeration.
responseData = parseHeaders(sb.ToString());
string errstring=null;
// find out what was returned
switch (responseData.errorType) {
case HttpErrorType.Success: // cool!
break;
case HttpErrorType.NoData: // nothing was returned, which we'll count as an error
errstring = "The file requested contains no data";
break;
case HttpErrorType.NotFound: // returned response code 404
errstring = "Doh! We got a Four-Oh-Four!";
break;
case HttpErrorType.Redirect:
// are we redirectng to another server here
if (responseData.Location.Length > 7 && responseData.Location.Substring(0, 7) == "http://") {
urlProperties = NewTransfer.SplitUrl(responseData.Location);
}
else
urlProperties.docPath = responseData.Location;
break;
case HttpErrorType.AuthRequired: // we need authentication
// check that the authentication method being used is Basic
if (responseData.WwwAuthenticate.Length < 5 || responseData.WwwAuthenticate.Substring(0, 5).ToLower()!="basic") {
errstring = "Sorry, for the moment this app only supports Basic authentication.";
break;
}
// open the authorisation details dialog and get the result.
AuthForm af = new AuthForm(false);
DialogResult dlg = af.ShowDialog();
if (dlg == DialogResult.OK) {
// build the value for the Authenticate field that will be sent back to the server
this.authstring = af.username + ":" + af.password;
// convert the string to a byte array
ASCIIEncoding encoda = new ASCIIEncoding();
char[] ctemp = authstring.ToCharArray();
byte[] btemp = encoda.GetBytes(ctemp);
// encode. this string will be picked up when enter the next cycle of the loop
ToBase64Transform encrypta = new ToBase64Transform();
authstring = encrypta.TransformFinalBlock(btemp, 0, btemp.Length).ToString();
}
else
errstring = "Access to this resource requires a user name and password";
break;
case HttpErrorType.Forbidden: // 403
errstring = "We couldn't get access to that file";
break;
case HttpErrorType.MiscClientErr: // some other 4xx error
errstring = "There was an problem with the submitted HTTP request.";
break;
case HttpErrorType.MiscServerErr: // some 5xx error
errstring = "Unknown server related error. Please try again later.";
break;
case HttpErrorType.UnknownErr: // we really should be trapping for every response code received, but while this is under construction ...
errstring = "Error: unknown response code";
break;
}
if (errstring!=null) throw new ApplicationException(errstring);
if (responseData.errorType == HttpErrorType.Success) break;
br.Close();
ns.Close();
connection.Close();
}
return responseData;
}
// the following function parses the response headers from an HTTP server
public HttpResponseData parseHeaders(string headers) {
string[] splitbyline = headers.Split(new char[] { '\r', '\n' });
string[] tempstring;
int elementcount=splitbyline.Length;
HttpResponseData responseData = new HttpResponseData();
for (int i=0;i<elementcount;i+=2) {
if (i==0) {
tempstring = splitbyline[0].Split(new char[] { ' ' });
switch (tempstring[1].Substring(0, 1)) {
case "2":
switch (tempstring[1].Substring(1, 2)) {
case "04":
responseData.errorType = HttpErrorType.NoData;
break;
default:
responseData.errorType = HttpErrorType.Success;
break;
}
break;
case "3":
switch (tempstring[1].Substring(1, 2)) {
case "02": // redirector
responseData.errorType = HttpErrorType.Redirect;
break;
}
break;
case "4":
switch (tempstring[1].Substring(1, 2)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -