Cassini source code analysis (4)

xiaoxiao2021-03-06  71

Because the Connection object is only related to the Host object, and the data member is only:

Private host _host; // point to host objects

Private socket _socket; // Current socket

We know that Host calls and only conn.processONEREQUEST (); method, so we must first find this method (many ways, let's take a look at the main):

Public void processONEREQUEST () {// Wait for at Least Some INPUT

IF (WaitforRequestBytes () == 0) {

WriteerRorandClose (400);

Return;

}

Request Request = New Request (_HOST, THIS);

Request.Process ();

}

From the code, this process must first ensure that the socket has data read. If you can't read it, you write a 400 error to Socket; if you have data read, construct a Request object, call the process of the request (huh, , The trouble is always a layer of layers to others J).

Continue to analyze the Connection object, WaitForRequestBytes () is actually a separate thread to read the socket, if the socket connection but there is no data flow, it will wait 10 seconds.

WriteerRorandClose (int); function call WriteerRorandClose (int, string)

{

String body = messages.formaterrorMessageBody (statuscode, _host.virtualpath);

IF (Message! = Null && Message.Length> 0)

Body = "/ r / n ";

Writentireresponsefromstring (Statuscode, Null, Body, False);

}

The WritentireResponsefromstring () function constructed the HTTP data stream according to the status code, and writes back to the client. It actually reflects the specific implementation of the HTTP protocol. We temporarily let go, track the Request object.

INTERNAL CLASS Request: SimpleWorkerRequest {. . . . . . } Inherited from .NET, in accordance with MSDN: HTTPWORKERREQUEST provides URL and query strings, and captures the output of the output to TextWriter. To achieve a more rich feature (such as providing the semapled content and header and capture response header or response body represented by binary data), you should extend SimpleWorkerRequest and rewrite the appropriate HttpWorkerRequest method.

Start from the Process function (the code is longer):

{

ReadallHeaders (); // HTTP request head yet

IF (_Headerbytes == null || _endheadersoffset <0 ||

_Headerbytestrings == null || _headerbytestrings.count == 0) {_conn.writeerrorandclose (400);

Return; // Returns if you read an HTTP header error

}

Parsequestline (); // Handling Request line input

// Check for Bad Path

IF (isbadpath ()) {/ / Prevent User Request BAD Path

_Conn.writeerRorandClose (400);

Return;

}

//Mit to local requests onLo

IF (! _Conn.islocal) {

_Conn.writeerRorandClose (403);

Return;

}

// check if the path is not well factory design or is not for the current app

Bool isclientscriptpath = false;

String Clientscript = NULL;

IF (! _host.isvirtualpathinapp (_path, out isclientscriptpath, outclientscript) {

_Conn.writeerRorandClose (404); // Test if the URL request belongs to the application path range, if not, the report 404 error.

Return;

}

PARSEHEADERS (); // Analyze HTTP request head

ParsepostedContent (); // Analyze the contents of the POST method

IF (_verb == "&& _PreloadedContentLength <_preloadededContentLength <_preloadedth) {// If it is a POST method, you need to wait for Post data to continue to call CONN's Waiting method Write100Continue until POST is complete

_Conn.write100Continue ();

}

// Special Case for Client Script

If (isclientScriptPath) {// If the request is a script path, then read the file directly (that is, .js file, follow the text files)

_Conn.writentireresponsefromfile (_host.physicalclientscriptpath clientscript, false);

Return;

}

// Special Case for Directory Listing

If (ProcessDirectoryListingRequest ()) {// If it is a request directory list, return to return

Return;

}

Prepareresponse (); // Prepare the response

// Hand the processing over to httpruntime

HTTPRUNTIME.PROCESSREQUEST (this); // Perform the ASP.NET content through HTTPRuntime, drive all ASP.NET web processing execution.

}

For this function detail, the following functions are analyzed one by one:

ReadallHeaders

Parsequestline

ParsepostedContent

ProcessDirectoryLISTINGREQUEST

Prepareresponse

Because they deal with HTTP Request. Private void readallheaders () {

_Headerbytes = NULL;

Do {

IF (! TryReadallHeaders ())

Break; // Something Bad Happend

}

While (_endheadersoffset <0); // Found / R / N / R / N

} This function continues to call TryReadallHeaders and take a closer look at this tryreadallheaders:

Private Bool TryreadallAdeaders () {

// read the first packet (Up to 32K)

Byte [] Headerbytes = _conn.readRequestBytes (MaxHeaderbytes); // Read the maximum data 32 * 1024 bytes from the Connection.

IF (headerbytes == null || Headerbytes.length == 0)

Return False; // If the read data fails, return an error, call this function to check if the data read is complete

IF (_Headerbytes! = null) {// previous Partial Read below the current read data

INT LEN = Headerbytes.length _Headerbytes.length;

IF (len> maxheaderbytes)

Return False;

Byte [] bytes = new byte [len];

// Note that the fast buffer.blockcopy method is called, but I think it can better handle read data issues, because repeatedly generated byte array is obviously not a good way.

Buffer.blockcopy (_Headerbytes, 0, Bytes, 0, _HeaderBytes.length);

Buffer.blockcopy (Headerbytes, 0, Bytes, _Headerbytes.length; Headerbytes.length);

_Headerbytes = bytes;

}

Else {

_headerbytes = headerbytes;

}

// Start Parsing The following preparation parsing request line

_StartHeadersoffset = -1;

_endheadersoffset = -1;

_headerbytestrings = new arraylist ();

// Find the end of headers Byteparser is a custom tool class. At this time, we only know that it is to help the byte array conversion, Bytestring is also a tool class, help convert byte arrays to strings.

BYTEPARSER PARSER = New Byteparser (_Headerbytes);

For (;;) {

BYTESTRING LINE = Parser.Readline ();

IF (line == null)

Break;

IF (_StartHeadersoffSet <0) {

_StartHeadersoffset = Parser.currentoffset;

}

IF (line.isempty) {

_endheadersoffset = parser.currentoffset;

Break;

}

_Headerbytestrings.Add (line);

}

Return True;

}

How to deal with decomposition line? Private void ParsequestLine () {

BYTESTRING Requestline = (bytestring) _HeaderbyTestrings [0];

BYTESTRING [] Elems = Requestline.Split (''); // We know that every Header has an inevitable space between the Header value, such as the head of the HTTP response returned by Cassini:

HTTP / 1.1 404 Not Found

/ / Judgment whether the header reads correct, the first line of the general request head should be, for example: GET / PUB / WWW / HTTP / 1.1

IF (Elems == Null || Elems.Length <2 || Elems.Length> 3) {

Return;

}

_verb = elems [0] .getstring (); // Read HTTP request method

BYTESTRING URLBYTES = ELEMS [1];

_URL = urlbytes.getstring (); // Get the requested URL

IF (Elems.Length == 3) / / Determine the protocol version of the HTTP request

_prot = elems [2] .getstring (); // currently only http / 1.1 or http / 1.0

Else

_prot = "http / 1.0";

// Query String

INT iqs = urlbytes.indexof ('?'); // Request parameter gets the byte array representation

IF (iqs> 0)

_QueryStringBytes = urlbytes.substring (iqs 1) .getbytes ();

Else

_QueryStringBytes = new byte [0];

iqs = _url.indexof ('?'); // get a string representation of PATH and parameters

IF (iqs> 0) {

_Path = _url.substring (0, iqs);

_QueryString = _url.substring (iqs 1);

}

Else {

_path = _url;

_QueryStringBytes = new byte [0];

}

// url-decode path Start URL decoding, the famous URL decoding error in this MS is here.

IF (_path.indexof ('%')> = 0) {

_Path = httputility.urldecode (_path); // Call the tool method of .NET

}

// Path INFO to get the PATH

Int lastdot = _Path.lastIndexof ('.');

INT lastslh = _path.lastindexof ('/');

IF (lastdot> = 0 && lastdddot

INT IPI = _Path.indexOf ('/', lastdot);

_filepath = _path.substring (0, IPI);

_pathinfo = _path.substring (IPI);

}

Else {

_filepath = _path; _pathinfo = String.empty;

}

_pathtranslated = mappath (_filepath); // Mapping path, map files to specific disk path

}

After processing HTTP Header, start processing HTTP request text, see ParsepostedContent

Private void parsepostedcontent () {

_ContentLength = 0;

_PreLoadedContentLength = 0;

String contenttionthvalue = _knownRequestHeaders [httpWorkerRequest.HeaderContentLength]; // check the length of the definition in the head

IF (ContentLengthValue! = null) {

Try {

_ContentLength = Int32.Parse (ContentLengthValue);

}

Catch {

}

}

/ / The following checks if the data is abnormal

IF (_Headerbytes.length> _endheadersoffset) {

_PreLoadedContentLength = _Headerbytes.length - _endheadersoffset;

IF (_PreLoadedContentLength> _ContentLength && _ContentLength> 0)

_PreLoadedContentLength = _ContentLength; // Don't Read More Than THE Content-Length Not Read too much data

_PreLoadedContent = new byte [_preloadededContentLength];

Buffer.blockcopy (_Headerbytes, _ndHeadersoffset, _PreLoadedContent, 0, _preloadedContentLength); // Copy Data

}

}

Above the Content data byte of the HTTP request to the _preloadedContent member variable.

The next processdirectoryListingRequest process does not have a file name, that is, directly request to browse a directory. slightly.

After the processing is complete, prepare to construct a response data to see Prepareresponse

_Headerssent = false; // Prepare to write to Connection

_RESPONSESTATUS = 200;

_RESPONSEHEADERSBUILDER = New stringbuilder ();

_RESPONSEBODYBYTES = New ArrayList ();

Other, we should pay attention

HTTPRUNTIME.PROCESSREQUEST (this); implicit call relationship. HTTPRUNTIME.ProcessRequest will call the related functions when you need to write data, which is commented by Cassini.

We must understand that these overloaded functions are called during the ASP.NET processing.

///

//

//Mplementation of httpWorkerRequest

//

///

Public override string geturipath () {// Returns the virtual path of the requested URI.

Return_Path;

}

Public override string getQueryString () {// Returns the query string specified in the request URL. Return_QueryString;

}

Public override byte [] getQueryStringRawbytes () {// Rewinded in the form of byte arrays in the form of a byte array when rewriting in the derived class.

Return_QueryStringBytes;

}

Public override string getrawurl () {// Returns the URL path included in the request header attached to the query string.

Return_url;

}

Public override string gethttpverbname () {// Returns the specified member of the request header.

Return_Verb;

}

Public override string gethttpversion () {// provides access to the requested HTTP version (such as "http / 1.1").

Return_prot;

}

Public override string getRemoteAddress () {// provides access to the specified member of the request header.

Return_conn.remoteip;

}

Public override int getRemotePort () {// provides access to the specified member of the request header.

Return 0;

}

Public override string getLocaladdress () {//

Return_conn.localip;

}

Public override int getLocalPort () {

Return_host.port;

}

Public override string getFilePath () {// When rewriting in the derived class, returns the physical path of the requested URI.

Return_filepath;

}

Public override string getFilePathTranslated () {// Returns the physical file path of the requested URI (translated from virtual path into a physical path: For example, translate from "/Proj1/page.aspx" C: / DIR / PAGE. ASPX ")

Return _PathTranslated;

}

Public override string getPathInfo () {// Returns other path information for resources with URL extensions. That is, for path / viRDIR/Page.html/tail ,getpathinfo value is / tail.

Return_pathinfo;

}

Public override string getAppPath () {// Returns the virtual path of the server application currently executing.

Return_host.virtualpath;

}

Public override string getAppPathTranslated () {// Returns the UNC translation path of the server application that is currently executing.

Return_host.physicalpath;

}

Public override byte [] getpreloadededEntityBody () {// Returns the part of the HTTP request body has been read.

Return_PreLoadedContent;

}

Public override bool iSentirentityBodyiSpreLoaded () {// Returns a value indicating whether all request data is available, and if you don't need to read further on the client.

Return (_ContentLength == _PreloadedContentLength);

}

Public override int tentityBody (Byte [] Buffer, int size) {// reads the client's request data (when it is not preloaded). INT BYTESREAD = 0;

Byte [] bytes = _conn.readRequestBytes (size);

IF (Bytes! = null && bytes.length> 0) {

BYTESREAD = bytes.length;

Buffer.blockcopy (Bytes, 0, Buffer, 0, Bytesread);

}

Return Bytesread;

}

Public Override String getknownRequestHeader (INDEX) {// Returns the standard HTTP request header corresponding to the specified index.

Return_KnownRequestHeaders [index];

}

Public Override String getUnknownRequestHeader (String name) {// Returns a non-standard HTTP request header value. Name

INT n = _unknownRequestheaders.Length;

For (int i = 0; i

IF (String.Compare (Name, _UnknownRequestHeaders [i] [0], true, cultureinfo.invariantculture) == 0)

Return_unknownRequestHeaders [i] [1];

}

Return NULL;

}

Public override string [] [] getUnknownRequestHeaders () {/ Name-value pair for all non-standard HTTP headers.

Return_unknownRequestHeaders;

}

Public override string getServerVariable (String name) {// returns a single server variable from the server variable dictionary associated with the request.

String s = string.empty;

Switch (name) {

Case "all_raw":

S = _allrawheaders;

Break;

Case "Server_Protocol":

S = _prot;

Break;

// more needed?

}

Return S;

}

Public override string mappath (String Path) {// Returns the physical path corresponding to the specified virtual 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.isvirtualpathappppppppatch) {

// 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 mapppedpath;

}

Public Override Void SendStatus (int stats) {// Specifies the HTTP status code and status description of the response; for example, SendStatus (200, "OK").

_RESPONSESTATUS = STATUSCODE;

}

Public Override Void SendknownResponseHeader (INDEX, STRING VALUE) {// Adds a standard HTTP header to a response.

IF (_Headerssent)

Return;

Switch (INDEX) {

Case httpWorkerRequest.HeaderServer:

Case httpWorkerRequest.HeaderDate:

Case httpWorkerRequest.HeaderConnection:

// ignore these

Return;

// Special Case Headers for Static File Responses

Case httpWorkerRequest.HeaderaCceptRanges:

IF (value == "bytes") {

_SpecialcasestaticFileHeaders = true;

Return;

}

Break;

Case httpWorkerRequest.HeadeRexpires:

Case httpWorkerRequest.HeaderlastModified:

IF (_SpecialcasestaticFileHeaders)

Return;

Break;

}

_ResponseHeadersbuilder.Append (getknownResponseheadername);

_RESPONSEHEADERSBUILDER.APPpend (":");

_RESPONSEHEADERSBUILDER.APPEND (VALUE);

_RESPONSEHEADERSBUILDER.APPpend ("/ r / n");

}

Public override void sendunknownResponseHead (String name, string value) {// Adds non-standard HTTP headers to a response. IF (_Headerssent)

Return;

_RESPONSEHEADERSBUILDER.APpend (name);

_RESPONSEHEADERSBUILDER.APPpend (":");

_RESPONSEHEADERSBUILDER.APPEND (VALUE);

_RESPONSEHEADERSBUILDER.APPpend ("/ r / n");

}

Public override void sendcalculatedContentLength {// Add the Content-Length HTTP header to the response.

IF (! _Headerssent) {

_RESPONSEHEADERSBUILDER.APPpend ("Content-Length:");

_RESPONSEHEADERSBUILDER.APpend (ContentLength.toString ());

_RESPONSEHEADERSBUILDER.APPpend ("/ r / n");

}

}

Public override bool headerssent () {// Returns a value indicating whether the HTTP response header has been sent to the client for the current request.

Return_Headerssent;

}

Public override bool isclientConnected () {// Returns a value indicating whether the client connection is still active.

Return_Conn.connected;

}

Public override void closeConnection () {// terminates the connection to the client.

_Conn.close ();

}

Public Override Void SendResponseFromMemory (byte [] data, intlength) {// adds the contents of the memory block to the response.

IF (Length> 0) {

BYTE [] bytes = new byte [length];

Buffer.blockcopy (Data, 0, Bytes, 0, Length);

_RESPONSEBODYBYTES.ADD (BYTES);

}

}

Public Override Void SendResponseFromFile (String FileName) {// Adds the contents of the file to the response.

IF (Length == 0)

Return;

FILESTREAM F = NULL;

Try {

f = New filestream (filename, filemode.open, fileaccess.read, fileshare.read);

SendResponsefromFileStream (f, offset, length);

}

Finally {

IF (f! = null)

f.close ();

}

}

Public Override Void SendResponseFromFile (INTPTR HANDLE, long offset, long length) {// Add file content to a response. Note this method is polymorphic

IF (Length == 0)

Return;

FILESTREAM F = NULL;

Try {

f = new filestream (Handle, FileAccess.Read, False);

SendResponsefromfilestream (f, offset, length);

Finally {

IF (f! = null)

f.close ();

}

}

Private Void SendresponseFromFileStream (FileStream F, long offset, long length) {// This is not overloaded, see clear J

Const int maxchunklength = 64 * 1024; // 64K as a transport block

Long filesis = f.ley;

IF (Length == -1)

Length = filesize - offset

IF (Length == 0 || Offset <0 || Length> FileSize - Offset)

Return;

IF (Offset> 0)

F.seek (offset, seekorigin.begin);

IF (Length <= maxchunklength) {

BYTE [] filebytes = new byte [(int) length];

INT BYTESREAD = F.Read (filebytes, 0, (int) Length);

SendResponseFromMemory (filebytes, bytesread);

}

Else {

Byte [] chunk = new byte [maxchunklength];

INT BYTESREMAING = (INT) Length;

While (bytesremaining> 0) {

INT BYTESTOREAD = (BytesRemaining

Int bytesread = f.read (chunk, 0, bytestoread);

SendResponseFromMemory (Chunk, BytesRead);

BYTESREMAING - = bytesread;

// Flush to release Keep Memory

IF (BytesRemaining> 0 && BytesRead> 0)

FlushResponse (false);

}

}

}

Public Override Void FlushResponse (Bool FinalFlush) {// Sends all hangned responses to the client.

IF (! _Headerssent) {

_Conn.writeheaders (_RESPONSESTATUS, _RESPONSEHEADERSBUILDER.TOSTRING ());

_headerssent = true;

}

For (int i = 0; i <_responsebodybytes.count; i ) {

Byte [] Bytes = (byte []) _RESPONSEBODYBYTES [i];

_Conn.writebody (bytes, 0, bytes.length);

}

_RESPONSEBODYBYTES = New ArrayList ();

IF (FinalFlush) {

_Conn.close ();

}

}

Public override void endofrequest () {// is used by the runtime to inform HTTPWORKERREQUEST Currently requested request processing has been completed.

// EMPTY METHOD

}

转载请注明原文地址:https://www.9cbs.com/read-91716.html

New Post(0)