Asynchronous pluggable protocol

Source: Internet
Author: User
Tags call back

This article was contributed by Dave stone

Introduction

Like most people who contrihing, I think it's a part of wanting to contribute something back into the system. However, I 've always felt that perhapsI'm not worthyBeing a mere social programmer rather than by Sion. anyhow, a caveat, I'm self taught and I don't profess to be an expert, however I do like to find out how things work, and this article is related to some of my findings regarding asynchronous pluggable protocols (Apps ). I came up with an idea for a piece of code whereby I cocould create some functionality that wocould sit between Internet Explorer and the HTTP protocol such that I cocould monitor and audit the traffic. after much research I discovered the asynchronous pluggable protocol technology that allows one to create a Protocol scheme (eg, much like http:, file:, etc) and that the architecture was extensible and flexible enough to introduce new schemes. to gain some exposure to the architecture I decided to create a simple protocol Scheme (APP :) That wowould retrieve an HTTP page. the code samples you will see merely demonstrate a simple request using wininet (not MFC) and using callbacks for asynchronous operation, the program will not stand on it's own and do much useful. there is hardly any error handling. </exit excuse mode>

The Registry

If you look underHkey_classes_root/protocols/HandlerYou'll see a very familiar list of protocol schemes, eg http:, file:, local:, mailto :. under each of these keys is a string value with the name of clsid. for hkey_classes_root/protocols/handler/HTTP/CLSID this is {79eac9e2-baf9-11ce-8c82-00aa004ba90b }. so, right now you're probably thinking is it that easy to create new schemes, well, fundamentally, the answer is yes. create a new COM Object (more on it's interfaces soon), create a new key under the handler hierarchy and create CLSID string value and off you go.

The interfaces

So, what of interfaces, well, the COM object needs to support iinternetprotocol, this is mandatory. others are optional and I'll talk about those later. iinternetprotocol is inherited from iinternetprotocolroot and together these interfaces have a number of methods that must be implemented in the pluggable protocol. they are:

// IInternetProtocolRootStart( LPCWSTR szUrl, IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo, DWORD grfPI, DWORD dwReserved);Continue( PROTOCOLDATA *pProtocolData);Abort( HRESULT hrReason, DWORD dwOptions);Terminate(DWORD dwOptions);Suspend( void);Resume( void);// IInternetProtocol based on IInternetProtocolRootRead( void *pv, ULONG cb, ULONG *pcbRead);Seek( LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition);LockRequest(DWORD dwOptions);UnlockRequest( void);

For the purposes of this article, the only real important ones are iinternetprotocolroot: Start () and iinternetprotocol: Read. both suspend () and resume () are not implemented and shoshould return e_notimpl. one of the optional interfaces is iinternetprotocolinfo, this is provided shocould you wish to process URL strings and break them up. I have not provided this, thus Internet Explorer will provide it's own processing.

Interface Definition-IDL

The IDL is relatively simple, no need to expose any dual interfaces, only iunknown is required, then the client (Internet Explorer) can go querying for other mandatory/option interfaces.

import "oaidl.idl";import "ocidl.idl";[uuid(615A023A-8069-11D2-A019-204C4F4F5020),version(1.0),helpstring("Pluggable 1.0 Type Library")]library PLUGGABLELib{importlib("stdole32.tlb");importlib("stdole2.tlb");[uuid(615A0247-8069-11D2-A019-204C4F4F5020),helpstring("HttpFilter Class")]coclass HttpFilter{interface IUnknown;};};
Requesting the data

Essential, when Internet Explorer sees the Protocol scheme name and an URL following, it will create the object (using the CLSID from the Registry) and pass information to the START () method. the first parameter is simple, this is the URL, including the scheme name, eg app :. the second parameter is a pointer to an interface, iinternetprotocolsink. this interface is provided by the client of an asynchronous pluggable protocol such that the handler can handle y the client of any events, eg data available. the rest of the parameters are trivial and not important for this particle example. so, we have the URL and need to start an HTTP transfer. first thing that must be done is to save the protocolsink pointer:

// Save pointers, we'll need them later
m_pProtocolSink = pOIProtSink;

As we'll be using the wininet functions, we need to first get a handle such that we can perform some I/O functions, we do this by using the internetopen () function. note that we specifyInternet_flag_asyncOption, because we do not want operations to go blocked and hold up Internet Explorer.

// Get handle for internet I/O
m_hInternetSession = InternetOpen (AgentName, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, INTERNET_FLAG_ASYNC);
if (!m_hInternetSession) {ATLTRACE(_T("InternetOpen () failed/n"));return S_FALSE;}else ATLTRACE(_T("InternetOpen () succeeded, handle is 0x%lX/n"), m_hInternetSession);    

Now that we have a handle to the 'internet' we can start doing something useful. the first thing you might do is manipulate the URL in some way so that you construct a new one, for the purposes of the example, I'll just use a canned URL, but before we do, we need to establish a callback. this is done thus.

InternetSetStatusCallback (m_hInternetSession, CHttpFilter::StatusCallback);

Note that the statuscallback () method is a static function as it can't be used in the context of a class/object. however, now we are going to actually start some real work, this takes the form of opening an URL (in asynchronous mode ). the very last parameter to this call is passing a user specified"Context"DWORD. I 've chosen to use our object pointer (this) so that the callback method can access some member variables.

#define CANNED_URL "http://www.corporate.com/default.htm"InternetOpenUrl(m_hInternetSession, CANNED_URL, NULL, 0, INTERNET_FLAG_RELOAD, (DWORD) this);
return S_OK;

If we weren't operating in an asynchronous environment, we wocould have expected internetopenurl () to immediately pass back a handle, however, our callback will communicate any handles.

The callback

That's it, we now need to sit back and wait for those Callbacks. As I said previusly,"Context"DWORD passed is our object pointer, so now, in the callback:

CHttpFilter::StatusCallback (HINTERNET hSession, DWORD Context, DWORD Status, LPVOID pInformation, DWORD InfoLength){CHttpFilter *pClass = (CHttpFilter *) Context;

We work it back the other way. A lot of the callto this callback reflect the status so-far, so you get a lot of "resolving hostname", "host contacted", "sending request", type messages. however, some of the messages are (obviusly) Important and contain some sort of information, this information is communicated through the pinformation and infolength parameters. the first one we are interested in is the handle that needs to be returned from the initial internetopenurl () call. by using a switch statement on the Status parameter we catch the appropriate status:

case INTERNET_STATUS_HANDLE_CREATED:ATLTRACE(_T("INTERNET_STATUS_HANDLE_CREATED as 0x%lX/n"), *(DWORD *)pInformation);pClass -> m_hHttpSession = * (HINTERNET *) pInformation;// Copy to member variablebreak;

The handle is stored for future use. we now wait for wininet to retrieve the file, when it arrives we'll receive the internet_status_request_complete status. at that point we then call back in to our client (Internet Explorer) through it's iinternetprotocolsink interface through the reportdata () method. this method allows you to report intermediate SS. the 100,100 reflects the fact that 100% of the 100% is complete and that the client shocould request data.

case INTERNET_STATUS_REQUEST_COMPLETE:ATLTRACE(_T("INTERNET_STATUS_REQUEST_COMPLETE/n"));// Tell sink that we have the datapClass -> m_pProtocolSink -> ReportData (BSCF_DATAFULLYAVAILABLE, 100, 100);break;

We now wait for the iinternetprotocolsink client, Internet Explorer, to call back into us and request the data. this is achieved by implementing the read () method. remember that the handle used by internetread () is the one that we have ed in the callback and saved away in a member variable.

CHttpFilter::Read(void *pv, ULONG cb, ULONG *pcbRead){BOOLStatus;Status = InternetReadFile (m_hHttpSession, pv, cb, pcbRead);ATLTRACE(_T("nBuffer is %ld/n"), *pcbRead);if (Status == FALSE) {DWORD error = GetLastError();ATLTRACE(_T("Errno = %d/n"), error);return INET_E_DOWNLOAD_FAILURE;}elseif (*pcbRead == cb) {// more to read ?return S_OK;}return S_FALSE;}

The documentation for iinternetprotocol: Read () states that the client may call the method several times after having read to end of file, in which case you must return s_false. apart from cleaning up by closing some handles (via internetclosehandle () that's about it.

Final note

As a small exercise, This asynchronous pluggable protocol was quite informative, in particle, just watching atltrace statements flashing by is an eye opener. my project took a different turn shortly afterwards. the thinking was that "Heck, Microsoft have done all the hard work in there HTTP asynchronous pluggable protocol, why not use that? ". So, I 've started down that path. now Internet Explorer CILS my pluggable protocol, and I call the real HTTP pluggable protocol pretending to be Internet Explorer (you only have to implement iinternetprotocolsink ). however, I soon found that I became unstuck in that Internet Explorer seems to special case the HTTP asynchronous pluggable protocol and made it non-pluggable. additionally, I ended u P implementing the URL Processing interface (iinternetprotocolinfo) and again used the wininet functions to do the hard work. however, HTTP aside, it does make the mind think, what if I cocould create ???? Pluggable protocol, eg a deciphering one (PGP :), or a database accessing one (SQL :). Have fun ....

Any feedback gratefully received.

Reference Material

Asynchronous pluggable protocols reference
Asynchronous pluggable protocols Overview
Iinternetprotocolroot
Iinternetprotocol
Iinternetprotocolsink
Etcprotocol demonstrates pluggable protocol handler (from KnowledgeBase: never got this sample to work in vc5 or 6)

Downloads

Download Demo project-11 KB

 

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.