Full support for asp.net stored in multilevel catalogs in WinForm

Source: Internet
Author: User
Tags empty http request implement socket thread web services server port
asp.net about six months ago wrote a simple example of embedding asp.net in a WinForm program, "Embedding asp.net in a WinForm program." Because it is a trial nature of the work, so lazy directly using the system with the simpleworkerrequest to complete the asp.net page request processing work. The use of a self-contained tool class is simple to implement, but is limited by many of the functions of the system, such as a friend who later mentioned the problem of not dealing directly with the multilevel subdirectories. (If the "/help/about.aspx" type of page request cannot be processed when the virtual directory is "/")
One of the best implementations for such requirements is the Cassini provided by Www.asp.net. This example fully demonstrates how to implement a simple Web server feature that supports ASP.net and is used as a Web server for debugging with many Open-source projects, such as Borland delphi.net. Although only dozens of K source code, but though small spite, still very worth seeing. But because Cassini is designed to handle WEB services, we need to make some customization to meet our needs on the basis of understanding its structure.

First look at the Cassini program structure.

Similar to the structure I used in my previous example, Cassini includes several main parts, including interface (Cassiniform), server (servers), host (host) and request processor (requests), and completes the WEB with several tools such as Connection The parsing and answering function of the request.

The overall work flow chart is as follows:
The following are program code:

+-------+ [1] +-------------+ [2] +--------+
| Admin |---->| Cassiniform |---->| Server |
+-------+ +-------------+ +--------+
| [3]
V
+--------+ [4] +------+
| Client |---->| Host |
+--------+ +------+
^ | [5]
| V
| +------------+ [6] +---------+
[7]| | Connection |---->| Request |--+
| +------------+ +---------+ | [7]
+----------------------------------------+



[1] Cassini Manager (Admin) first through the Cassiniform interface, set the WEB server port, the page physical directory and virtual directory configuration information;
[2] then construct the server object with configuration information and invoke the Server.start method to start the WEB server;
The following are program code:

public class Cassiniform:form
{
private void Start ()
{
// ...
try {
_server = new Cassini.server (PortNumber, _virtroot, _apppath);
_server. Start ();
}
catch {
Display error messages
}
// ...
}
}



The [3] Server object will get or automatically initialize the ASP.net registry configuration when it is established. This work is done through the server.getinstallpathandconfigureaspnetifneeded method. The working principle is to obtain the appropriate ASP.net version through the version of HttpRuntime's Assembly (System.Web.dll), and then query the registry for HKEY_LOCAL_ Whether there is a correct asp.net installation path under Machinesoftwaremicrosoftasp.net, or if so, if so, otherwise it will be based on the System.Web.dll version and Hkey_local_ Machinesoftwaremicrosoft.netframework the. NET Framework dynamically constructs an appropriate ASP.net registry configuration according to information such as directories. The reason for this is that asp.net can be manually logged out using Aspnet_regiis.exe after following the. NET Framework, and running a WEB server that supports asp.net must have the appropriate settings.
After the configuration and asp.net installation path has been completed, Server establishes and configures the host object as the host of the asp.net.
The following are program code:

public class Server:marshalbyrefobject
{
private void CreateHost () {
_host = (host) Applicationhost.createapplicationhost (typeof (Host), _virtualpath, _physicalpath);
_host. Configure (This, _port, _virtualpath, _physicalpath, _installpath);
}

public void Start () {
if (_host!= null)
_host. Start ();
}
}



[4] The host class, as the host class for ASP.net, mainly completes three parts of the work: Configuring the ASP.net runtime environment, responding to client-initiated Web page requests, and determining the validity of client requests.
The primary task of configuring the ASP.net runtime environment is to obtain sufficient configuration information for the execution of ASP.net and for the validity of subsequent requests. Examples include WEB service ports, page virtual paths, page physical paths, and ASP.net program installation paths that Server can provide, and the virtual and physical paths of asp.net client scripts that the Host calculates based on this information, and so on. In addition, the uninstall event appdomain.domainunload for the AppDomain of the pipeline is also connected, and the Web service is automatically terminated when the Web server is stopped.
A Web page request feature that responds to client (clients) is created by establishing a Socket to listen on the Web service TCP port specified by the Server object. The Host.start method establishes the Socket and calls the Host.onstart method asynchronously from the thread pool to listen for the request in the background, and the Host.onstart method asynchronously invokes the thread pool after receiving the WEB request host.onsocketaccept Method completes the response of the request, Host.onsocketaccept is responsible for creating the Connection object when processing the Web request, and further calls the Connection.processonerequest method to process the Web request. Although the Host does not use a complex request allocation algorithm, the flexible use of the thread pool makes its performance completely free of bottlenecks, and is a good example of the use of thread pooling.
The following are program code:

Internal class Host:marshalbyrefobject
{
public void Start () {
if (_started)
throw new InvalidOperationException ();

Establish a Socket listening Web service port
_socket = new Socket (addressfamily.internetwork, SocketType.Stream, protocoltype.tcp);
_socket. Bind (New IPEndPoint (Ipaddress.any, _port));
_socket. Listen ((int) socketoptionname.maxconnections);

_started = true;
ThreadPool.QueueUserWorkItem (_onstart); asynchronous invocation through the thread pool
}

private void OnStart (Object unused) {
while (_started) {
try {
Socket socket = _socket. Accept (); Responding to client requests
ThreadPool.QueueUserWorkItem (_onsocketaccept, socket); asynchronous invocation through the thread pool
}
catch {
Thread.Sleep (100);
}
}
_stopped = true;
}

private void Onsocketaccept (Object acceptedsocket) {
Connection conn = new Connection (this, (Socket) acceptedsocket);
Conn. Processonerequest (); Processing client requests
}
}



Finally, the function of determining the validity of the client request is provided to Connection to determine the validity of the request by means of three overloaded Host.isvirtualpathinapp methods, which are discussed in detail when the client request is processed.

[5] When the Host establishes the Connection object and invokes its Processonerequest method to process the user request, the Connection object waits for the client to request data (Waitforrequestbytes) First, and then creates the request Object and calls the Request.process method to process the request. and itself, it provides support for the Request class through a bunch of waitxxx functions.
The following are program code:

Internal class Connection {
public void Processonerequest () {
Wait for at least some input
if (waitforrequestbytes () = = 0) {//Waiting for client to request data
Writeerrorandclose (400); Send HTTP 400 error to client
Return
}

Request Request = new request (_host, this);
Request. Process ();
}

private int waitforrequestbytes () {
int availbytes = 0;

try {
if (_socket. Available = = 0) {
Poll until there is data
_socket. Poll (100000/* 100ms */, Selectmode.selectread); Waiting for client data 100ms time
if (_socket. Available = = 0 && _socket. Connected)
_socket. Poll (10000000/* 10sec */, Selectmode.selectread);
}

Availbytes = _socket. Available;
}
catch {
}

return availbytes;
}




[6] Upon receiving a request from Connection, request reads the requested content from the client and analyzes it according to the HTTP protocol. Since this article is not an analysis of the HTTP protocol, this part of the code is not discussed in detail.
After the Request.parserequestline function analyzes the request page path for the HTTP request, the Host.isvirtualpathinapp function mentioned earlier is called to determine if the path is subordinate to the virtual path provided by the WEB server. and returns whether this virtual path points to asp.net client script. If the virtual path of the WEB request ends with "/", call the Request.processdirectorylistingrequest method to return the response of the column directory; otherwise, call the Httpruntime.processrequest method to complete the actual asp.net request processing work.
HttpRuntime uses the IoC's strategy to get the final page through the unified interface provided by the base class HttpWorkerRequest of the Request. The biggest difference with the simpleworkerrequest implementation I used in previous articles is that Request.mappath completes a more complete virtual directory mapping mechanism to the physical directory.
Simpleworkerrequest.mappath to achieve relatively simple:
The following are program code:

public override string Simpleworkerrequest.mappath (string path)
{
if (!this._hasruntimeinfo)
{
return null;
}

string physpath = null;
String appphyspath = this._appphyspath.substring (0, (this._appphyspath.length-1)); Remove trailing slash

if (((path = null) | | (Path. Length = = 0)) | | Path. Equals ("/"))
{
PhysPath = Appphyspath;
}

if (path. StartsWith (This._appvirtpath))
{
PhysPath = Appphyspath + path. Substring (this._appvirtpath.length). Replace ('/', ' \ ');
}

Internalsecuritypermissions.pathdiscovery (PhysPath). Demand ();

return physpath;
}



Request.mappath implementation is relatively to improve a lot, considering a lot of simpleworkerrequest can not handle the situation, make Request more adaptable.
The following are program code:

public override string Request.mappath (string path) {
String Mappedpath = String.Empty;

if (path = = NULL | | path. Length = = 0 | | Path. Equals ("/")) {
Asking for the site root
if (_host. VirtualPath = = "/") {
App at the site root
Mappedpath = _host. PhysicalPath;
}
else {
Unknown site Root-don ' t point to app root to avoid double config inclusion
Mappedpath = environment.systemdirectory;
}
}
else if (_host. Isvirtualpathapppath (path)) {
Application path
Mappedpath = _host. PhysicalPath;
}
else if (_host. Isvirtualpathinapp (path)) {
Inside app but not the app path itself
Mappedpath = _host. PhysicalPath + path. Substring (_host. Normalizedvirtualpath.length);
}
else {
Outside of app--make relative to app path
if (path. StartsWith ("/"))
Mappedpath = _host. PhysicalPath + path. Substring (1);
Else
Mappedpath = _host. PhysicalPath + path;
}

Mappedpath = Mappedpath.replace ('/', ' \ ');

if (Mappedpath.endswith ("\") &&!mappedpath.endswith (": \"))
Mappedpath = mappedpath.substring (0, mappedpath.length-1);

return mappedpath;
}




For further discussion of Cassini, you can refer to the discussion www.asp.net on the special edition.

[7] After the httruntime completes the specific asp.net page processing work, will pass the REQUEST.SENDRESPONSEFROMXXX series function, will return the page result to the client.

Although the Simpleworkerrequest.mappath method is simple to implement, it is theoretically possible to handle the situation of multilevel catalogs. When using SimpleWorkerRequest, nested directories cannot be processed because simpleworkerrequest in the constructor incorrectly decomposes information such as the virtual directory where the requested page resides.
SimpleWorkerRequest's two constructors, after saving the Request page virtual path (such as "/help/about.aspx"), call the Extractpagepathinfo method to further decompose the page path.
The following are program code:

private void Simpleworkerrequest.extractpagepathinfo ()
{
int idx = This._page. IndexOf ('/');
if (idx >= 0)
{
This._pathinfo = This._page. Substring (IDX);
This._page = This._page. Substring (0, IDX);
}
}



This._pathinfo is the storage field provided for the implementation of Httpworkerrequest.getpathinfo. GetPathInfo will return the path information in the URL after the page, for example, "Path/virdir/page.html/tail" will return "/tail". Many of the early HTTP client programs, such as the distribution of webaction in Delphi, took advantage of the feature of this path information, which was again requested for distribution after the Web page or ISAPI level. However, because of the simpleworkerrequest implementation or design constraints, the process pathinfo will be a "/help/about.aspx" similar to the multi-level URL error cut off. The this._path that is eventually returned to HttpRuntime will become an empty string, while This._pathinfo is set to "/help/about.aspx", and a single-stage path such as "About.aspx" is unaffected.
Know this principle, you can modify the simpleworkerrequest slightly, overload affected by the Extractpagepathinfo several methods, can complete the multi-level directory structure under the support of the page. If further mapping support is required, such as supporting multiple virtual subdirectories at the same time, you can implement MapPath methods such as Cassini Request.
The following are program code:

public class Request:simpleworkerrequest
{
private string _appphyspath;
private string _appvirtpath;

private string _page;
private string _pathinfo;

Public Request (String page, string query, TextWriter output): Base (page, query, Output)
{
This._appphyspath = Thread.getdomain (). GetData (". AppPath"). ToString ();
This._appvirtpath = Thread.getdomain (). GetData (". Hostingvirtualpath"). ToString ();

this._page = page;

TODO: Further parse Path Info from page
}

public override string GetPathInfo ()
{
if (This._pathinfo = null)
{
return string. Empty;
}
return this._pathinfo;
}

private string getpathinternal (bool includepathinfo)
{
String path = (_appvirtpath.equals ("/")? _page: _appvirtpath + _page);

if (Includepathinfo && (_pathinfo!= null))
{
return path + this._pathinfo;
}
Else
{
return path;
}
}

public override string Geturipath ()
{
Return getpathinternal (TRUE);
}

public override string GetFilePath ()
{
return getpathinternal (FALSE);
}

public override string Getrawurl ()
{
string query = this. GetQueryString ();

if (query!= null) && (query. Length > 0))
{
Return Getpathinternal (True) + "?" + query;
}
Else
{
Return getpathinternal (TRUE);
}
}

public override string Getfilepathtranslated ()
{
return _appphyspath + _page. Replace ('/', ' \ ');
}

public override string MapPath (string path)
{
string physpath = null;

if (((path = null) | | (Path. Length = = 0)) | | Path. Equals ("/"))
{
PhysPath = This._appphyspath;
}

if (path. StartsWith (This._appvirtpath))
{
PhysPath = This._appphyspath + path. Substring (this._appvirtpath.length). Replace ('/', ' \ ');
}

return physpath;
}
}




Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.