c# https request

When encountering an Https website and c# http request, an SSL connection error is always reported. After searching, I found a solution:

       .net 2.0 needs to introduce a third-party component: BouncyCastle.dll, here is an example I wrote:

       

copy code
        public static string RequestWebServerByTCP(Uri uri, string method, NameValueCollection parameter, string cookie, Encoding encoding)
        {
            try
            {
                StringBuilder RequestHeaders = new StringBuilder();

                RequestHeaders.Append(method + " " + uri.PathAndQuery + " HTTP/1.1\r\n");

                method = method.ToUpper();

                if (method == POSTMETHOD)
                    RequestHeaders.Append("Content-Type:application/x-www-form-urlencoded\r\n");
                RequestHeaders.Append("User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11\r\n");
                RequestHeaders.Append("Cookie:" + cookie + "\r\n");
                RequestHeaders.Append("Accept:*/*\r\n");
                RequestHeaders.Append("Host:" + uri.Host + "\r\n");


                byte[] postdata = null;
                StringBuilder sb = new StringBuilder();

                if (method == GETMETHOD)
                {
                    uri = GetMethodQueryString(uri, parameter, encoding);
                }
                else if (method == POSTMETHOD)
                {
                    if (parameter != null)
                    {
                        foreach (string key in parameter)
                        {
                            sb.Append(string.Format(FORMATSTR1, System.Web.HttpUtility.UrlEncode(key, encoding), System.Web.HttpUtility.UrlEncode(parameter[key], encoding)));
                        }
                    }
                    if (sb.Length != 0)
                    {
                        sb = sb.Remove(sb.Length - 1, 1);
                    }
                    postdata = encoding.GetBytes(sb.ToString());

                    RequestHeaders.Append("Content-Length:" + postdata.Length + "\r\n");
                }

                RequestHeaders.Append("Connection:close\r\n\r\n");
                byte[] req = Encoding.UTF8.GetBytes(RequestHeaders.ToString() + sb.ToString());


                int port = 443;

                MyTlsClient client = new MyTlsClient();
                var protocol = OpenTlsConnection(uri.Host, port, client);

                Stream tlsStream = protocol.Stream;
                tlsStream.Write(req, 0, req.Length);
                tlsStream.Flush();

                StreamReader reader = new StreamReader(tlsStream);

                String line;
                StringBuilder html = new StringBuilder();

                string firstLine = "";

                int i = 0;

                while ((line = reader.ReadLine()) != null)
                {
                    if (i == 0)
                    {
                        firstLine = line;
                        i++;
                    }

                    html.AppendLine(line);

                    if (line.Contains("</html>"))
                    {
                        break;
                    }
                }
                protocol.Close();

                string httpstatusCode = "";

                string[] httpstatus = firstLine.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
                if (httpstatus.Length > 2)
                {
                    httpstatusCode = httpstatus[1];
                }
                else
                {
                    // request invalid
                    httpstatusCode = "400";
                }
                return html.ToString();

            }
            catch
            {
                return "";
            }

        }
copy code

  Why does the requested html need to be read line by line? When I was debugging, I found a bug. If I read it at one time, it couldn't stop and finally reported an error, so I made a judgment to read the end of the html.

       The default class provided is inherited:

copy code
  class MyTlsClient : DefaultTlsClient
    {
        public override TlsAuthentication GetAuthentication()
        {
            return new MyTlsAuthentication();
        }
    }

    // Need class to handle certificate auth  
    class MyTlsAuthentication : TlsAuthentication
    {
        public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
        {
            // return client certificate  
            return null;
        }

        public void NotifyServerCertificate(Certificate serverCertificate)
        {
            // validate server certificate  
        }
    }
copy code

 

copy code
       internal static TlsClientProtocol OpenTlsConnection(string hostname, int port, TlsClient client)
        {
            TcpClient tcp = new TcpClient(hostname, port);

            TlsClientProtocol protocol = new TlsClientProtocol(tcp.GetStream(), secureRandom);
            protocol.Connect(client);
            return protocol;
        }
copy code

The method of splicing url parameters:

copy code
      private static Uri GetMethodQueryString(Uri uri, NameValueCollection parameter, Encoding encoding)
        {
            List<KeyValuePair<string, string>> parameter1 = new List<KeyValuePair<string, string>>();
            foreach (string key in parameter)
            {
                parameter1.Add(new KeyValuePair<string, string>(key, parameter[key]));
            }
            return GetMethodQueryString(uri, parameter1, encoding);
        }

        private static Uri GetMethodQueryString(Uri uri, List<KeyValuePair<string, string>> parameter, Encoding encoding)
        {
            string format = string.Empty;
            UriBuilder uribuilfer = new UriBuilder(uri);

            string QueryString = string.Empty;
            if (string.IsNullOrEmpty(uribuilfer.Query))
            {
                format = FORMATSTR1;
            }
            else
            {
                format = FORMATSTR2;
            }
            QueryString = uribuilfer.Query;
            if (parameter != null)
            {
                foreach (KeyValuePair<string, string> item in parameter)
                {
                    QueryString += string.Format(format, System.Web.HttpUtility.UrlEncode(item.Key, encoding), System.Web.HttpUtility.UrlEncode(item.Value, encoding));
                }
            }
            QueryString = QueryString.TrimEnd(new char[] { '&' });
            QueryString = QueryString.TrimStart(new char[] { '?' });
            uribuilfer.Query = QueryString;
            uri = uribuilfer.Uri;
            return uri;
        }
copy code

   Note: What is the difference between parameters of type List<KeyValuePair<string, string>> and NameValueCollection ? They all contain the same key, but when stored, the NameValueCollection will separate the values ​​containing the same key with commas and store them together. In this way, the request may fail and the data cannot be obtained. I have tossed around for a long time because of this problem. I implemented the request in python, and then implemented it in .net core. Finally, I finally lowered my arrogant head and saw that there was a problem when passing the parameters.

       In .net 4.0, just add one sentence: ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

       In .net 4.5, nothing to worry about.

       In 2.0, even TCP is used, but we can see the essence of http request, convert a formatted request header + request data into binary and send it to a certain port of the host, return the stream, and read the stream, you can get to the result.

       Speaking of which, let's take a look at the Request message format:

       

copy code
GET https://www.baidu.com/ HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: www.baidu.com
Cookie: BAIDUID=C1EFC3A3466AAAEBE74C6F6E7F413FA8:FG=1; BIDUPSID=C1EFC3A3466AAAEBE74C6F6E7F413FA8; PSTM=1525339270; BD_LAST_QID=12260391193367555241
copy code

1. The request line, including the requested method, url, and http protocol version

2. Request headers, received formats, browser proxies, cookies, etc.

3, blank line

4. Request body, transfer data

 

Response format:

copy code
HTTP/1.1 200 OK
Bdpagetype: 1
Bdqid: 0x9a1ff959000016d0
Cache-Control: private
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Cxy_all: baidu+77e5655ffd82ce31adf5edff251fc585
Date: Thu, 03 May 2018 09:21:10 GMT
Expires: Thu, 03 May 2018 09:21:03 GMT
Server: BWS / 1.1
Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: BD_HOME=0; path=/
Set-Cookie: H_PS_PSSID=1428_21080_20719; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Vary: Accept-Encoding
X-Powered-By: HPHP
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked

html
copy code

1. Status line

2. Message header, content-type, Date, Set-Cookie

3, blank line

4. Text

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325289225&siteId=291194637