HOWTO: Work around SendRawData quirk of httpd.dll on Windows CE

This is actually a question about the HTTP Server in Windows CE , which actually has nothing to do with IIS because it is not even the same code base. So, the server's behavior can be different from IIS (and in this case, I would say that it is very different than IIS's behavior). Incidentally, this is similar to how ISA Server also supports an ISAPI Filter API which loosely looks like IIS's, but once again, completely different code base and no guarantee of similar behavior...

Question:

Dear Mr. Wang,

I am a 25 old student from Germany, and I'm trying to write an ISAPI-Filter for the Webserver integrated in Windows CE 4.2 (httpd.dll).

The Filter should replace some placeholders with dynamically calculated data.

Because of the different sizes of the source and destination code it is necessary to change the "Content-Length"-part of the header.

To do this i execute the following steps:

In the "OnSendRawData"-Method:

1. Catch the header and store it into a buffer
2. suppress the output (to do this I set cbInData=0 and pvInData='\0')
3. Catch the html-data
4. suppress output (to do this I set cbInData=0 and pvInData='\0')
5. Replace the Data
6. recalculate the "Content-Length" in the Header

In the "OnEndOfRequest"-Method:

7. Send the new Header
8. Send the modified Data

This works really fine, if the html-data's size is less or equal to 4096 bytes.

If the html-file is greater than 4k, the server is splitting the file in several blocks with a size of 4k.

To replace the data an calculate the the new size I need the whole file, so I tried to catch all data-blocks and copy the content to one big string.

A problem occur if I suppress the output of the first data-block by setting the cbInData=0 and pvInData='\0'. After this the server won't send the following data blocks, the next "NotificationType" sent by the server is "SF_NOTIFY_END_OF_REQUEST".

If I don't suppress the output, the data is collected an manipulated correctly, but the corresponding website is not valid (two html-data-blocks and the manipulated header-data as ascii-text).

Could you tell me a possibility to suppress the output of the html-data that doesn't cause a jump to "EndOfRequest"?

I tried several way of terminating the pvInData, but the result was the same (incomplete data or, if i don't suppress the output, two html-blocks).

Thank you very much!

Best regards,

Answer:

Actually, what you are proposing to do is pretty much one of the correct ways to modify outgoing responses and does work on IIS on non-WinCE Windows OS. However, httpd on Windows CE 4.2 is not from the same codebase as IIS (it is completely different rewrite by another team that reimplemented the ISAPI Filter API themselves) and apparently behaves differently.

I can think of two suggestions:

  1. When you suppress output, try setting only cbInData to 0. On IIS, as long as cbInData = 0, the output is suppressed. Setting pvInData to NULL is an optional step. I do not know if httpd.dll behaves likewise, though.
  2. HACK: Instead of suppressing output, try sending out a single SPACE character instead. Many parts of the HTTP RFC treat multiple LWS (linear white space) characters as a single LWS and optionally normalized away from interpretation (for example, HTTP header field values CANNOT have leading/trailing LWS because it can be trimmed away and is not considered a part of the field's value). Thus, if you can control the output that the client sees such that the single SPACE character of the suppressed output ends up being leading/trailing LWS of something that the client interprets as HTTP header (and buffer the rest), you can hack around the issue in httpd.dll

Example:

Suppose this is what the original request looks like, where you replace %DATA% with "Custom Data"

 Packet 1
HTTP/1.1 200 OK\r\n
Server: My Web Server\r\n
Content-Type: text/html\r\n
Content-Length: 6\r\n
\r\n

Packet 2
%DATA%

For #2, maybe you want to buffer and send the following response:

 Packet 1 (SF_NOTIFY_SEND_RAW_DATA for Packet 1 above)
HTTP/1.1 200 OK\r\n
Server: My Web Server

Packet 2 (SF_NOTIFY_SEND_RAW_DATA for Packet 2 above)
SP

Packet 3 (WriteClient from SF_NOTIFY_END_OF_REQUEST of rest of buffered request)
\r\n
Content-Type: text/html\r\n
Content-Length: 11\r\n
\r\n
Custom Data

Notice that you suppressed output in packet #2 to consume the %DATA% , but you output a SP instead. What the client sees is a Server: header with a value of "My Web Server SP", but HTTP spec says that trailing SP can be normalized to one LWS and can be trimmed. Thus, a valid HTTP client should still interpret the Server: header with the field value of "My Web Server".

Voila. You have just buffered and modified the response without really breaking HTTP rules nor how its values are interpreted and worked around an apparent quirk in httpd.dll.

//David