Heribert Schuetz wrote:
> Hi,
>
> we are currently implementing a protocol handler (let's say for a schema
> "map") somewhat similar to the one for "resource" URIs. For an URI like
> "map:foo" the channel (a `mapChannel') looks up "foo" in some table,
> where it is mapped to, say, "http://bar.com/". So the mapChannel
> constructs an auxiliary channel for "http://bar.com/". This happens in
> mapChannel::Init(). Most of the other methods simply call the
> corresponding methods of the auxiliary channel.
>
> Exceptions:
>
> - mapChannel::AsyncOpen(...) calls AsyncOpen(...) of the auxiliary
> channel, but it passes itself as the listener rather than the
> "outside" listener received as an argument. (The outside listener is
> remembered in a member variable of the mapChannel.) Thus listener
> calls (OnStartRequest, OnStopRequest, OnDataAvailable) from the
> auxiliary channel are sent to the mapChannel, which forwards these
> calls to the outside listener, however passing itself rather than the
> received channel (which would typically be the auxiliary channel).
>
> - mapChannel::GetURI() returns the original URI ("map:foo") rather than
> asking the auxiliary channel for its URI ("http://bar.com/").
>
> The modification of AsyncOpen(...) and the implementation of the
> listener interface by mapChannel has essentially the purpose to force
> the outer listener to ask the mapChannel rather than the auxiliary
> channel for its URI.
>
> So far everything is quite standard and we think that we more or less
> understand it. It works. Now there is a somewhat more complex situation
> for which I would ask for your advice.
>
> Actually the mapping table is not readily available for lookup. We
> rather have a socket connection to some "map server". We can write the
> original URI ("map:foo" or just "foo") to that socket and tell the
> socket's input listener to perform some callback action when the
> translated URI arrives from the server.
>
> That is, we have only some sort of "asynchronous" access to the mapping
> table and we are not sure how to adapt the implementation of mapChannel
> to that.
>
> - One idea is to use threads and event queues to provide a synchronous
> interface to the "mapping service" (i.e., a function call returning
> the mapped URI rather than a callback).
>
> However, this might be overkill. It might also block important threads
> if not done properly. Furthermore, we would have to learn a lot about
> threads and event queues. (Is there any up-to-date documentation
> available?)
don't do this if you can help it... your fears about blocking an
important thread are right on... you'd be blocking the UI thread in mozilla!
>
> - Another idea is to stick with the callback-style interface of the
> mapping service: The mapChannel sends "foo" to the mapping service,
> thereby registering a callback that will eventually create the
> auxiliary channel.
take a look at nsAboutCacheEntry.cpp for such an example of a wrapped
channel.
>
> The problem with this approach is that we do not know how to deal with
> method calls to the mapChannel before the auxiliary channel is
> available. So we cannot simply forward them to the auxiliary channel.
> Well, for certain methods we can do something sensible, but still we
> would like to simply forward most method calls in order to make our
> layer as transparent as possible.
can you create the auxilary channel and simply delay the actual call to
AsyncOpen on the auxilary channel? this is what nsAboutCacheEntry does.
>
> Fortunately, it looks like many channel methods are not invoked before
> a channel has called OnStartRequest(...) at its listener. (After that
> time we know that the auxiliary channel exists and we can forward
> mapChannel method calls to it.) Can we rely on this? Is there some
> (written or implicit) specification that tells when which channel
> method may be legally invoked?
if you really can't create the auxilary channel up front, then you'd
need to implement nsIChannel methods as such:
GetStatus() -> let status = NS_OK
IsPending() -> let isPending = PR_TRUE
Cancel() -> cancel the creation of the auxilary channel, change the
status to the error code passed in and fire onStartRequest and
onStopRequest
Suspend()/Resume() -> you can safely ignore these, return
NS_ERROR_NOT_IMPLEMENTED
[GS]etLoadGroup() -> hold onto the load group if given one, and apply it
to your auxilary channel
[GS]etLoadAttributes() -> same as above
[GS]etNotificationCallbacks() -> same as above
[GS]etOwner() -> same as above
[GS]etOriginalURI() -> same as above
[GS]etContent{Type,Length} -> you can fail if onStartRequest has not yet
been called
GetURI() -> you know what to do here
SetURI() -> return NS_ERROR_NOT_IMPLEMENTED
[G]etSecurityInfo() -> let securityInfo = NULL
Open() -> return NS_ERROR_NOT_IMPLEMENTED
AsyncOpen() -> remember (listener, context) and return NS_OK
>
> Furthermore we have no idea how to implement mapChannel::Open() with
> this approach.
mozilla does not currently use nsIChannel::open() for anything.
>
>
> Any hints are appreciated,
>
> Heribert.