Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5442 lines
160 KiB

/*++
Copyright (C) Microsoft Corporation, 2001
Module Name:
Http2.hxx
Abstract:
HTTP2 transport-specific constants and types.
Author:
KamenM 08-30-01 Created
Revision History:
--*/
/*
====== DATA STRUCTURES & BASIC SCENARIOS ======
Client/Server Side General Map:
+------------------------------+
| |
| Runtime Connection |
| |
+------------------------------+
| |
| Transport Connection |
| |
+------------------------------+
|
|
+--------------------------+--------------------------+
\ \ \ \
+---------+ +---------+ +---------+ +---------+
| IN | | IN | | OUT | | OUT |
| Channel | | Channel | | Channel | | Channel |
| Stack 0 | | Stack 1 | | Stack 0 | | Stack 1 |
+---------+ +---------+ +---------+ +---------+
Note that unless channel recycling is in progress, we will have only one
channel of a given type active at a time. That is the data structure will
look like this:
+------------------------------+
| |
| Runtime Connection |
| |
+------------------------------+
| |
| Transport Connection |
| |
+------------------------------+
|
|
+--------------------------+--------------------------+
\ \ \ \
+---------+ = +---------+ =
| IN | | OUT |
| Channel | | Channel |
| Stack 0 | | Stack 0 |
+---------+ +---------+
When we recycle a channel (let's say an IN channel), we will attach the new IN channel
in the empty slot:
+------------------------------+
| |
| Runtime Connection |
| |
+------------------------------+
| |
| Transport Connection |
| |
+------------------------------+
|
|
+--------------------------+--------------------------+
\ \ \ \
+---------+ +---------+ +---------+ =
| IN | | IN | | OUT |
| Channel | | Channel | | Channel |
| Stack 0 | | Stack 1 | | Stack 0 |
+---------+ +---------+ +---------+
When we're done establishing the new channel, we will discard the old channel and the data
structure will look like this:
+------------------------------+
| |
| Runtime Connection |
| |
+------------------------------+
| |
| Transport Connection |
| |
+------------------------------+
|
|
+--------------------------+--------------------------+
\ \ \ \
= +---------+ +---------+ =
| IN | | OUT |
| Channel | | Channel |
| Stack 1 | | Stack 0 |
+---------+ +---------+
The connection between the transport connection (the virtual connection)
and the channel stacks is a "soft" connection (denoted by '\' in the diagram).
Both keep a pointer to each other, but the object on the other end can disappear
in a safe manner. This is done in the following way. Before each party can
dereference the pointer, it must lock it. The lock operation can fail if the
pointer is gone or is going away. The disconnect always originates
from the virtual connection. If the virtual connection decides to disconnect a
channel stack, it calls into the channel (after locking) to tell it it is going
away. This operation will block until all calls from the channel stack up have
completed. New locks from the channel to the virtual connection (called Parent)
will fail. Once all upcalls have been drained, the channel->parent pointer will
be zero'ed out and execution will return to the virtual connection which will
zero out its pointer to the channel.
The Runtime Connection/Virtual Connection pair resides in a single block of memory
and has a single lifetime (refcounted) with the lifetime controlled by the runtime.
Each channel stack has its own lifetime and refcounting. This allows it to go away
earlier or stay behind the virtual connection.
IN/OUT Proxy General Map:
+------------------------------+
| |
| Transport Connection |
| |
+------------------------------+
|
|
+--------------------------+--------------------------+
\ \ \ \
+---------+ +---------+ +---------+ +---------+
| IN | | IN | | OUT | | OUT |
| Channel | | Channel | | Channel | | Channel |
| Stack 0 | | Stack 1 | | Stack 0 | | Stack 1 |
+---------+ +---------+ +---------+ +---------+
The same rules apply as in the Client/Server case.
CHANNEL STACKS:
The top and bottom channel have somewhat special functions. All other channels fit
the same pattern. The stack is modeled similiarly to the NT IO System driver
stack (where a channel corresponds to a driver and the virtual connection and the top
channel map to the IO System). Each channel interacts uniformly with its neighbouring
channels and its top channel. It doesn't care what the neighbouring channels are and
what the top channel is. A single channel type (class) can and does participate in
multiple channel stacks. It can handle a certain number of operations (see the virtual
methods in HTTP2TransportChannel for a list of the operations) though if it doesn't want
to handle a particular operation, it doesn't need to override it and it can just delegate
to the base class (which in most cases sends down). Each operation has a direction. For
example, receives (reads) go down. This means that if a read is issued to the channel
stack, the channel on the top is given a crack at it. If it wants to do something special
during receives, it can do so. If not, it simply delegates to the channel below it (it
doesn't know what channel is below it and it doesn't care). Thus the read operation travels
through the whole stack with each party getting a crack at the operation (very much like
an IRP travelling through the driver stack except that C++ classes are used here).
The basic map of a channel stack is like this:
/////////////////////////////////
/ Top Channel /<------+
///////////////////////////////// |
| /\ |
\/ | |
+-------------------------------+ |
| Channel 1 +-------+
+-------------------------------+ /\
| /\ |
\/ | |
... |
... |
... |
| /\ |
\/ | |
+-------------------------------+ |
| Channel N +-------+
+-------------------------------+ /\
| /\ |
\/ | |
(-------------------------------) |
( Bottom Channel )-------+
(-------------------------------)
Each channel points to its upper and lower layer (with the exception of top and bottom). Each
channel also points to the top channel while provides special functionality like refcounting
to all other channels. The bottom channel has some special responsibilities in terms of
completing the async requests whose completion was initiated by the IO System.
Client Side IN Channel Stack (top channel denoted by /// box and bottom by (-) box):
//////////////////////////////////
/ HTTP2ClientInChannel /
+--------------------------------+
| HTTP2PlugChannel |
+--------------------------------+
| HTTP2FlowControlSender |
+--------------------------------+
| HTTP2PingOriginator |
+--------------------------------+
| HTTP2ChannelDataOriginator |
(--------------------------------)
( HTTP2WinHttpTransportChannel )
(--------------------------------)
Client Side OUT Channel Stack (same notation):
//////////////////////////////////
/ HTTP2ClientOutChannel /
+--------------------------------+
| HTTP2EndpointReceiver |
+--------------------------------+
| HTTP2PingReceiver |
(--------------------------------)
( HTTP2WinHttpTransportChannel )
(--------------------------------)
In Proxy IN Channel Stack (same notation):
//////////////////////////////////
/ HTTP2InProxyInChannel /
+--------------------------------+
| HTTP2ProxyReceiver |
+--------------------------------+
| HTTP2PingReceiver |
(--------------------------------)
( HTTP2IISTransportChannel )
(--------------------------------)
In Proxy OUT Channel Stack (same notation):
//////////////////////////////////
/ HTTP2InProxyOutChannel /
+--------------------------------+
| HTTP2ProxyPlugChannel |
+--------------------------------+
| HTTP2FlowControlSender |
(--------------------------------)
(HTTP2ProxySocketTransportChannel)
(--------------------------------)
| /\
\/ |
+--------------------------------+
| WS_HTTP2_CONNECTION |
+--------------------------------+
Note how the OUT channel points to a simple transport connection. The connection
is not part of the stack semantically (though it does have the same lifetime) but
it does perform a lot of the functions of the stack.
Out Proxy IN Channel Stack (same notation):
//////////////////////////////////
/ HTTP2OutProxyInChannel /
+--------------------------------+
| HTTP2ProxyReceiver |
(--------------------------------)
(HTTP2ProxySocketTransportChannel)
(--------------------------------)
| /\
\/ |
+--------------------------------+
| WS_HTTP2_CONNECTION |
+--------------------------------+
Out Proxy OUT Channel Stack (same notation):
//////////////////////////////////
/ HTTP2OutProxyOutChannel /
+--------------------------------+
| HTTP2ProxyPlugChannel |
+--------------------------------+
| HTTP2FlowControlSender |
+--------------------------------+
| HTTP2PingOriginator |
+--------------------------------+
| HTTP2PingReceiver |
(--------------------------------)
( HTTP2IISSenderTransportChannel )
(--------------------------------)
Server IN Channel Stack (same notation):
//////////////////////////////////
/ HTTP2ServerInChannel /
+--------------------------------+
| HTTP2EndpointReceiver |
(--------------------------------)
( HTTP2SocketTransportChannel )
(--------------------------------)
| /\
\/ |
+--------------------------------+
| WS_HTTP2_CONNECTION |
+--------------------------------+
Server OUT Channel Stack (same notation):
//////////////////////////////////
/ HTTP2ServerOutChannel /
+--------------------------------+
| HTTP2PlugChannel |
+--------------------------------+
| HTTP2FlowControlSender |
+--------------------------------+
| HTTP2ChannelDataOriginator |
(--------------------------------)
( HTTP2SocketTransportChannel )
(--------------------------------)
| /\
\/ |
+--------------------------------+
| WS_HTTP2_CONNECTION |
+--------------------------------+
EXAMPLE SCENARIOS:
Client side send (steps numbered next to diagram):
+------------------------------+
| |
| Runtime Connection |<-----------------------------+
| | |
+------------------------------+ |
1 | /\ |
\/ | |
+------------------------------+ |
| | |
| Transport Connection | |
| | |
+------------------------------+ |
16 | |
| |
+--------------------------+------------------------------+ |
2 \ 15 \ \ \ |
///////////////////////////////// = +---------+ = |
/ HTTP2ClientInChannel / | OUT | |
///////////////////////////////// | Channel | |
3 | /\ 14 | Stack 0 | |
\/ | +---------+ |
+-------------------------------+ |
| HTTP2PlugChannel + |
+-------------------------------+ |
4 | /\ 13 |
\/ | |
+-------------------------------+ |
| HTTP2FlowControlSender + |
+-------------------------------+ |
5 | /\ 12 |
\/ | |
+-------------------------------+ |
| HTTP2PingOriginator + |
+-------------------------------+ |
6 | /\ 11 |
\/ | |
+-------------------------------+ |
| HTTP2ChannelDataOriginator + |
+-------------------------------+ |
7 | /\ 10 |
\/ | |
(-------------------------------) |
( HTTP2WinHttpTransportChannel ) |
(-------------------------------) |
|
8 9 17 -------------------------------------------+
1. Runtime calls HTTP_Send (in order to do a send)
2. Virtual connection locks default out channel, adds a reference and submits
the send to the top channel.
3. Top channel optionally does some processing and submits to lower layer.
4. HTTP2PlugChannel optionally does some processing and submits to lower layer.
5. HTTP2FlowControlSender optionally does some processing and submits to lower layer.
6. HTTP2PingOriginator optionally does some processing and submits to lower layer.
7. HTTP2ChannelDataOriginator optionally does some processing and submits to lower layer.
8. HTTP2WinHttpTransportChannel optionally does some processing, submits to WinHTTP and
returns. The return goes all the way to the runtime.
9. A send complete will be indicated by WinHTTP on a different thread. Our WinHTTP callback
gets called and it posts a request on an RPC worker thread to process the send complete.
10. HTTP2WinHttpTransportChannel optionally does some processing and sends to upper layer by
calling SendComplete on it.
11. HTTP2ChannelDataOriginator optionally does some processing and sends to upper layer by
calling SendComplete on it.
12. HTTP2PingOriginator optionally does some processing and sends to upper layer by
calling SendComplete on it.
13. HTTP2FlowControlSender optionally does some processing and sends to upper layer by
calling SendComplete on it.
14. HTTP2PlugChannel optionally does some processing and sends to upper layer by
calling SendComplete on it.
15. HTTP2ClientInChannel optionally does some processing and sends to virtual connection by
calling SendComplete on it.
16. Virtual connection may do some processing on it and returns. The call returns all the way
to the worker thread.
17. Depending on the return code it returned with, it will either
go to the runtime, or the worker thread will return to the pool. If return code is not
RPC_P_PACKET_CONSUMED, the runtime will be called. Else the thread will return directly to
the pool.
Server Side Connection Establishment (steps numbered next to diagram):
A1. A2.
////////////////////// /////////////////////////////////////
/ HTTP Address / <-------+ / Runtime Connection /
////////////////////// | /////////////////////////////////////
+---- / WS_HTTP2_INITIAL_CONNECTION /
/////////////////////////////////////
A1. A listen (AcceptEx) on the address completes.
A2. Address object creates a runtime connection and an object of type
WS_HTTP2_INITIAL_CONNECTION. Note that the transport doesn't know whether it
will be talking the new or the old http protocol. Therefore it establishes
the WS_HTTP2_INITIAL_CONNECTION object which will look into the first received
data packet on this connection and then decide what object it will create. If the
first data packet is an old HTTP data packet, it goes into the old protocol. If it is
a new data packet, it proceeds to step B1.
+------------------------------+
| |
| Runtime Connection |
| |
+------------------------------+ B1
| |~/////////////////////////////////////
| Transport Connection |~/ WS_HTTP2_INITIAL_CONNECTION /
| |~/////////////////////////////////////
+------------------------------+ B3 |
| |
| |
+--------------------------+------------------------------+ |
\ \ \ \ |
///////////////////////////////// = = = |
/ HTTP2ServerInChannel / |
+-------------------------------+ |
| HTTP2EndpointReceiver + | B2
(-------------------------------) B4 |
( HTTP2SocketTransportChannel ) |
(-------------------------------) |
| /\ |
\/ | |
+--------------------------------+ |
| WS_HTTP2_CONNECTION |~---------------------------------------------------+
+--------------------------------+
B1. The WS_HTTP2_INITIAL_CONNECTION receives a packet from the new protocol. Assume it
is D1/A1.
B2. WS_HTTP2_INITIAL_CONNECTION allocates space for the IN channel stack and migrates the
WS_HTTP2_INITIAL_CONNECTION object into its proper location on the stack turning it into
WS_HTTP2_CONNECTION (the tilde (~) stands for morph into - sure enough Booch didn't have a
symbol for this operation :-)). It initializes the rest of the stack.
B3. The place where WS_HTTP2_INITIAL_CONNECTION used to be becomes a virtual connection
(again morphing).
B4. A connection with this cookie is searched for. If none is found, the current
virtual connection is added to the cookie collection.
If one is found, it will look this way:
+------------------------------+
| |
| Runtime Connection |
| |
+------------------------------+
| |
| Transport Connection |
| |
+------------------------------+
|
|
+--------------------------+--------------------------+
\ \ \ \
= = +---------+ =
| OUT |
| Channel |
| Stack 0 |
+---------+
In this case we don't attach the newly created stack to the virtual connection, so the
current virtual connection looks like this:
+------------------------------+
| |
| Runtime Connection |
| |
+------------------------------+
| |
| Transport Connection |
| |
+------------------------------+
|
|
+--------------------------+--------------------------+
\ \ \ \
= = = =
We add the newly created stack to the existing virtual connection so that it now looks like
this:
+------------------------------+
| |
| Runtime Connection |
| |
+------------------------------+
| |
| Transport Connection |
| |
+------------------------------+
|
|
+--------------------------+--------------------------+
\ \ \ \
+---------+ = +---------+ =
| IN | | OUT |
| Channel | | Channel |
| Stack 0 | | Stack 0 |
+---------+ +---------+
Then we return failure so that the old virtual connection is destroyed (we have
effectively attached a leg to the existing virtual connection).
====== RULES =======
Rule 1: Can't issue upcalls from submission context
Rule 2: _Can_ issue upcalls from upcall context
Rule 3: Can't disconnect channels from upcall context (unless
the calling channel is exempt or we disconnect a different
channel than the one we're making the upcall on)
Rule 4: Channels above EndpointReceiver need not consume failed
receives. If they choose to consume them, they can do
so, but they can always delegate this to EndpointReceiver.
If they delegate and return an error, the connection will
be aborted by the endpoint receiver. This only applies to
received that came in as failures, not to successful receives
converted to failures. In the conversion case, rule 8 needs
to be observed.
Rule 5: Whoever consumes the packet must send the return status
to RPC_P_CONSUME_PACKET, free the packet and possibly do the
abort work in case of failure.
Rule 6: Fatal errors on the proxy abort the whole tomato using
AbortAndDestroy. Fatal errors on the client or server
should only abort the channels or the connection, but
not destroy the connection. The runtime will do that
on client/server.
Rule 7: EndpointReceiver will not consume success RTS receives.
Rule 8: Failed RTS receives do not have a valid buffer. Corollary is that
if channels convert successfull RTS receive to a failure, they must
consume the packet
Rule 9: Channels that initiate operations on lower channels from upcall or
neutral context must use BeginSimpleSubmitAsync to synchronize with aborts.
Channels that are from submission context already have that
synchronization provided.
Rule 10: Receive traffic below the endpoint receiver or in the absence of an endpoint
receiver must be raw only.
Rule 11: Abort on client/server must completely destroy the object without damaging
the data. Abort may be closed multiple times, and subsequent aborts must
be able to figure out the abort was already done (which they do by looking
at the data)
Rule 12: All elements in a proxy can return any error code to an upcall. The bottom
channel will consume it if not consumed already.
Rule 13: All elements in a client or server must be careful to consume the packets
the runtime is not supposed to see.
Rule 14: Upcall contexts and aborts are not synchronized. Corollary - if code in
upcall context wants to submit something, it must call
TopChannel->BeginSimpleSubmitAsync. Exception is when channel is detached
before aborted.
Rule 15: Submission contexts and aborts are sycnhronized.
Rule 16: Failed sends may have a buffer, but it must not be touched. It is owned
by the transport
Rule 17: All lower channels are guaranteed single abort by the top channel.
Rule 18: All channels above HTTP2ChannelDataOriginator must monitor send return codes
and if channel recycling is needed, initiate one. Those in runtime context just pass
the error code to the virtual connection. Those in neutral context get this for free
when they call AsyncCompleteHelper.
Rule 19: Channel detach can happen before Abort.
Rule 20: Unlinked Send Context must be channel agnostic. During channel recycling, we may
start a send on one channel, but then migrate the send to a different channel and
actually complete (including wait for it) on a different channel.
Rule 21: You can't synchronously wait for result in submission context
Rule 22: If a send is migrated b/n channels during recycling, care must be exercised
to complete the send in case of failure, and not just drop it on the floor.
Rule 23: The periodic timer holds one refcount to the channel. It will be removed during Abort
Rule 24: Each ping started holds one refcount (like all async operations)
Rule 25: Timeouts must be set only in upcalls in the proxy. This is because the cancelling
is synchronized with upcalls. Otherwise we have a race. If you decide to change this rule
you must modify HTTP2ProxyVirtualConnection::DisconnectChannels and provide other
synchronization mechanism.
Rule 26: All code that initiates sends on the endpoints must watch for and handle
channel recycling
Rule 27: During HTTP proxy search, the in channel will be direct connection while the out
channel will be through the proxy
Rule 28: If the RPC proxy is a web farm, and clients access it through an HTTP proxy that does
not support keep alive connections, and RPC_C_HTTP_FLAG_USE_FIRST_AUTH_SCHEME is not specified
and at least one machine supports only Basic authentication or anonymous access, then all
machines in the web farm must also support Basic/anonymous respectively.
Rule 29: If anybody calls HandleSendResultFromNeutralContext, all necessary cleanup will be done
inside HandleSendResultFromNeutralContext. Callers can handle error code only if they want.
Rule 30: The last send on the server will be sync from the runtime. There will be exactly one such
send. The transport will complete it immediately for the sake of unblocking the runtime thread
but will keep working on it in the background.
Rule 31: The channels must not touch the send context after the upper layer SendComplete has
completed - it may have been freed.
Rule 32: The Winsock providers will touch the overlapped on the return path of an IO. If the
overlapped may not be around for the return path of an IO submission, Winsock must not be used
for the IO.
Rule 33: If we're in a timer callback, we can't abort the connection from this thread. Abort will
try to cancel the timer and will deadlock. The abort must be offloaded to an RPC worker thread.
Rule 34: On the endpoints, if a receive complete will not return RPC_P_PACKET_CONSUMED, it must
not free the packet - it is owned by runtime in both failure and success case.
Rule 35: All abort paths on the client that can execute during open must check the IgnoreAborts flag
or use an abort method that does.
Rule 36: HTTP_Abort and HTTP_Close may be called on destroyed but unfreed objects. These functions
and all their callees must not use virtual methods (the vtable is gone), or must check for already
destroyed object.
Rule 37: Callers of ClientOpenInternal must abort the connection if IsFromUpcall is true.
ClientOpenInternal will not abort it in this case.
Rule 38: Callers of FreeChannelPointer must make sure that there are no pending operations on the
channel that carry runtime object refcounts. Otherwise, when they complete, their parent pointer
will be set to NULL and they won't be able to free their refcount.
Rule 39: If you are in an upcall on the virtual connection level on an endpoint (client or server)
the only thing that keeps the virtual connection from disappearing from underneath you is
the lock of the channel on the virtual connection. After this lock is gone (i.e. you call
FreeChannelPointer), you must not touch anything on the virtual connection - it may be gone.
Rule 40: HTTP2ChannelPointer::SetChannel() and HTTP2ChannelPointer::LockChannelPointer() can't race.
This also implies that HTTP2ServerVirtualConnection::InitializeServerConnection() that may attach
channels and call SetChannel should be synchronized with the paths that will LockChannelPointer,
such as AbortChannels().
*/
#if _MSC_VER >= 1200
#pragma once
#endif
#ifndef __HTTP2_HXX__
#define __HTTP2_HXX__
typedef enum tagHTTPVersion
{
httpvHTTP = 0,
httpvHTTP2
} HTTPVersion;
typedef enum tagHTTP2TrafficType
{
http2ttNone = 0,
http2ttRTS,
http2ttData,
http2ttAny,
http2ttRaw,
http2ttRTSWithSpecialBit, // used on the client only
http2ttDataWithSpecialBit, // to indicate additional conditions
http2ttRawWithSpecialBit
} HTTP2TrafficType;
// the RTS and Data will be used as 'OR' variables as well. Make sure it fits
C_ASSERT((http2ttRTS | http2ttData) == http2ttAny);
const int SendContextFlagPutInFront = 0x01;
const int SendContextFlagSendLast = 0x02;
const int SendContextFlagNonChannelData = 0x04;
const int SendContextFlagAbandonedSend = 0x08;
const int SendContextFlagProxySend = 0x10;
const int SendContextFlagPluggedChannel = 0x20;
extern long ChannelIdCounter;
class HTTP2SendContext : public CO_SEND_CONTEXT
{
public:
#if DBG
HTTP2SendContext (void)
{
ListEntryUsed = FALSE;
}
inline void SetListEntryUsed (
void
)
{
ASSERT(ListEntryUsed == FALSE);
ListEntryUsed = TRUE;
}
inline void SetListEntryUnused (
void
)
{
ListEntryUsed = FALSE;
}
#else
inline void SetListEntryUsed (
void
)
{
}
inline void SetListEntryUnused (
void
)
{
}
#endif
union
{
HANDLE SyncEvent; // sync sends will send this event. The completion
// path will check the event, and if it is set, it
// will fire it instead of propagating the completion
// beyond the channel. Cleared by the consumer of
// the completed operation
void *BufferToFree; // valid for abandoned sends only. Such sends are async
// and will use this memory location to store the
// pointer of the buffer to free once the send is done
} u;
LIST_ENTRY ListEntry; // used to queue packets
HTTP2TrafficType TrafficType; // the type of traffic in this context
#if DBG
BOOL ListEntryUsed; // for debug builds, set to non-zero if this
// packet is already queued. Otherwise zero. This
// field prevents multiple use of the ListEntry
// structure
#endif // DBG
unsigned int Flags; // currently can be SendContextFlagPutInFront
unsigned int UserData; // place for the user to store stuff
};
// a utility class with storage and manipulation routines for
// an HTTP2 RTS cookie
class HTTP2Cookie
{
public:
RPC_STATUS Create (
void
);
BYTE *GetCookie (
void
)
{
return Cookie;
}
void SetCookie (
IN BYTE *NewCookie
)
{
RpcpMemoryCopy(Cookie, NewCookie, sizeof(Cookie));
}
int Compare (
IN HTTP2Cookie *OtherCookie
)
{
return RpcpMemoryCompare(Cookie, OtherCookie->Cookie, sizeof(Cookie));
}
void ZeroOut (
void
)
{
RpcpMemorySet(Cookie, 0, sizeof(Cookie));
}
private:
BYTE Cookie[16];
};
class HTTP2VirtualConnection; // forward
class CookieCollection;
// a server cookie. Besides the capabilities of HTTP2Cookie,
// it can be queued in a cookie collection and can
// point to a virtual connection
class HTTP2ServerCookie : public HTTP2Cookie
{
public:
HTTP2ServerCookie (
void
) : RefCount(1)
{
RpcpInitializeListHead(&ListEntry);
Connection = NULL;
}
HTTP2ServerCookie (
IN HTTP2ServerCookie &Cookie
) : RefCount(1)
{
ASSERT(RpcpIsListEmpty(&Cookie.ListEntry));
ASSERT(Cookie.Connection == NULL);
SetCookie(Cookie.GetCookie());
RpcpInitializeListHead(&ListEntry);
Connection = NULL;
}
inline void SetConnection (
IN HTTP2VirtualConnection *NewConnection
)
{
Connection = NewConnection;
}
inline void AddRefCount (
void
)
{
int LocalRefCount = RefCount.Increment();
LogEvent(SU_REFOBJ, EV_INC, this, IntToPtr(0x77), LocalRefCount, 1, 1);
}
inline BOOL RemoveRefCount (
void
)
{
int LocalRefCount;
LogEvent(SU_REFOBJ, EV_DEC, this, IntToPtr(0x77), RefCount.GetInteger(), 1, 1);
LocalRefCount = RefCount.Decrement();
ASSERT(LocalRefCount >= 0);
return (LocalRefCount == 0);
}
friend CookieCollection;
private:
LIST_ENTRY ListEntry;
HTTP2VirtualConnection *Connection;
INTERLOCKED_INTEGER RefCount; // used when we fake web farms on the same machine. In this case
// instead of having each channel register its own connection,
// they use the refcount to coordinate the use of the entry in
// the cookie collection. Adds are synchronzed through the cookie collection
// but remove reference counts are not.
};
// the HTTP2 resolver hint. Essentially, a transport level
// association.
class HTTPResolverHint : public TCPResolverHint
{
public:
HTTPVersion Version; // what version of HTTP did we choose
// for the association
RPCProxyAccessType AccessType; // do we access the server directly?
char *RpcServer; // cache the converted name. If server name
// is less than sizeof(RpcServerName),
// RpcServerName will be used as storage. Otherwise
// a heap block will be allocated.
char RpcServerName[40]; // storage for the converted name
int ServerNameLength; // in bytes without terminating NULL
USHORT ServerPort;
char *RpcProxy; // cache the RpcProxy name.
int ProxyNameLength; // in bytes without terminating NULL
USHORT RpcProxyPort;
char *HTTPProxy; // cache the HTTPProxy name.
USHORT HTTPProxyPort;
HTTP2Cookie AssociationGroupId; // the transport association group id
// helper functions
inline void FreeHTTPProxy (void)
{
if (HTTPProxy)
{
delete HTTPProxy;
HTTPProxy = NULL;
}
}
inline void FreeRpcProxy (void)
{
if (RpcProxy)
{
delete RpcProxy;
RpcProxy = NULL;
}
}
inline void FreeRpcServer (void)
{
if (RpcServer && (RpcServer != RpcServerName))
{
delete RpcServer;
RpcServer = NULL;
}
}
void VerifyInitialized (
void
);
void Initialize (
void
)
{
RpcProxy = NULL;
HTTPProxy = NULL;
RpcServer = NULL;
}
};
class HTTP2Channel; // forward
class HTTP2TransportChannel
{
public:
HTTP2TransportChannel (
void
)
{
TopChannel = NULL;
LowerLayer = NULL;
UpperLayer = NULL;
}
// the destructor of the lower channels will never get called.
// Don't put anything here. Use FreeObject
virtual ~HTTP2TransportChannel (
void
)
{
}
virtual RPC_STATUS Send (
IN OUT HTTP2SendContext *SendContext
);
virtual RPC_STATUS Receive (
IN HTTP2TrafficType TrafficType
);
virtual RPC_STATUS SendComplete (
IN RPC_STATUS EventStatus,
IN OUT HTTP2SendContext *SendContext
);
virtual RPC_STATUS ReceiveComplete (
IN RPC_STATUS EventStatus,
IN HTTP2TrafficType TrafficType,
IN BYTE *Buffer,
IN UINT BufferLength
);
// Abort travels from top to bottom only
virtual void Abort (
IN RPC_STATUS RpcStatus
);
// travels up
virtual void SendCancelled (
IN HTTP2SendContext *SendContext
);
virtual void FreeObject (
void
) = 0;
// travels down
virtual void Reset (
void
);
void SetLowerChannel (IN HTTP2TransportChannel *LowerChannel)
{
LowerLayer = LowerChannel;
}
void SetUpperChannel (IN HTTP2TransportChannel *UpperChannel)
{
UpperLayer = UpperChannel;
}
void SetTopChannel (IN HTTP2Channel *TopChannel)
{
this->TopChannel = TopChannel;
}
protected:
// descendants may override this (specifically the proxies
// use their own version)
virtual RPC_STATUS AsyncCompleteHelper (
IN RPC_STATUS CurrentStatus
);
RPC_STATUS HandleSendResultFromNeutralContext (
IN RPC_STATUS CurrentStatus
);
HTTP2TransportChannel *LowerLayer;
HTTP2TransportChannel *UpperLayer;
HTTP2Channel *TopChannel;
};
class HTTP2BottomChannel : public HTTP2TransportChannel
{
public:
virtual RPC_STATUS SendComplete (
IN RPC_STATUS EventStatus,
IN OUT HTTP2SendContext *SendContext
);
virtual RPC_STATUS ReceiveComplete (
IN RPC_STATUS EventStatus,
IN HTTP2TrafficType TrafficType,
IN BYTE *Buffer,
IN UINT BufferLength
);
};
class HTTP2SocketTransportChannel : public HTTP2BottomChannel
{
public:
HTTP2SocketTransportChannel (
IN WS_HTTP2_CONNECTION *RawConnection,
OUT RPC_STATUS *Status
)
{
this->RawConnection = RawConnection;
}
virtual RPC_STATUS Send (
IN OUT HTTP2SendContext *SendContext
);
virtual RPC_STATUS Receive (
IN HTTP2TrafficType TrafficType
);
// SendComplete is inheritted
// ReceiveComplete is inheritted
virtual void Abort (
IN RPC_STATUS RpcStatus
);
virtual void FreeObject (
void
);
virtual void Reset (
void
);
private:
WS_HTTP2_CONNECTION *RawConnection;
};
class HTTP2FragmentReceiver : public HTTP2BottomChannel
{
public:
HTTP2FragmentReceiver (
void
)
{
pReadBuffer = NULL;
MaxReadBuffer = 0;
iLastRead = 0;
}
virtual RPC_STATUS Receive (
IN HTTP2TrafficType TrafficType
);
virtual RPC_STATUS ReceiveComplete (
IN RPC_STATUS EventStatus,
IN HTTP2TrafficType TrafficType,
IN OUT BYTE **Buffer,
IN OUT UINT *BufferLength
);
protected:
virtual RPC_STATUS PostReceive (
void
) = 0;
virtual ULONG GetPostRuntimeEvent (
void
) = 0;
RPC_STATUS DepositReceivedData (
IN ULONG DataSize,
IN BYTE *Data
);
BYTE *pReadBuffer; // the buffer we have submitted a read on. The read
// is actually submitted at pReadBuffer + iLastRead
ULONG MaxReadBuffer; // the maximum size of pReadBuffer
ULONG iLastRead; // next byte we will read in
ULONG iPostSize; // our normal post size
};
//
// Current state of the HTTP2WinHttpTransportChannel.
//
// The state diagram is as follows:
//
//
// whtcsNew -> whtcsOpeningRequest ->
// whtcsSendingRequest -> whtcsSentRequest -> whtcsReceivingResponse -> whtcsReceivedResponse
// | /\ | /\
// \/ | \/ |
// whtcsWriting whtcsReading
//
typedef enum tagHTTP2WinHttpTransportChannelState
{
whtcsNew = 0,
whtcsOpeningRequest,
whtcsSendingRequest,
whtcsSentRequest,
whtcsReceivingResponse,
whtcsReceivedResponse,
whtcsWriting,
whtcsReading,
whtcsDraining
} HTTP2WinHttpTransportChannelState;
class HTTP2WinHttpTransportChannel : public HTTP2FragmentReceiver
{
public:
friend void CALLBACK WinHttpCallback (
IN HINTERNET hInternet,
IN DWORD_PTR dwContext,
IN DWORD dwInternetStatus,
IN LPVOID lpvStatusInformation OPTIONAL,
IN DWORD dwStatusInformationLength
);
HTTP2WinHttpTransportChannel (
OUT RPC_STATUS *RpcStatus
);
RPC_STATUS HTTP2WinHttpTransportChannel::Open (
IN HTTPResolverHint *Hint,
IN const RPC_CHAR *Verb,
IN const RPC_CHAR *Url,
IN const RPC_CHAR *AcceptType,
IN ULONG ContentLength,
IN ULONG CallTimeout,
IN RPC_HTTP_TRANSPORT_CREDENTIALS_W *HttpCredentials,
IN ULONG ChosenAuthScheme,
IN const BYTE *AdditionalData OPTIONAL
);
virtual RPC_STATUS Send (
IN OUT HTTP2SendContext *SendContext
);
virtual RPC_STATUS Receive (
IN HTTP2TrafficType TrafficType
);
virtual RPC_STATUS SendComplete (
IN RPC_STATUS EventStatus,
IN OUT HTTP2SendContext *SendContext
);
virtual void Abort (
IN RPC_STATUS RpcStatus
);
virtual void FreeObject (
void
);
RPC_STATUS DirectReceiveComplete (
OUT BYTE **ReceivedBuffer,
OUT ULONG *ReceivedBufferLength,
OUT void **RuntimeConnection
);
RPC_STATUS DirectSendComplete (
OUT BYTE **SentBuffer,
OUT void **SendContext
);
void DelayedReceive (
void
);
void VerifyServerCredentials (
void
);
inline ULONG GetChosenAuthScheme (
void
)
{
return ChosenAuthScheme;
}
inline BOOL IsKeepAlive (
void
)
{
return KeepAlive;
}
private:
virtual RPC_STATUS PostReceive (
void
);
virtual ULONG GetPostRuntimeEvent (
void
);
ULONG NegotiateAuthScheme (
void
);
void ContinueDrainChannel (
void
);
void TryClosingWinHttpHandle (
IN OUT HINTERNET *pHandle
);
HINTERNET hSession;
HINTERNET hConnect;
HINTERNET hRequest;
ULONG NumberOfBytesTransferred;
RPC_STATUS AsyncError;
MUTEX Mutex;
LIST_ENTRY BufferQueueHead;
long PreviousRequestContentLength;
// Used to wait for state transitions. This is actually the thread
// event borrowed.
HANDLE SyncEvent; // valid during waits
HTTP2TrafficType DelayedReceiveTrafficType; // valid during b/n delayed
// receive and its consumption
// Current state of the transport
HTTP2WinHttpTransportChannelState State;
HTTP2SendContext *CurrentSendContext; // the context we are currently
// sending. Set before we submit a write, and picked
// on write completion.
long SendsPending;
RPC_HTTP_TRANSPORT_CREDENTIALS_W *HttpCredentials; // the transport
// credentials as kept by the runtime. These are the
// encrypted version.
ULONG ChosenAuthScheme; // valid after RPC_P_AUTH_NEEDED failure
ULONG CredentialsSetForScheme; // the scheme for which credentials have
// already been set
BOOL KeepAlive; // TRUE if the WinHttp connection is keep alive
};
class HTTP2ProxySocketTransportChannel : public HTTP2SocketTransportChannel
{
public:
HTTP2ProxySocketTransportChannel (
IN WS_HTTP2_CONNECTION *RawConnection,
OUT RPC_STATUS *Status
) : HTTP2SocketTransportChannel(RawConnection, Status)
{
}
virtual RPC_STATUS AsyncCompleteHelper (
IN RPC_STATUS CurrentStatus
);
};
typedef enum tagIISTransportChannelDirection
{
iistcdSend = 0,
iistcdReceive
} IISTransportChannelDirection;
// cannot have pending reads at the same time as pending writes
// Class must enforce that. Also, it must accept multiple
// sends at a time, but does one send in reality - all others
// are buffered pending completion of the first one. It has to
// provide defragmentation services for upper channels
class HTTP2IISTransportChannel : public HTTP2FragmentReceiver
{
public:
HTTP2IISTransportChannel (
IN void *ConnectionParameter
)
{
ControlBlock = (EXTENSION_CONTROL_BLOCK *)ConnectionParameter;
Direction = iistcdReceive; // all IIS channels start in receive mode
IISIoFlags = HSE_IO_ASYNC;
CurrentSendContext = NULL;
iPostSize = gPostSize;
ReadsPending = 0;
IISCloseEnabled = FALSE;
ECBDataConsumed = FALSE;
}
virtual RPC_STATUS Receive (
IN HTTP2TrafficType TrafficType
);
virtual RPC_STATUS ReceiveComplete (
IN RPC_STATUS EventStatus,
IN HTTP2TrafficType TrafficType,
IN BYTE *Buffer,
IN UINT BufferLength
);
virtual void Abort (
IN RPC_STATUS RpcStatus
);
virtual void FreeObject (
void
);
void IOCompleted (
IN ULONG Bytes,
DWORD Error
);
void DirectReceive (
void
);
inline void EnableIISSessionClose (
void
)
{
ASSERT(IISCloseEnabled == FALSE);
IISCloseEnabled = TRUE;
}
RPC_STATUS PostedEventStatus; // valid only on pick up from posts
protected:
virtual RPC_STATUS ReceiveCompleteInternal (
IN RPC_STATUS EventStatus,
IN HTTP2TrafficType TrafficType,
IN BOOL ReadCompleted,
IN OUT BYTE **Buffer,
IN OUT UINT *BufferLength
);
inline void ReverseDirection (
void
)
{
// direction can be reverted only once
ASSERT(Direction == iistcdReceive);
Direction = iistcdSend;
}
void FreeIISControlBlock (
void
);
virtual RPC_STATUS AsyncCompleteHelper (
IN RPC_STATUS CurrentStatus
);
virtual RPC_STATUS PostReceive (
void
);
virtual ULONG GetPostRuntimeEvent (
void
);
// the direction in which the channel is flowing
IISTransportChannelDirection Direction;
EXTENSION_CONTROL_BLOCK *ControlBlock;
ULONG BytesToTransfer; // valid only for the duration of a submission
// We never really use it, but we have to give
// some storage to IIS
DWORD IISIoFlags; // same thing here. Flags that we pass to IIS. They
// are always the same, but we have to have some
// storage to give to IIS.
HTTP2SendContext *CurrentSendContext; // the context we are currently
// sending. Set before we submit a write, and picked
// on write completion.
int ReadsPending; // the number of reads pending on the channel.
// Can be either 0 or 1. Not consumed in this class
// but derived classes make use of it to synchronize
// reads and writes
BOOL IISCloseEnabled; // if this flag is set to non-zero, IIS Close must be
// called on free object. Otherwise, IIS Close must
// not be called on free object. It starts as FALSE,
// and eventually becomes non-zero when we know
// we will not be returning a synchronous error to IIS.
// It never goes back to FALSE.
BOOL ECBDataConsumed; // if this flag is non-zero, the received data
// embedded in the ECB block have been consumed. If
// it is zero, receive must consume any data embedded
// in the ECB block
};
class HTTP2IISSenderTransportChannel : public HTTP2IISTransportChannel
{
public:
HTTP2IISSenderTransportChannel (
IN void *ConnectionParameter,
OUT RPC_STATUS *RpcStatus
) : HTTP2IISTransportChannel(ConnectionParameter),
Mutex(RpcStatus,
FALSE, // pre-allocate semaphore
5000 // spin count
), SendsPending(0)
{
RpcpInitializeListHead(&BufferQueueHead);
}
virtual RPC_STATUS Send (
IN OUT HTTP2SendContext *SendContext
);
virtual RPC_STATUS SendComplete (
IN RPC_STATUS EventStatus,
IN OUT HTTP2SendContext *SendContext
);
virtual void Abort (
IN RPC_STATUS RpcStatus
);
virtual void FreeObject (
void
);
protected:
virtual RPC_STATUS ReceiveCompleteInternal (
IN RPC_STATUS EventStatus,
IN HTTP2TrafficType TrafficType,
IN BOOL ReadCompleted,
IN OUT BYTE **Buffer,
IN OUT UINT *BufferLength
);
private:
RPC_STATUS SendQueuedContextIfNecessary (
IN ULONG LocalSendsPending,
IN RPC_STATUS EventStatus
);
LIST_ENTRY BufferQueueHead;
INTERLOCKED_INTEGER SendsPending;
MUTEX Mutex;
};
// base class for the Endpoint receiver (used at endpoints)
// and the proxy receiver - used at both proxies
class HTTP2GenericReceiver : public HTTP2TransportChannel
{
public:
HTTP2GenericReceiver (
IN ULONG ReceiveWindowSize,
IN BOOL IsServer,
OUT RPC_STATUS *Status
) : Mutex(Status)
{
this->IsServer = IsServer;
DirectCompletePosted = FALSE;
DirectReceiveInProgress = FALSE;
ReceiveWindow = ReceiveWindowSize;
BytesInWindow = 0;
BytesReceived = 0;
FreeWindowAdvertised = ReceiveWindowSize;
}
// inherit send from base class
// inherit receive from base class
// inherit send complete from base class
virtual RPC_STATUS ReceiveComplete (
IN RPC_STATUS EventStatus,
IN HTTP2TrafficType TrafficType,
IN BYTE *Buffer,
IN UINT BufferLength
) = 0;
virtual void FreeObject (
void
);
void TransferStateToNewReceiver (
OUT HTTP2GenericReceiver *NewReceiver
);
RPC_STATUS BytesReceivedNotification (
IN ULONG Bytes
);
void BytesConsumedNotification (
IN ULONG Bytes,
IN BOOL OwnsMutex,
OUT BOOL *IssueAck,
OUT ULONG *BytesReceivedForAck,
OUT ULONG *WindowForAck
);
protected:
RPC_STATUS SendFlowControlAck (
IN ULONG BytesReceivedForAck,
IN ULONG WindowForAck
);
// for endpoint receivers, contains
// only the type of packets not requested. The buffers in the queue are "not wanted" -
// nobody asked for them. They arrived when we were waiting for something else. As such
// they do not carry any reference counts and do not need to be propagated to anybody.
// If we abort (or about to abort) the connection, we can just drop them on the floor.
// N.B. Packets with first bit set are transfers from an old channel and should
// not be accounted for in flow control
QUEUE BufferQueue;
MUTEX Mutex;
// a flag that indicates whether a direct receive has been posted. Only
// one direct receive can be posted as a time.
BOOL DirectCompletePosted;
BOOL DirectReceiveInProgress;
ULONG BytesInWindow;
ULONG ReceiveWindow;
ULONG BytesReceived; // counter of total data bytes received. Wraps
LONG FreeWindowAdvertised; // the size of the free window as advertised. This
// may differ from the actual because we do not advertise
// on every change
BOOL IsServer;
};
// provides complex receive at the endpoints - client and server.
// It has two main responsibilities:
// 1. It receives two receives - RTS and Data. It must translate
// them into 1 raw receive
// 2. It manages flow control between a producer - the transport
// and the cosumer - the runtime
class HTTP2EndpointReceiver : public HTTP2GenericReceiver
{
public:
HTTP2EndpointReceiver (
IN ULONG ReceiveWindowSize,
IN BOOL IsServer,
OUT RPC_STATUS *Status
) : HTTP2GenericReceiver(ReceiveWindowSize,
IsServer,
Status)
{
ReceivesPosted = http2ttNone;
ReceivesQueued = http2ttNone;
}
// HTTP2EndpointReceiver inherits send from HTTP2TransportChannel
virtual RPC_STATUS Receive (
IN HTTP2TrafficType TrafficType
);
virtual RPC_STATUS ReceiveComplete (
IN RPC_STATUS EventStatus,
IN HTTP2TrafficType TrafficType,
IN BYTE *Buffer,
IN UINT BufferLength
);
virtual void Abort (
IN RPC_STATUS RpcStatus
);
virtual void FreeObject (
void
);
virtual RPC_STATUS DirectReceiveComplete (
OUT BYTE **ReceivedBuffer,
OUT ULONG *ReceivedBufferLength,
OUT void **RuntimeConnection,
OUT BOOL *IsServer
);
inline BOOL IsDataReceivePosted (
void
)
{
return (ReceivesPosted & http2ttData);
}
RPC_STATUS TransferStateToNewReceiver (
OUT HTTP2EndpointReceiver *NewReceiver
);
inline void BlockDataReceives (
void
)
{
Mutex.Request();
}
inline void UnblockDataReceives (
void
)
{
Mutex.Clear();
}
protected:
// map of posted receives. Can be any combination of http2ttRTS and http2ttData
// (including http2ttNone and http2ttAny). If a receive is posted, new receive
// request should be reflected on the map, but not really posted
HTTP2TrafficType ReceivesPosted;
// the type of receives in the queue. Can be http2ttRTS or http2ttData. If we
// have queued packets of a given type, this type cannot be in the map.
// Must be http2ttNone if we do not have any receives in the queue.
HTTP2TrafficType ReceivesQueued;
};
// Responsibility of this class is flow control between receiving leg
// of the proxy and sending leg of the proxies
class HTTP2ProxyReceiver : public HTTP2GenericReceiver
{
public:
HTTP2ProxyReceiver (
IN ULONG ReceiveWindowSize,
OUT RPC_STATUS *Status
) : HTTP2GenericReceiver(ReceiveWindowSize,
FALSE, // IsServer - for proxies it actually doesn't matter
Status
)
{
}
// HTTP2ProxyReceiver inherits send from HTTP2GenericReceiver
// HTTP2ProxyReceiver inherits receive from HTTP2GenericReceiver
virtual RPC_STATUS ReceiveComplete (
IN RPC_STATUS EventStatus,
IN HTTP2TrafficType TrafficType,
IN BYTE *Buffer,
IN UINT BufferLength
);
virtual void Abort (
IN RPC_STATUS RpcStatus
);
virtual void FreeObject (
void
);
};
// N.B. Only transitions from unplugged to one of the plugged states are
// allowed after data were sent. Transitions b/n plugged states are not allowed
// The enum members are ordered in strict correspondence to the HTTP2TrafficType
// enum. This allows to determine whether a traffic goes through with single
// comparison
typedef enum tagHTTP2PlugLevels
{
http2plInvalid,
http2plRTSPlugged, // plugged for data and RTS, unplugged for raw
http2plDataPlugged, // plugged for data, unplugged for RTS and raw
http2plUnplugged // unplugged
} HTTP2PlugLevels;
// a channel that can be plugged/unplugged. When plugged,
// it doesn't send anything - it just queues packets. When
// unplugged, it shoots the whole queue down and then
// starts transmitting packets normally
class HTTP2PlugChannel : public HTTP2TransportChannel
{
public:
HTTP2PlugChannel (
OUT RPC_STATUS *Status
) : Mutex(Status,
FALSE, // pre-allocate semaphore
5000 // spin count
)
{
RpcpInitializeListHead(&BufferQueueHead);
PlugLevel = http2plDataPlugged;
#if DBG
TrafficSentOnChannel = FALSE;
#endif // DBG
SendFailedStatus = RPC_S_INTERNAL_ERROR;
}
virtual RPC_STATUS Send (
IN OUT HTTP2SendContext *SendContext
);
virtual void Abort (
IN RPC_STATUS RpcStatus
);
virtual void FreeObject (
void
);
virtual void Reset (
void
);
void InsertHeadBuffer (
HTTP2SendContext *SendContext
);
RPC_STATUS DirectSendComplete (
void
);
RPC_STATUS Unplug (
void
);
void SetStrongPlug (
void
);
protected:
HTTP2PlugLevels PlugLevel;
LIST_ENTRY BufferQueueHead;
MUTEX Mutex;
RPC_STATUS SendFailedStatus;
#if DBG
BOOL TrafficSentOnChannel;
#endif // DBG
};
class HTTP2ProxyPlugChannel : public HTTP2PlugChannel
{
public:
HTTP2ProxyPlugChannel (
OUT RPC_STATUS *Status
) : HTTP2PlugChannel(Status)
{
}
protected:
virtual RPC_STATUS AsyncCompleteHelper (
IN RPC_STATUS CurrentStatus
);
};
class HTTP2FlowControlSender : public HTTP2TransportChannel
{
public:
HTTP2FlowControlSender (
IN BOOLEAN IsServer,
IN BOOLEAN SendToRuntime,
OUT RPC_STATUS *Status
) : Mutex(Status),
SendsPending(0)
{
SendContextOnDrain = NULL;
PeerReceiveWindow = 0;
RpcpInitializeListHead(&BufferQueueHead);
DataBytesSent = 0;
PeerAvailableWindow = 0;
this->IsServer = IsServer;
this->SendToRuntime = SendToRuntime;
AbortStatus = RPC_S_OK;
}
virtual RPC_STATUS Send (
IN OUT HTTP2SendContext *SendContext
);
// Receive is inheritted from HTTP2TransportChannel
virtual RPC_STATUS SendComplete (
IN RPC_STATUS EventStatus,
IN OUT HTTP2SendContext *SendContext
);
// ReceiveComplete is inherrited
virtual void Abort (
IN RPC_STATUS RpcStatus
);
inline void SetPeerReceiveWindow (
IN ULONG PeerReceiveWindow
)
{
// the peer receive window can be set
// only once. We don't support
// dynamic resizing
ASSERT(this->PeerReceiveWindow == 0);
this->PeerReceiveWindow = PeerReceiveWindow;
PeerAvailableWindow = PeerReceiveWindow;
}
inline ULONG GetPeerReceiveWindow (
void
)
{
return PeerReceiveWindow;
}
virtual void FreeObject (
void
);
virtual void SendCancelled (
IN HTTP2SendContext *SendContext
);
RPC_STATUS FlowControlAckNotify (
IN ULONG BytesReceivedForAck,
IN ULONG WindowForAck
);
void GetBufferQueue (
OUT LIST_ENTRY *NewQueueHead
);
RPC_STATUS DirectSendComplete (
OUT BOOL *IsServer,
OUT BOOL *SendToRuntime,
OUT void **SendContext,
OUT BUFFER *Buffer,
OUT UINT *BufferLength
);
private:
RPC_STATUS SendInternal (
IN OUT HTTP2SendContext *SendContext,
IN BOOL IgnoreQueuedBuffers
);
LIST_ENTRY BufferQueueHead;
MUTEX Mutex;
ULONG DataBytesSent;
ULONG PeerAvailableWindow;
ULONG PeerReceiveWindow;
HTTP2SendContext *SendContextOnDrain; // if armed, last send complete or arming
// function if no sends pending will send this packet
INTERLOCKED_INTEGER SendsPending;
RPC_STATUS AbortStatus;
BOOLEAN IsServer;
BOOLEAN SendToRuntime;
};
class HTTP2ChannelDataOriginator : public HTTP2TransportChannel
{
public:
HTTP2ChannelDataOriginator (
IN ULONG ChannelLifetime,
IN BOOL IsServer,
OUT RPC_STATUS *Status
);
virtual RPC_STATUS Send (
IN OUT HTTP2SendContext *SendContext
);
// HTTP2ChannelDataOriginator inherits receive from HTTP2TransportChannel
// SendComplete is inheritted
// ReceiveComplete is inheritted
virtual void Abort (
IN RPC_STATUS RpcStatus
);
virtual void FreeObject (
void
);
virtual void Reset (
void
);
void GetBufferQueue (
OUT LIST_ENTRY *NewQueueHead
);
RPC_STATUS DirectSendComplete (
OUT BOOL *IsServer,
OUT void **SendContext,
OUT BUFFER *Buffer,
OUT UINT *BufferLength
);
inline void PlugChannel (
void
)
{
// fake true channel exhaustion to force the channel
// to queue things instead of sending
if (BytesSentOnChannel < NonreservedLifetime)
BytesSentOnChannel = NonreservedLifetime + 1;
}
RPC_STATUS RestartChannel (
void
);
RPC_STATUS NotifyTrafficSent (
IN ULONG TrafficSentSize
);
protected:
#if DBG
inline void RawDataBeingSent (
void
)
{
ASSERT(RawDataAlreadySent == FALSE);
RawDataAlreadySent = TRUE;
}
#else // DBG
inline void RawDataBeingSent (
void
)
{
}
#endif // DBG
private:
ULONG BytesSentOnChannel;
ULONG ChannelLifetime;
ULONG NonreservedLifetime;
LIST_ENTRY BufferQueueHead;
MUTEX Mutex;
BOOL ChannelReplacementTriggered;
BOOL IsServer;
RPC_STATUS AbortStatus; // valid only between abort and direct send completes
#if DBG
BOOL RawDataAlreadySent;
#endif // DBG
};
typedef enum tagHTTP2ReceiveType
{
http2rtNone = 0,
http2rtRTS,
http2rtData,
http2rtAny
} HTTP2ReceiveType;
class HTTP2ReceiveContext
{
HTTP2ReceiveType ReceiveType;
BYTE *Buffer;
ULONG BufferLength;
LIST_ENTRY ListEntry;
};
const ULONG ThresholdConsecutivePingsOnInterval = 10; // after 10 pings we'll start scaling back
const ULONG PingScaleBackInterval = 30 * 1000; // 30 seconds in milliseconds
class HTTP2PingOriginator : public HTTP2TransportChannel
{
public:
HTTP2PingOriginator (
IN BOOL NotifyTopChannelForPings
)
{
LastPacketSentTimestamp = 0;
PingInterval = 0;
PingTimer = NULL;
KeepAliveInterval = 0;
ConnectionTimeout = 0;
this->NotifyTopChannelForPings = NotifyTopChannelForPings;
}
virtual RPC_STATUS Send (
IN OUT HTTP2SendContext *SendContext
);
// HTTP2PingOriginator inherits receive from HTTP2TransportChannel
virtual RPC_STATUS SendComplete (
IN RPC_STATUS EventStatus,
IN OUT HTTP2SendContext *SendContext
);
// ReceiveComplete is inheritted
virtual void Abort (
IN RPC_STATUS RpcStatus
);
RPC_STATUS SetKeepAliveTimeout (
IN BOOL TurnOn,
IN BOOL bProtectIO,
IN KEEPALIVE_TIMEOUT_UNITS Units,
IN OUT KEEPALIVE_TIMEOUT KATime,
IN ULONG KAInterval = 5000 OPTIONAL
);
virtual void FreeObject (
void
);
// travels up
virtual void SendCancelled (
IN HTTP2SendContext *SendContext
);
virtual void Reset (
void
);
RPC_STATUS SetConnectionTimeout (
IN ULONG ConnectionTimeout
);
void DisablePings (
void
);
void TimerCallback (
void
);
RPC_STATUS ReferenceFromCallback (
void
);
inline ULONG GetGracePeriod (
void
)
{
// currently the policy is that the grace period is 1/2
// of the ping interval
return (PingInterval >> 1);
}
inline ULONG GetPingIntervalFromConnectionTimeout (
IN ULONG ConnectionTimeout
)
{
return (ConnectionTimeout >> 1);
}
inline ULONG GetPingInterval (
IN ULONG ConnectionTimeout,
IN ULONG KeepAliveInterval
)
{
ULONG ConnectionTimeoutPing;
ULONG ActualPingInterval;
ConnectionTimeoutPing = GetPingIntervalFromConnectionTimeout(ConnectionTimeout);
ASSERT(ConnectionTimeoutPing || KeepAliveInterval);
if (KeepAliveInterval)
{
if (ConnectionTimeoutPing)
ActualPingInterval = min(ConnectionTimeoutPing, KeepAliveInterval);
else
ActualPingInterval = KeepAliveInterval;
}
else
ActualPingInterval = ConnectionTimeoutPing;
return ActualPingInterval;
}
inline ULONG ScaleBackPingInterval (
void
)
{
ULONG LocalPingInterval;
LocalPingInterval = PingInterval + PingScaleBackInterval;
if (LocalPingInterval > GetPingIntervalFromConnectionTimeout(ConnectionTimeout))
LocalPingInterval = GetPingIntervalFromConnectionTimeout(ConnectionTimeout);
return LocalPingInterval;
}
RPC_STATUS SetNewPingInterval (
IN ULONG NewPingInterval
);
void RescheduleTimer (
void
);
private:
void DisablePingsInternal (
void
);
RPC_STATUS SendPingPacket (
void
);
RPC_STATUS SendInternal (
IN OUT HTTP2SendContext *SendContext
);
ULONG LastPacketSentTimestamp; // the timestamp of the last sent packet
ULONG PingInterval; // the ping interval
HANDLE PingTimer; // the handle for the periodic
// timer. If no timer is set, this is
// NULL
ULONG KeepAliveInterval; // current keep alive interval
ULONG ConnectionTimeout; // current connection timeout
ULONG ConsecutivePingsOnInterval; // how many consecutive pings did we
// see on this channel without other
// activities. If too many, we will
// scale back on the pings
BOOL NotifyTopChannelForPings;
};
class HTTP2PingReceiver : public HTTP2TransportChannel
{
public:
HTTP2PingReceiver::HTTP2PingReceiver (
IN BOOL PostAnotherReceive
)
{
this->PostAnotherReceive = PostAnotherReceive;
}
// Send is inheritted
// HTTP2PingReceiver inherits receive from HTTP2TransportChannel
// Send complete is inheritted
virtual RPC_STATUS ReceiveComplete (
IN RPC_STATUS EventStatus,
IN HTTP2TrafficType TrafficType,
IN BYTE *Buffer,
IN UINT BufferLength
);
// Abort is inherrited
virtual void FreeObject (
void
);
protected:
BOOL PostAnotherReceive;
};
typedef struct tagReceiveOverlapped
{
ULONG_PTR Internal; // same layout as stock overlapped
BYTE *Buffer;
ULONG BufferLength;
ULONG IOCompleted;
HANDLE hEvent;
} ReceiveOverlapped;
C_ASSERT(FIELD_OFFSET(ReceiveOverlapped, hEvent) == FIELD_OFFSET(OVERLAPPED, hEvent));
C_ASSERT(FIELD_OFFSET(ReceiveOverlapped, IOCompleted) == FIELD_OFFSET(OVERLAPPED, OffsetHigh));
// Channel checks the event in the request, and if
// set, fires it and doesn't forward any further. It also strips
// the receive event off receives. It still allows both async
// and sync requests down
class HTTP2Channel : public HTTP2TransportChannel
{
public:
HTTP2Channel (
IN int InitialFlag,
OUT RPC_STATUS *Status
) : RefCount (1),
Aborted (0),
SubmitAsyncStarted (0),
ParentPointerLockCount(0)
{
AbortReason = RPC_S_OK;
ChannelId = 0;
Flags.SetFlagUnsafe(InitialFlag);
}
virtual RPC_STATUS Send (
IN OUT HTTP2SendContext *SendContext
);
virtual RPC_STATUS Receive (
IN HTTP2TrafficType TrafficType
);
virtual RPC_STATUS SendComplete (
IN RPC_STATUS EventStatus,
IN OUT HTTP2SendContext *SendContext
);
virtual RPC_STATUS ReceiveComplete (
IN RPC_STATUS EventStatus,
IN HTTP2TrafficType TrafficType,
IN BYTE *Buffer,
IN UINT BufferLength
);
// aborts the channel. Called from above or in neutral context
// only. Calling it from submit context will cause deadlock
virtual void Abort (
IN RPC_STATUS RpcStatus
);
void PrepareForSyncSend (
IN ULONG BufferLength,
IN BYTE *Buffer,
IN OUT HTTP2SendContext *SendContext
);
virtual RPC_STATUS SyncSend (
IN HTTP2TrafficType TrafficType,
IN ULONG BufferLength,
IN BYTE *Buffer,
IN BOOL fDisableCancelCheck,
IN ULONG Timeout,
IN BASE_ASYNC_OBJECT *Connection,
IN HTTP2SendContext *SendContext
);
// on receiving channels forwards traffic on
// the sending channel. On sending channels it gets
// sent down. The default implementation is
// for sending channels
virtual RPC_STATUS ForwardTraffic (
IN BYTE *Packet,
IN ULONG PacketLength
);
virtual RPC_STATUS ForwardFlowControlAck (
IN ULONG BytesReceivedForAck,
IN ULONG WindowForAck
);
virtual RPC_STATUS AsyncCompleteHelper (
IN RPC_STATUS CurrentStatus
);
virtual RPC_STATUS SetKeepAliveTimeout (
IN BOOL TurnOn,
IN BOOL bProtectIO,
IN KEEPALIVE_TIMEOUT_UNITS Units,
IN OUT KEEPALIVE_TIMEOUT KATime,
IN ULONG KAInterval = 5000 OPTIONAL
);
// some top channels provide notification
// mechanism for channels below them to
// notify them when the last packet is sent.
// Some channels don't support this and will ASSERT
virtual RPC_STATUS LastPacketSentNotification (
IN HTTP2SendContext *LastSendContext
);
// travels up
virtual void SendCancelled (
IN HTTP2SendContext *SendContext
);
virtual void PingTrafficSentNotify (
IN ULONG PingTrafficSize
);
void AddReference (
void
)
{
int Count;
// make sure we don't just make up refcounts
// out of thin air
ASSERT(RefCount.GetInteger() > 0);
Count = RefCount.Increment();
LogEvent(SU_REFOBJ, EV_INC, this, IntToPtr(ObjectType), Count, 1, 1);
}
void RemoveReference (
void
)
{
int Count;
LogEvent(SU_REFOBJ, EV_DEC, this, IntToPtr(ObjectType), RefCount.GetInteger(), 1, 1);
Count = RefCount.Decrement();
ASSERT(Count >= 0);
if (0 == Count)
{
FreeObject();
}
}
int GetReferenceCount ( // used only for debugging - never for code logic
void
)
{
return RefCount.GetInteger();
}
virtual void FreeObject (
void
);
// if it return FALSE, an abort has already been issued by somebody
// and there is no need to abort. If it returns TRUE, abort may proceed
BOOL InitiateAbort (
void
)
{
if (Aborted.Increment() > 1)
{
return FALSE;
}
LogEvent(SU_TRANS_CONN, EV_ABORT, this, 0, ObjectType, 1, 2);
while (SubmitAsyncStarted.GetInteger() > 0)
{
Sleep(1);
}
return TRUE;
}
// provides submission context (i.e. abort synchronization)
// without adding a refcount
RPC_STATUS BeginSimpleSubmitAsync (
void
)
{
int Count;
Count = SubmitAsyncStarted.Increment();
LogEvent(SU_HTTPv2, EV_INC, this, IntToPtr(ObjectType), Count, 1, 1);
if (Aborted.GetInteger() > 0)
{
LogEvent(SU_HTTPv2, EV_DEC, this, IntToPtr(ObjectType), Count, 1, 1);
SubmitAsyncStarted.Decrement();
return GetAbortReason();
}
return RPC_S_OK;
}
// if it returns error, the channel is aborted and the operation
// can be considered failed. If it returns RPC_S_OK, FinishSubmitAsync
// must be called when the submission is done, and RemoveReference
// when the async operation is done.
RPC_STATUS BeginSubmitAsync (
void
)
{
int Count;
Count = SubmitAsyncStarted.Increment();
LogEvent(SU_HTTPv2, EV_INC, this, IntToPtr(ObjectType), Count, 1, 1);
if (Aborted.GetInteger() > 0)
{
LogEvent(SU_HTTPv2, EV_DEC, this, IntToPtr(ObjectType), Count, 1, 1);
SubmitAsyncStarted.Decrement();
ASSERT(GetAbortReason() != RPC_S_OK);
return GetAbortReason();
}
AddReference();
return RPC_S_OK;
}
// Same as BeginSubmitAsync except that this API ignores pending aborts.
// It is used in cases where we submit an async send on a plugged channel
// and want the send to be queued even if the channel has an abort pending.
void BeginSubmitAsyncNonFailing (
void
)
{
int Count;
Count = SubmitAsyncStarted.Increment();
LogEvent(SU_HTTPv2, EV_INC, this, IntToPtr(ObjectType), Count, 1, 1);
AddReference();
}
void FinishSubmitAsync (
void
)
{
LogEvent(SU_HTTPv2, EV_DEC, this, IntToPtr(ObjectType), SubmitAsyncStarted.GetInteger(), 1, 1);
SubmitAsyncStarted.Decrement();
}
inline void VerifyAborted (
void
)
{
ASSERT(Aborted.GetInteger() > 0);
}
void SetParent (
IN HTTP2VirtualConnection *NewParent
)
{
Parent = NewParent;
}
void *GetRuntimeConnection (
void
)
{
// N.B. In some cases during channel recycling,
// the parent can be NULL (if the channel was
// detached before it was aborted).
return (void *)Parent;
}
void SetChannelId (
int NewChannelId
)
{
ASSERT(ChannelId == 0);
ASSERT(NewChannelId != 0);
ChannelId = NewChannelId;
}
HTTP2VirtualConnection *LockParentPointer (
void
)
{
HTTP2VirtualConnection *LocalParent;
ParentPointerLockCount.Increment();
LocalParent = (HTTP2VirtualConnection *)Parent;
if (LocalParent == NULL)
{
ParentPointerLockCount.Decrement();
}
return LocalParent; // may be NULL - that's ok
}
void UnlockParentPointer (
void
)
{
ASSERT(ParentPointerLockCount.GetInteger() > 0);
ParentPointerLockCount.Decrement();
}
inline void DrainUpcallsAndFreeParent (
void
)
{
DrainUpcallsAndFreeParentInternal (0);
}
inline void DrainUpcallsAndFreeParentFromUpcall (
void
)
{
DrainUpcallsAndFreeParentInternal (1);
}
void DrainPendingSubmissions (
void
)
{
#if DBG
int Retries = 0;
#endif
while (SubmitAsyncStarted.GetInteger() > 0)
{
Sleep(2);
#if DBG
Retries ++;
if (Retries > 100000)
{
ASSERT(!"Cannot drain pending submissions on channel");
Retries = 0;
}
#endif
}
}
inline void SetAbortReason (
RPC_STATUS RpcStatus
)
{
if (AbortReason == RPC_S_OK)
AbortReason = RpcStatus;
}
inline RPC_STATUS GetAbortReason (
void
)
{
ASSERT(Aborted.GetInteger() > 0);
if (AbortReason == RPC_S_OK)
return RPC_P_CONNECTION_CLOSED;
else
return AbortReason;
}
virtual void AbortConnection (
IN RPC_STATUS AbortReason
);
void AbortAndDestroyConnection (
IN RPC_STATUS AbortStatus
);
RPC_STATUS HandleSendResultFromNeutralContext (
IN RPC_STATUS CurrentStatus
);
RPC_STATUS IsInChannel (
OUT BOOL *InChannel
);
BOOL IsProxyChannel (
void
)
{
return Flags.GetFlag(ProxyChannelType);
}
protected:
BOOL SetDeletedFlag(
void
)
{
int SavedObjectType = ObjectType;
if (((SavedObjectType & OBJECT_DELETED) == 0) &&
InterlockedCompareExchange((long *)&ObjectType, SavedObjectType | OBJECT_DELETED, SavedObjectType) == SavedObjectType)
{
LogEvent(SU_REFOBJ, EV_DELETE, this, IntToPtr(SavedObjectType), RefCount.GetInteger(), 1, 1);
return TRUE;
}
return FALSE;
}
void DrainUpcallsAndFreeParentInternal (
IN int UpcallsToLeave
)
{
#if DBG
int Retries = 0;
#endif
LogEvent(SU_HTTPv2, EV_SET, &Parent, (void *)Parent, 0, 1, 0);
Parent = NULL;
while (ParentPointerLockCount.GetInteger() > UpcallsToLeave)
{
Sleep(2);
#if DBG
Retries ++;
if (Retries > 100000)
{
ASSERT(!"Cannot drain upcalls on channel");
Retries = 0;
}
#endif
}
}
RPC_STATUS CheckSendCompleteForSync (
IN RPC_STATUS EventStatus,
IN OUT HTTP2SendContext *SendContext
);
RPC_STATUS ForwardUpSendComplete (
IN RPC_STATUS EventStatus,
IN OUT HTTP2SendContext *SendContext
);
virtual RPC_STATUS CheckReceiveCompleteForSync (
IN RPC_STATUS EventStatus,
IN HTTP2TrafficType TrafficType,
IN BYTE *Buffer,
IN UINT BufferLength
);
RPC_STATUS ForwardUpReceiveComplete (
IN RPC_STATUS EventStatus,
IN BYTE *Buffer,
IN UINT BufferLength
);
HANDLE_TYPE ObjectType;
// the channel always has one lifetime reference +
// 1 reference for all async operations (sends,
// receives, timers)
INTERLOCKED_INTEGER RefCount;
INTERLOCKED_INTEGER Aborted;
INTERLOCKED_INTEGER SubmitAsyncStarted;
volatile HTTP2VirtualConnection *Parent;
INTERLOCKED_INTEGER ParentPointerLockCount;
static const ProxyChannelType = 1;
// can be ProxyChannelType
CompositeFlags Flags;
RPC_STATUS AbortReason;
int ChannelId; // opaque number set by the parent that the channel reports back
// on complete events
RPC_STATUS ForwardFlowControlAckOnDefaultChannel (
IN BOOL IsInChannel,
IN ForwardDestinations Destination,
IN ULONG BytesReceivedForAck,
IN ULONG WindowForAck
);
RPC_STATUS ForwardFlowControlAckOnThisChannel (
IN ULONG BytesReceivedForAck,
IN ULONG WindowForAck,
IN BOOL NonChannelData
);
};
typedef enum tagHTTP2StateValues
{
http2svInvalid = 0,
http2svClosed, // 1
http2svOpened, // 2
http2svA3W, // 3
http2svC2W, // 4
http2svOpened_A6W, // 5
http2svOpened_A10W, // 6
http2svOpened_A5W, // 7
http2svB2W, // 8
http2svB3W, // 9
http2svC1W, // 10
http2svOpened_CliW, // 11
http2svOpened_A9W, // 12
http2svA11W, // 13
http2svOpened_B1W, // 14
http2svA2W, // 15
http2svOpened_A4W, // 16
http2svOpened_A8W, // 17
http2svOpened_B3W, // 18
http2svOpened_D5A8W, // 19
http2svSearchProxy, // 20
http2svNonDefaultChannelCloseWait // 21
} HTTP2StateValues;
class HTTP2State
{
public:
HTTP2State (OUT RPC_STATUS *RpcStatus)
: Mutex(RpcStatus)
{
State = http2svClosed;
}
MUTEX Mutex;
HTTP2StateValues State;
};
// The maximum number of concurent locks on HTTP2ChannelPointer.
// When we free HTTP2ChannelPointer, we mark that it is freed by setting
// ChannelPointerLockCount to -(MAX_CHANNEL_POINTER_LOCKS+1). This way
// ChannelPointerLockCount will remain below zero even if MAX_CHANNEL_POINTER_LOCKS
// threads attempt to lock the channel pointer simultaneously.
#define MAX_CHANNEL_POINTER_LOCKS (100)
class HTTP2ChannelPointer
{
public:
HTTP2ChannelPointer (
void
) : ChannelPointerLockCount(-1*(MAX_CHANNEL_POINTER_LOCKS+1)) // The channel pointer starts of not set.
{
Channel = NULL;
}
HTTP2Channel *LockChannelPointer (
void
)
{
int Count;
LogEvent(SU_HTTPv2, EV_INC, this, Channel, ChannelPointerLockCount.GetInteger(), 1, 1);
Count = ChannelPointerLockCount.Increment();
// If Count is below zero, the channel is freed and not set. It can't be locked.
if (Count < 0)
{
LogEvent(SU_HTTPv2, EV_DEC, this, Channel, Count, 1, 1);
ChannelPointerLockCount.Decrement();
return NULL;
}
return Channel;
}
void UnlockChannelPointer (
void
)
{
LogEvent(SU_HTTPv2, EV_DEC, this, Channel, ChannelPointerLockCount.GetInteger(), 1, 1);
// The channel pointer must have been locked.
ASSERT(ChannelPointerLockCount.GetInteger() > 0);
ChannelPointerLockCount.Decrement();
}
void FreeChannelPointer (
IN BOOL DrainUpCalls,
IN BOOL CalledFromUpcallContext,
IN BOOL Abort,
IN RPC_STATUS AbortStatus
)
{
HTTP2Channel *LocalChannel;
LogEvent(SU_HTTPv2, EV_DELETE, this, Channel, ChannelPointerLockCount.GetInteger(), 1, 1);
// verify input parameters
if (DrainUpCalls == FALSE)
{
ASSERT(CalledFromUpcallContext == FALSE);
}
if (Abort == FALSE)
{
ASSERT(AbortStatus == RPC_S_OK);
}
LocalChannel = Channel;
while (TRUE)
{
// Wait until the channel pointer becomes unlocked and then free it.
// To do this we wait until ChannelPointerLockCount drops to 0.
// and atomically set it to a negative number. ChannelPointerLockCount<0 signals
// a freed channel to the others.
if (ChannelPointerLockCount.CompareExchange(-1*(MAX_CHANNEL_POINTER_LOCKS+1), 0) == 0)
break;
// It is possible that the channel pointer
// is freed from two places. E.g. client freeing
// old channel while being aborted.
// If the ChannelPointer is already freed, we will bail out.
if (ChannelPointerLockCount.GetInteger() < 0)
return;
Sleep(2);
}
if (LocalChannel)
{
if (DrainUpCalls)
{
// make sure all calls to us have been drained
if (CalledFromUpcallContext)
LocalChannel->DrainUpcallsAndFreeParentFromUpcall();
else
LocalChannel->DrainUpcallsAndFreeParent();
}
if (Abort)
LocalChannel->Abort(AbortStatus);
// we're ready to detach - remove child lifetime reference
LocalChannel->RemoveReference();
}
}
inline void SetChannel (
HTTP2Channel *NewChannel
)
{
LogEvent(SU_HTTPv2, EV_SET, this, Channel, ChannelPointerLockCount.GetInteger(), 1, 1);
// The channel must have been in the freed state originally.
ASSERT(ChannelPointerLockCount.GetInteger() < 0);
// Initialize Channel before we adjust ChannelPointerLockCount.
// ChannelPointerLockCount>=0 implies a set channel to other threads.
Channel = NewChannel;
// Mark the channel as set.
ChannelPointerLockCount.Exchange(0);
}
inline BOOL IsChannelSet (
void
)
{
return (ChannelPointerLockCount.GetInteger() >= 0);
}
void DrainPendingLocks (
IN ULONG LocksToLeave
)
{
#if DBG
int Retries = 0;
#endif
while (ChannelPointerLockCount.GetInteger() > (long)LocksToLeave)
{
Sleep(2);
#if DBG
Retries ++;
if (Retries > 100000)
{
ASSERT(!"Cannot drain pending locks on channel");
Retries = 0;
}
#endif
}
}
private:
// This is the interlocked integer that is used for all syncronization needs as follows:
// - ChannelPointerLockCount == 0 - allocated/set and unlocked
// - ChannelPointerLockCount > 0 - locked
// - ChannelPointerLockCount < 0 - freed and not set
//
// ========== ============= ==========
// | Freed | -- SetChannel --> | Allocated | -- LockCP ---> | Locked |
// | lock<0 | <- FreeCP ------- | lock==0 | <- UnlockCP -- | lock>0 |
// ========== ============= ==========
//
INTERLOCKED_INTEGER ChannelPointerLockCount;
HTTP2Channel *Channel;
};
// the only members of BASE_ASYNC_OBJECT used are type and id. Think
// of better way to do this than wholesale inheritance
class HTTP2VirtualConnection : public BASE_ASYNC_OBJECT
{
public:
HTTP2VirtualConnection (
void
) : Aborted(0)
{
InChannelIds[0] = InChannelIds[1] = 0;
OutChannelIds[0] = OutChannelIds[1] = 0;
DefaultLoopbackChannelSelector = 0;
}
virtual ~HTTP2VirtualConnection (
void
)
{
}
virtual RPC_STATUS Send (
IN UINT Length,
IN BUFFER Buffer,
IN PVOID SendContext
);
virtual RPC_STATUS Receive (
void
);
virtual RPC_STATUS SendComplete (
IN RPC_STATUS EventStatus,
IN OUT HTTP2SendContext *SendContext,
IN int ChannelId
) = 0;
virtual RPC_STATUS ReceiveComplete (
IN RPC_STATUS EventStatus,
IN BYTE *Buffer,
IN UINT BufferLength,
IN int ChannelId
) = 0;
virtual RPC_STATUS SyncSend (
IN ULONG BufferLength,
IN BYTE *Buffer,
IN BOOL fDisableShutdownCheck,
IN BOOL fDisableCancelCheck,
IN ULONG Timeout
);
virtual RPC_STATUS SyncRecv (
IN BYTE **Buffer,
IN ULONG *BufferLength,
IN ULONG Timeout
);
virtual void Abort (
void
) = 0;
virtual void Close (
IN BOOL DontFlush
);
virtual RPC_STATUS TurnOnOffKeepAlives (
IN BOOL TurnOn,
IN BOOL bProtectIO,
IN BOOL IsFromUpcall,
IN KEEPALIVE_TIMEOUT_UNITS Units,
IN OUT KEEPALIVE_TIMEOUT KATime,
IN ULONG KAInterval = 5000 OPTIONAL
);
virtual RPC_STATUS QueryClientAddress (
OUT RPC_CHAR **pNetworkAddress
);
virtual RPC_STATUS QueryLocalAddress (
IN OUT void *Buffer,
IN OUT unsigned long *BufferSize,
OUT unsigned long *AddressFormat
);
virtual RPC_STATUS QueryClientId(
OUT RPC_CLIENT_PROCESS_IDENTIFIER *ClientProcess
);
virtual RPC_STATUS QueryClientIpAddress (
IN OUT RPC_CLIENT_IP_ADDRESS *ClientIpAddress
);
void AbortChannels (
IN RPC_STATUS RpcStatus
);
BOOL AbortAndDestroy (
IN BOOL IsFromChannel,
IN int CallingChannelId,
IN RPC_STATUS AbortStatus
);
virtual void LastPacketSentNotification (
IN int ChannelId,
IN HTTP2SendContext *LastSendContext
);
inline HTTP2Channel *LockDefaultInChannel (
OUT HTTP2ChannelPointer **ChannelPointer
)
{
return LockDefaultChannel (&DefaultInChannelSelector,
InChannels, ChannelPointer);
}
inline HTTP2Channel *LockDefaultOutChannel (
OUT HTTP2ChannelPointer **ChannelPointer
)
{
return LockDefaultChannel (&DefaultOutChannelSelector,
OutChannels, ChannelPointer);
}
RPC_STATUS PostReceiveOnChannel (
IN HTTP2ChannelPointer *ChannelPtr,
IN HTTP2TrafficType TrafficType
);
RPC_STATUS PostReceiveOnDefaultChannel (
IN BOOL IsInChannel,
IN HTTP2TrafficType TrafficType
);
RPC_STATUS ForwardTrafficToChannel (
IN HTTP2ChannelPointer *ChannelPtr,
IN BYTE *Packet,
IN ULONG PacketLength
);
RPC_STATUS ForwardTrafficToDefaultChannel (
IN BOOL IsInChannel,
IN BYTE *Packet,
IN ULONG PacketLength
);
RPC_STATUS SendTrafficOnChannel (
IN HTTP2ChannelPointer *ChannelPtr,
IN HTTP2SendContext *SendContext
);
RPC_STATUS SendTrafficOnDefaultChannel (
IN BOOL IsInChannel,
IN HTTP2SendContext *SendContext
);
virtual RPC_STATUS RecycleChannel (
IN BOOL IsFromUpcall
);
RPC_STATUS StartChannelRecyclingIfNecessary (
IN RPC_STATUS RpcStatus,
IN BOOL IsFromUpcall
);
// N.B.: The pointer to the cookie returned
// is safe only while the calling channel
// has a lock on its parent
inline HTTP2Cookie *MapChannelIdToCookie (
IN int ChannelId
)
{
if (ChannelId == InChannelIds[0])
{
return &InChannelCookies[0];
}
else if (ChannelId == InChannelIds[1])
{
return &InChannelCookies[1];
}
else if (ChannelId == OutChannelIds[0])
{
return &OutChannelCookies[0];
}
else if (ChannelId == OutChannelIds[1])
{
return &OutChannelCookies[1];
}
else
{
ASSERT(0);
return NULL;
}
}
HTTP2Channel *MapCookieToChannelPointer (
IN HTTP2Cookie *ChannelCookie,
OUT HTTP2ChannelPointer **ChannelPtr
);
HTTP2Channel *MapCookieToAnyChannelPointer (
IN HTTP2Cookie *ChannelCookie,
OUT HTTP2ChannelPointer **ChannelPtr
);
inline BOOL IsInChannel (
IN int ChannelId
)
{
return ((ChannelId == InChannelIds[0])
|| (ChannelId == InChannelIds[1]));
}
inline BOOL IsInChannel (
IN HTTP2ChannelPointer *ChannelPtr
)
{
return ((ChannelPtr == &InChannels[0])
|| (ChannelPtr == &InChannels[1]));
}
inline int GetDefaultInChannelId (
void
)
{
return InChannelIds[DefaultInChannelSelector];
}
inline BOOL IsDefaultInChannel (
IN int ChannelId
)
{
return (ChannelId == GetDefaultInChannelId());
}
inline BOOL IsOutChannel (
IN int ChannelId
)
{
return ((ChannelId == OutChannelIds[0])
|| (ChannelId == OutChannelIds[1]));
}
inline BOOL IsOutChannel (
IN HTTP2ChannelPointer *ChannelPtr
)
{
return ((ChannelPtr == &OutChannels[0])
|| (ChannelPtr == &OutChannels[1]));
}
inline int GetDefaultOutChannelId (
void
)
{
return OutChannelIds[DefaultOutChannelSelector];
}
inline BOOL IsDefaultOutChannel (
IN int ChannelId
)
{
return (ChannelId == GetDefaultOutChannelId());
}
inline BOOL IsAborted (
void
)
{
return (Aborted.GetInteger() > 0);
}
#if DBG
inline void VerifyValidChannelId (
IN int ChannelId
)
{
ASSERT(IsInChannel(ChannelId) || IsOutChannel(ChannelId));
}
#else
inline void VerifyValidChannelId (
IN int ChannelId
)
{
}
#endif
protected:
inline int GetNonDefaultInChannelSelector (
void
)
{
return (DefaultInChannelSelector ^ 1);
}
inline int GetNonDefaultOutChannelSelector (
void
)
{
// return 0 to 1 and 1 to 0
return (DefaultOutChannelSelector ^ 1);
}
inline void SwitchDefaultInChannelSelector (
void
)
{
DefaultInChannelSelector = DefaultInChannelSelector ^ 1;
}
inline void SwitchDefaultOutChannelSelector (
void
)
{
DefaultOutChannelSelector = DefaultOutChannelSelector ^ 1;
}
inline void SwitchDefaultLoopbackChannelSelector (
void
)
{
DefaultLoopbackChannelSelector = DefaultLoopbackChannelSelector ^ 1;
}
inline int CompareCookieWithDefaultInChannelCookie (
IN HTTP2Cookie *OtherCookie
)
{
return InChannelCookies[DefaultInChannelSelector].Compare(OtherCookie);
}
inline int CompareCookieWithDefaultOutChannelCookie (
IN HTTP2Cookie *OtherCookie
)
{
return OutChannelCookies[DefaultOutChannelSelector].Compare(OtherCookie);
}
inline int AllocateChannelId (
void
)
{
return InterlockedIncrement(&ChannelIdCounter);
}
inline HTTP2ChannelPointer *GetChannelPointerFromId (
IN int ChannelId
)
{
if (ChannelId == InChannelIds[0])
{
return &InChannels[0];
}
else if (ChannelId == InChannelIds[1])
{
return &InChannels[1];
}
else if (ChannelId == OutChannelIds[0])
{
return &OutChannels[0];
}
else if (ChannelId == OutChannelIds[1])
{
return &OutChannels[1];
}
else
{
ASSERT(0);
return NULL;
}
}
virtual HTTP2Channel *LockDefaultSendChannel (
OUT HTTP2ChannelPointer **ChannelPtr
);
virtual HTTP2Channel *LockDefaultReceiveChannel (
OUT HTTP2ChannelPointer **ChannelPtr
);
void SetFirstInChannel (
IN HTTP2Channel *NewChannel
);
void SetFirstOutChannel (
IN HTTP2Channel *NewChannel
);
void SetNonDefaultInChannel (
IN HTTP2Channel *NewChannel
);
void SetNonDefaultOutChannel (
IN HTTP2Channel *NewChannel
);
INTERLOCKED_INTEGER Aborted;
HTTP2ServerCookie EmbeddedConnectionCookie;
HTTP2ChannelPointer InChannels[2];
int InChannelIds[2];
HTTP2Cookie InChannelCookies[2];
volatile int DefaultInChannelSelector;
HTTP2ChannelPointer OutChannels[2];
int OutChannelIds[2];
HTTP2Cookie OutChannelCookies[2];
volatile int DefaultOutChannelSelector;
volatile int DefaultLoopbackChannelSelector; // selector to get
// target of loopback flow control traffic. Used to select an in channel
// on the client and an out channel on the server
virtual void DisconnectChannels (
IN BOOL ExemptChannel,
IN int ExemptChannelId
);
private:
HTTP2Channel *LockDefaultChannel (
IN volatile int *DefaultChannelSelector,
IN HTTP2ChannelPointer ChannelSet[],
OUT HTTP2ChannelPointer **ChannelPointer
)
{
int LocalDefaultChannelSelector;
HTTP2Channel *Channel;
// there is a small race condition we need to take care of
// By the time we grab the default channel with the
// selector, it could have changed and we could be referencing
// a non-default channel. We know the object will be there, so
// there is no danger of AV. It just may be the wrong channel
do
{
LocalDefaultChannelSelector = *DefaultChannelSelector;
Channel = ChannelSet[LocalDefaultChannelSelector].LockChannelPointer();
// channel is locked. Check whether we got what we wanted
if (LocalDefaultChannelSelector == *DefaultChannelSelector)
break;
if (Channel)
{
// the selector changed - unlock and loop around
ChannelSet[LocalDefaultChannelSelector].UnlockChannelPointer();
}
}
while (TRUE);
*ChannelPointer = &ChannelSet[LocalDefaultChannelSelector];
return (HTTP2Channel *) Channel;
}
};
class HTTP2ClientVirtualConnection; // forward
class HTTP2ClientChannel : public HTTP2Channel
{
public:
HTTP2ClientChannel (
OUT RPC_STATUS *Status
) : HTTP2Channel (0, // InitialFlag
Status)
{
RpcpMemorySet(&Ol, 0, sizeof(Ol));
}
RPC_STATUS ClientOpen (
IN HTTPResolverHint *Hint,
IN const char *Verb,
IN int VerbLength,
IN BOOL InChannel,
IN BOOL ReplacementChannel,
IN BOOL UseWinHttp,
IN RPC_HTTP_TRANSPORT_CREDENTIALS_W *HttpCredentials, OPTIONAL
IN ULONG ChosenAuthScheme,OPTIONAL
IN HTTP2WinHttpTransportChannel *WinHttpChannel, OPTIONAL
IN ULONG CallTimeout,
IN const BYTE *AdditionalData, OPTIONAL
IN ULONG AdditionalDataLength OPTIONAL
);
virtual RPC_STATUS SendComplete (
IN RPC_STATUS EventStatus,
IN OUT HTTP2SendContext *SendContext
);
virtual RPC_STATUS CheckReceiveCompleteForSync (
IN RPC_STATUS EventStatus,
IN HTTP2TrafficType TrafficType,
IN BYTE *Buffer,
IN UINT BufferLength
);
HTTP2ClientVirtualConnection *LockParentPointer (
void
)
{
return (HTTP2ClientVirtualConnection *)HTTP2Channel::LockParentPointer();
}
void WaitInfiniteForSyncReceive (
void
);
inline BOOL IsSyncRecvPending (
void
)
{
return (Ol.ReceiveOverlapped.hEvent != NULL);
}
inline void RemoveEvent (
void
)
{
ASSERT(Ol.ReceiveOverlapped.hEvent != NULL);
Ol.ReceiveOverlapped.hEvent = NULL;
}
RPC_STATUS SubmitSyncRecv (
IN HTTP2TrafficType TrafficType
);
RPC_STATUS WaitForSyncRecv (
IN BYTE **Buffer,
IN ULONG *BufferLength,
IN ULONG Timeout,
IN ULONG ConnectionTimeout,
IN BASE_ASYNC_OBJECT *Connection,
OUT BOOL *AbortNeeded,
OUT BOOL *IoPending
);
virtual void AbortConnection (
IN RPC_STATUS AbortReason
);
protected:
union
{
OVERLAPPED Overlapped;
ReceiveOverlapped ReceiveOverlapped;
} Ol;
};
class HTTP2ClientInChannel : public HTTP2ClientChannel
{
public:
HTTP2ClientInChannel (
IN HTTP2ClientVirtualConnection *ClientVirtualConnection,
OUT RPC_STATUS *RpcStatus
) : HTTP2ClientChannel (RpcStatus)
{
SetParent((HTTP2VirtualConnection *)ClientVirtualConnection);
}
RPC_STATUS ClientOpen (
IN HTTPResolverHint *Hint,
IN const char *Verb,
IN int VerbLength,
IN BOOL UseWinHttp,
IN RPC_HTTP_TRANSPORT_CREDENTIALS_W *HttpCredentials,
IN ULONG ChosenAuthScheme,
IN ULONG CallTimeout,
IN const BYTE *AdditionalData, OPTIONAL
IN ULONG AdditionalDataLength OPTIONAL
);
inline RPC_STATUS Unplug (
void
)
{
HTTP2PlugChannel *PlugChannel;
PlugChannel = GetPlugChannel();
return PlugChannel->Unplug();
}
inline void GetChannelOriginatorBufferQueue (
OUT LIST_ENTRY *NewBufferHead
)
{
GetDataOriginatorChannel()->GetBufferQueue(NewBufferHead);
}
inline RPC_STATUS SetConnectionTimeout (
IN ULONG ConnectionTimeout
)
{
return GetPingOriginatorChannel()->SetConnectionTimeout(ConnectionTimeout);
}
virtual RPC_STATUS SetKeepAliveTimeout (
IN BOOL TurnOn,
IN BOOL bProtectIO,
IN KEEPALIVE_TIMEOUT_UNITS Units,
IN OUT KEEPALIVE_TIMEOUT KATime,
IN ULONG KAInterval = 5000 OPTIONAL
);
inline RPC_STATUS FlowControlAckNotify (
IN ULONG BytesReceivedForAck,
IN ULONG WindowForAck
)
{
return GetFlowControlSenderChannel()->FlowControlAckNotify(BytesReceivedForAck,
WindowForAck
);
}
inline void SetPeerReceiveWindow (
IN ULONG PeerReceiveWindow
)
{
GetFlowControlSenderChannel()->SetPeerReceiveWindow (
PeerReceiveWindow
);
}
inline ULONG GetPeerReceiveWindow (
void
)
{
return GetFlowControlSenderChannel()->GetPeerReceiveWindow ();
}
inline void GetFlowControlSenderBufferQueue (
OUT LIST_ENTRY *NewBufferHead
)
{
GetFlowControlSenderChannel()->GetBufferQueue(NewBufferHead);
}
inline void DisablePings (
void
)
{
GetPingOriginatorChannel()->DisablePings();
}
inline ULONG GetChosenAuthScheme (
void
)
{
return GetWinHttpConnection()->GetChosenAuthScheme();
}
inline BOOL IsKeepAlive (
void
)
{
return GetWinHttpConnection()->IsKeepAlive();
}
private:
inline HTTP2PlugChannel *GetPlugChannel (
void
)
{
BYTE *Channel = (BYTE *)this;
Channel += SIZE_OF_OBJECT_AND_PADDING(HTTP2ClientInChannel);
return (HTTP2PlugChannel *)Channel;
}
inline HTTP2ChannelDataOriginator *GetDataOriginatorChannel (
void
)
{
BYTE *Channel = (BYTE *)this;
Channel += SIZE_OF_OBJECT_AND_PADDING(HTTP2ClientInChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2PlugChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2FlowControlSender)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2PingOriginator)
;
return (HTTP2ChannelDataOriginator *)Channel;
}
inline HTTP2FlowControlSender *GetFlowControlSenderChannel (
void
)
{
BYTE *Channel = (BYTE *)this;
Channel += SIZE_OF_OBJECT_AND_PADDING(HTTP2ClientInChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2PlugChannel)
;
return (HTTP2FlowControlSender *)Channel;
}
inline HTTP2PingOriginator *GetPingOriginatorChannel (
void
)
{
BYTE *Channel = (BYTE *)this;
Channel += SIZE_OF_OBJECT_AND_PADDING(HTTP2ClientInChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2PlugChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2FlowControlSender)
;
return (HTTP2PingOriginator *)Channel;
}
inline HTTP2WinHttpTransportChannel *GetWinHttpConnection (
void
)
{
BYTE *Channel = (BYTE *)this;
Channel += SIZE_OF_OBJECT_AND_PADDING(HTTP2ClientInChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2PlugChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2FlowControlSender)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2PingOriginator)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2ChannelDataOriginator)
;
return (HTTP2WinHttpTransportChannel *)Channel;
}
};
class HTTP2ClientOutChannel : public HTTP2ClientChannel
{
public:
HTTP2ClientOutChannel (
IN HTTP2ClientVirtualConnection *ClientVirtualConnection,
OUT RPC_STATUS *RpcStatus
) : HTTP2ClientChannel (RpcStatus)
{
SetParent((HTTP2VirtualConnection *)ClientVirtualConnection);
}
RPC_STATUS ClientOpen (
IN HTTPResolverHint *Hint,
IN const char *Verb,
IN int VerbLength,
IN BOOL ReplacementChannel,
IN BOOL UseWinHttp,
IN RPC_HTTP_TRANSPORT_CREDENTIALS_W *HttpCredentials,
IN ULONG ChosenAuthScheme,
IN ULONG CallTimeout,
IN const BYTE *AdditionalData, OPTIONAL
IN ULONG AdditionalDataLength OPTIONAL
);
virtual RPC_STATUS ForwardFlowControlAck (
IN ULONG BytesReceivedForAck,
IN ULONG WindowForAck
);
inline BOOL IsDataReceivePosted (
void
)
{
return GetEndpointReceiver()->IsDataReceivePosted();
}
inline RPC_STATUS TransferReceiveStateToNewChannel (
OUT HTTP2ClientOutChannel *NewChannel
)
{
return GetEndpointReceiver()->TransferStateToNewReceiver (
NewChannel->GetEndpointReceiver()
);
}
inline void BlockDataReceives (
void
)
{
GetEndpointReceiver()->BlockDataReceives();
}
inline void UnblockDataReceives (
void
)
{
GetEndpointReceiver()->UnblockDataReceives();
}
virtual RPC_STATUS SetKeepAliveTimeout (
IN BOOL TurnOn,
IN BOOL bProtectIO,
IN KEEPALIVE_TIMEOUT_UNITS Units,
IN OUT KEEPALIVE_TIMEOUT KATime,
IN ULONG KAInterval = 5000 OPTIONAL
);
inline ULONG GetChosenAuthScheme (
void
)
{
return GetWinHttpConnection()->GetChosenAuthScheme();
}
inline BOOL IsKeepAlive (
void
)
{
return GetWinHttpConnection()->IsKeepAlive();
}
private:
inline HTTP2EndpointReceiver *GetEndpointReceiver (
void
)
{
BYTE *ThisChannelPtr = (BYTE *)this;
ThisChannelPtr += SIZE_OF_OBJECT_AND_PADDING(HTTP2ClientOutChannel);
return (HTTP2EndpointReceiver *)ThisChannelPtr;
}
inline HTTP2WinHttpTransportChannel *GetWinHttpConnection (
void
)
{
BYTE *Channel = (BYTE *)this;
Channel += SIZE_OF_OBJECT_AND_PADDING(HTTP2ClientOutChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2EndpointReceiver)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2PingReceiver)
;
return (HTTP2WinHttpTransportChannel *)Channel;
}
};
class HTTP2ClientVirtualConnection : public HTTP2VirtualConnection
{
public:
HTTP2ClientVirtualConnection (
IN RPC_HTTP_TRANSPORT_CREDENTIALS_W *NewCredentials,
OUT RPC_STATUS *RpcStatus
) : InChannelState(RpcStatus),
OutChannelState(RpcStatus),
BlockAbortsCounter (0)
{
ConnectionTimeout = 0;
type = COMPLEX_T | CONNECTION | CLIENT;
ReissueRecv = FALSE;
ChannelsAborted = FALSE;
ConnectionHint.Initialize();
CurrentKeepAlive = 0;
HttpCredentials = NewCredentials;
InProxyConnectionTimeout = 0;
ProtocolVersion = 0;
}
inline ~HTTP2ClientVirtualConnection (
void
)
{
ConnectionHint.FreeHTTPProxy();
ConnectionHint.FreeRpcProxy();
ConnectionHint.FreeRpcServer();
}
RPC_STATUS
ClientOpen(
IN HTTPResolverHint *Hint,
IN BOOL HintWasInitialized,
IN UINT ConnTimeout,
IN ULONG CallTimeout
);
virtual RPC_STATUS SendComplete (
IN RPC_STATUS EventStatus,
IN OUT HTTP2SendContext *SendContext,
IN int ChannelId
);
virtual RPC_STATUS ReceiveComplete (
IN RPC_STATUS EventStatus,
IN BYTE *Buffer,
IN UINT BufferLength,
IN int ChannelId
);
virtual RPC_STATUS SyncRecv (
IN BYTE **Buffer,
IN ULONG *BufferLength,
IN ULONG Timeout
);
virtual void Abort (
void
);
virtual void Close (
IN BOOL DontFlush
);
virtual RPC_STATUS TurnOnOffKeepAlives (
IN BOOL TurnOn,
IN BOOL bProtectIO,
IN BOOL IsFromUpcall,
IN KEEPALIVE_TIMEOUT_UNITS Units,
IN OUT KEEPALIVE_TIMEOUT KATime,
IN ULONG KAInterval = 5000 OPTIONAL
);
virtual RPC_STATUS RecycleChannel (
IN BOOL IsFromUpcall
);
RPC_STATUS OpenReplacementOutChannel (
void
);
inline HTTP2ClientInChannel *LockDefaultInChannel (
OUT HTTP2ChannelPointer **ChannelPointer
)
{
return (HTTP2ClientInChannel *)HTTP2VirtualConnection::LockDefaultInChannel(ChannelPointer);
}
inline HTTP2ClientOutChannel *LockDefaultOutChannel (
OUT HTTP2ChannelPointer **ChannelPointer
)
{
return (HTTP2ClientOutChannel *)HTTP2VirtualConnection::LockDefaultOutChannel(ChannelPointer);
}
void AbortChannels (
IN RPC_STATUS RpcStatus
);
protected:
virtual HTTP2Channel *LockDefaultSendChannel (
OUT HTTP2ChannelPointer **ChannelPtr
);
virtual HTTP2Channel *LockDefaultReceiveChannel (
OUT HTTP2ChannelPointer **ChannelPtr
);
private:
RPC_STATUS
ClientOpenInternal(
IN HTTPResolverHint *Hint,
IN BOOL HintWasInitialized,
IN UINT ConnTimeout,
IN ULONG CallTimeout,
IN BOOL ClientOpenInChannel,
IN BOOL ClientOpenOutChannel,
IN BOOL IsReplacementChannel,
IN BOOL IsFromUpcall
);
RPC_STATUS AllocateAndInitializeInChannel (
IN HTTPResolverHint *Hint,
IN BOOL HintWasInitialized,
IN ULONG CallTimeout,
IN BOOL UseWinHttp,
OUT HTTP2ClientInChannel **ReturnInChannel
);
RPC_STATUS AllocateAndInitializeOutChannel (
IN HTTPResolverHint *Hint,
IN BOOL HintWasInitialized,
IN ULONG CallTimeout,
IN BOOL UseWinHttp,
OUT HTTP2ClientOutChannel **ReturnOutChannel
);
RPC_STATUS InitializeRawConnection (
IN OUT WS_HTTP2_CONNECTION *RawConnection,
IN HTTPResolverHint *Hint,
IN BOOL HintWasInitialized,
IN ULONG CallTimeout
);
inline void SetClientOpenInEvent (
void
)
{
InChannelState.Mutex.Request();
if (ClientOpenInEvent)
{
SetEvent(ClientOpenInEvent);
}
InChannelState.Mutex.Clear();
}
inline void SetClientOpenOutEvent (
void
)
{
InChannelState.Mutex.Request();
if (ClientOpenOutEvent)
{
SetEvent(ClientOpenOutEvent);
}
InChannelState.Mutex.Clear();
}
BOOL IsInChannelKeepAlive (
IN BOOL IsReplacementChannel
);
BOOL IsOutChannelKeepAlive (
IN BOOL IsReplacementChannel
);
ULONG GetInChannelChosenScheme (
IN BOOL IsReplacementChannel
);
ULONG GetOutChannelChosenScheme (
IN BOOL IsReplacementChannel
);
BOOL IsInChannelPositiveWithWait (
void
);
inline RPC_STATUS BlockAborts (
void
)
{
int LocalBlockAbortsCounter = BlockAbortsCounter.Increment();
LogEvent(SU_REFOBJ, EV_INC, this, IntToPtr(0x88), LocalBlockAbortsCounter, 1, 1);
// only 4 channels are subject to abort. Give it some space for multiple aborts
// in progress, and don't allow more than 10
ASSERT(BlockAbortsCounter.GetInteger() <= 10);
if (ChannelsAborted)
{
UnblockAborts();
return RPC_P_CONNECTION_SHUTDOWN;
}
return RPC_S_OK;
}
inline void UnblockAborts (
void
)
{
LogEvent(SU_REFOBJ, EV_DEC, this, IntToPtr(0x88), BlockAbortsCounter.GetInteger(), 1, 1);
BlockAbortsCounter.Decrement();
ASSERT(BlockAbortsCounter.GetInteger() >= 0);
}
inline void WaitForAbortsToUnblock (
void
)
{
#if DBG
int Retries = 0;
#endif
ASSERT(ChannelsAborted);
while (BlockAbortsCounter.GetInteger())
{
Sleep(2);
#if DBG
Retries ++;
if (Retries > 100000)
{
ASSERT(!"Cannot wait out for aborts to unblock");
Retries = 0;
}
#endif
}
}
HTTP2State InChannelState; // used as virtual connection state while
// the channels are not fully established
HTTP2State OutChannelState;
ULONG ConnectionTimeout; // connection timeout from the runtime perspective
// (but in milliseconds)
ULONG CurrentKeepAlive; // the current keep alive value for the connection
ULONG ProtocolVersion;
ULONG InProxyConnectionTimeout; // the connection timeout from IIS channel
// perspective. Used only after transport open
HANDLE ClientOpenInEvent; // valid only during open. Destruction must be
// protected by the InChannelState
HANDLE ClientOpenOutEvent; // valid only during open. Destruction must be
// protected by the InChannelState
RPC_STATUS InOpenStatus; // valid only during open
RPC_STATUS OutOpenStatus; // valid only during open
HTTPResolverHint ConnectionHint;
BOOL ReissueRecv; // valid only during channel recycling
// When set, it means the old default channel was nuked
// and a new default channel is established. In this
// case the connection must reissue the recv after
// consuming this flag
INTERLOCKED_INTEGER BlockAbortsCounter; // If set, this means abort should block until
// cleared. The open path on the client sets this to prevent a bunch
// of pesky race conditions where worker threads abort
// the connection while it is trying to get opened.
// This is a counter that may be accessed from multiple threads
BOOL ChannelsAborted; // non-zero if the channels are aborted. This flag
// goes from 0 to non-zero, but never back.
RPC_HTTP_TRANSPORT_CREDENTIALS_W *HttpCredentials; // the transport
// credentials as kept by the runtime. These are the
// encrypted version.
};
extern BOOL g_fHttpClientInitialized;
extern BOOL g_fHttpServerInitialized;
RPC_STATUS InitializeHttpClient (
void
);
RPC_STATUS InitializeHttpServer (
void
);
inline RPC_STATUS InitializeHttpClientIfNecessary (
void
)
{
if (g_fHttpClientInitialized)
return RPC_S_OK;
else
return InitializeHttpClient();
}
inline RPC_STATUS InitializeHttpServerIfNecessary (
void
)
{
if (g_fHttpServerInitialized)
return RPC_S_OK;
else
return InitializeHttpServer();
}
const ULONG HTTP2DefaultClientReceiveWindow = 64 * 1024; // 64K for client
const ULONG HTTP2DefaultInProxyReceiveWindow = 64 * 1024; // 64K for in proxy
const ULONG HTTP2DefaultOutProxyReceiveWindow = 64 * 1024; // 64K for out proxy
const ULONG HTTP2DefaultServerReceiveWindow = 64 * 1024; // 64K for server
const USHORT HTTP2ProtocolVersion = 1;
const ULONG DefaultClientKeepAliveInterval = 5 * 60 * 1000; // 5 minutes in milliseconds
const ULONG DefaultClientNoResponseKeepAliveInterval = 5 * 1000; // 5 seconds in milliseconds
const ULONG MinimumClientKeepAliveInterval = 1 * 60 * 1000; // 1 minute in milliseconds
const ULONG MinimumClientNewKeepAliveInterval = 10 * 1000; // 10 seconds in milliseconds
const ULONG MinimumClientSideKeepAliveInterval = 30 * 1000; // 30 seconds in milliseconds
const ULONG MinimumChannelLifetime = 1024 * 128; // 128K
extern ULONG DefaultChannelLifetime; // 128K for now
extern char *DefaultChannelLifetimeString;
extern ULONG DefaultChannelLifetimeStringLength; // does not include null terminator
const ULONG DefaultNoResponseTimeout = 15 * 60 * 1000; // 15 minutes in milliseconds
class HTTP2ProxyServerSideChannel : public HTTP2Channel
{
public:
HTTP2ProxyServerSideChannel (
IN HTTP2VirtualConnection *ProxyVirtualConnection,
WS_HTTP2_CONNECTION *RawConnection,
OUT RPC_STATUS *RpcStatus
) : HTTP2Channel(HTTP2Channel::ProxyChannelType, RpcStatus)
{
SetParent(ProxyVirtualConnection);
ASSERT(RawConnection);
this->RawConnection = RawConnection;
}
RPC_STATUS InitializeRawConnection (
IN RPC_CHAR *ServerName,
IN USHORT ServerPort,
IN ULONG ConnectionTimeout,
IN I_RpcProxyIsValidMachineFn IsValidMachineFn
);
private:
WS_HTTP2_CONNECTION *RawConnection;
};
// This is a wrapper around the timer handle used
// to track the handle and the connection that owns it.
struct TimerContext
{
// Each timer points to the connection so that we know which
// connection to notify during timeout callbacks.
class HTTP2TimeoutTargetConnection *Parent;
// A handle refers to the thread pool's timer.
// When Handle != NULL, timer is active. We set it
// to NULL when timer is inactive.
HANDLE Handle;
};
class HTTP2TimeoutTargetConnection : public HTTP2VirtualConnection
{
public:
HTTP2TimeoutTargetConnection (
void
)
{
InChannelTimer.Handle = NULL;
InChannelTimer.Parent = this;
OutChannelTimer.Handle = NULL;
OutChannelTimer.Parent = this;
}
~HTTP2TimeoutTargetConnection (
void
)
{
// make sure no one destroys the object with pending time outs
ASSERT(InChannelTimer.Handle == NULL);
ASSERT(OutChannelTimer.Handle == NULL);
// Make sure the timers point to the connection object.
ASSERT(InChannelTimer.Parent == this);
ASSERT(OutChannelTimer.Parent == this);
}
RPC_STATUS SetTimeout (
IN ULONG Timeout,
IN TimerContext *pTimer
);
void CancelTimeout (
IN TimerContext *pTimer
);
inline void CancelAllTimeouts (
void
)
{
CancelTimeout(&InChannelTimer);
CancelTimeout(&OutChannelTimer);
}
inline TimerContext* GetInChannelTimer (
void
)
{
return &InChannelTimer;
}
inline TimerContext* GetOutChannelTimer (
void
)
{
return &OutChannelTimer;
}
virtual void TimeoutExpired (
IN TimerContext *pTimer
) = 0;
protected:
inline void TimerExpiredNotify (
IN TimerContext *pTimer
)
{
pTimer->Handle = NULL;
}
inline void VerifyValidTimer (
IN TimerContext *pTimer
)
{
ASSERT(pTimer == &InChannelTimer
|| pTimer == &OutChannelTimer);
}
inline void VerifyTimerNotSet (
IN TimerContext *pTimer
)
{
VerifyValidTimer(pTimer);
ASSERT(pTimer->Handle == NULL);
}
private:
TimerContext InChannelTimer;
TimerContext OutChannelTimer;
};
class HTTP2ProxyVirtualConnection : public HTTP2TimeoutTargetConnection
{
public:
HTTP2ProxyVirtualConnection (
OUT RPC_STATUS *RpcStatus
) : State(RpcStatus),
RundownBlock(0)
{
IsConnectionInCollection = FALSE;
ConnectionParameter = NULL;
ServerName = NULL;
ProxyConnectionCookie = NULL;
}
~HTTP2ProxyVirtualConnection (
void
)
{
if (ServerName)
{
delete [] ServerName;
ServerName = NULL;
}
}
virtual RPC_STATUS InitializeProxyFirstLeg (
IN USHORT *ServerAddress,
IN USHORT *ServerPort,
IN void *ConnectionParameter,
IN I_RpcProxyCallbackInterface *ProxyCallbackInterface,
OUT void **IISContext
) = 0;
virtual RPC_STATUS StartProxy (
void
) = 0;
virtual RPC_STATUS SendComplete (
IN RPC_STATUS EventStatus,
IN OUT HTTP2SendContext *SendContext,
IN int ChannelId
);
virtual void Abort (
void
);
virtual BOOL IsInProxy (
void
) = 0;
virtual void EnableIISSessionClose (
void
) = 0;
virtual void DisconnectChannels (
IN BOOL ExemptChannel,
IN int ExemptChannelId
);
RPC_STATUS AddConnectionToCookieCollection (
void
);
void RemoveConnectionFromCookieCollection (
void
);
inline void BlockConnectionFromRundown (
void
)
{
RundownBlock.Increment();
}
inline void UnblockConnectionFromRundown (
void
)
{
RundownBlock.Decrement();
}
virtual void TimeoutExpired (
IN TimerContext *pTimer
);
inline HTTP2ServerCookie *GetCookie (
void
)
{
return ProxyConnectionCookie;
}
RPC_STATUS ProxyForwardDataTrafficToDefaultChannel (
IN BOOL IsInChannel,
IN BYTE *Packet,
IN ULONG PacketLength,
IN ULONG ChannelId
);
protected:
inline int ConvertChannelIdToSendContextUserData (
IN int ChannelId
)
{
return (ChannelId & 0xFFFF);
}
inline HTTP2ChannelPointer *MapSendContextUserDataToChannelPtr (
IN int SendContextUserData
)
{
// check the in channels only
if (ConvertChannelIdToSendContextUserData(InChannelIds[0]) == SendContextUserData)
return &InChannels[0];
else if (ConvertChannelIdToSendContextUserData(InChannelIds[1]) == SendContextUserData)
return &InChannels[1];
else
return NULL;
}
HTTP2State State;
ULONG ConnectionTimeout;
ULONG ProtocolVersion;
I_RpcProxyCallbackInterface *ProxyCallbackInterface; // callback interface for proxy specific
// functions
RPC_CHAR *ServerName; // the server name. Valid until the out channel is opened.
USHORT ServerPort; // the server port. Valid for the lifetime of the connection
ULONG IISConnectionTimeout;
BOOL IsConnectionInCollection;
void *ConnectionParameter; // valid only between establishment header and first
// RTS packet
INTERLOCKED_INTEGER RundownBlock;
HTTP2ServerCookie *ProxyConnectionCookie; // proxies don't use the embedded connection
// cookie - they use this pointer. The reason is that multiple connections
// can use the same cookie when we act as web farm and we can't have
// the cookie embedded in any individual connection as this opens life
// time issues.
};
class HTTP2InProxyVirtualConnection;
class HTTP2InProxyInChannel : public HTTP2Channel
{
public:
HTTP2InProxyInChannel (
IN HTTP2InProxyVirtualConnection *InProxyVirtualConnection,
OUT RPC_STATUS *RpcStatus
) : HTTP2Channel(HTTP2Channel::ProxyChannelType, RpcStatus)
{
SetParent((HTTP2VirtualConnection *)InProxyVirtualConnection);
}
inline void TransferReceiveStateToNewChannel (
OUT HTTP2InProxyInChannel *NewChannel
)
{
GetProxyReceiver()->TransferStateToNewReceiver (
NewChannel->GetProxyReceiver()
);
}
virtual RPC_STATUS ForwardFlowControlAck (
IN ULONG BytesReceivedForAck,
IN ULONG WindowForAck
);
inline void BytesConsumedNotification (
IN ULONG Bytes,
IN BOOL OwnsMutex,
OUT BOOL *IssueAck,
OUT ULONG *BytesReceivedForAck,
OUT ULONG *WindowForAck
)
{
GetProxyReceiver()->BytesConsumedNotification (Bytes,
OwnsMutex,
IssueAck,
BytesReceivedForAck,
WindowForAck
);
}
inline void EnableIISSessionClose (
void
)
{
GetIISTransportChannel()->EnableIISSessionClose();
}
protected:
inline HTTP2ProxyReceiver *GetProxyReceiver (
void
)
{
BYTE *ThisChannelPtr = (BYTE *)this;
ThisChannelPtr += SIZE_OF_OBJECT_AND_PADDING(HTTP2InProxyInChannel);
return (HTTP2ProxyReceiver *)ThisChannelPtr;
}
inline HTTP2IISTransportChannel *GetIISTransportChannel (
void
)
{
BYTE *ThisChannelPtr = (BYTE *)this;
ThisChannelPtr += SIZE_OF_OBJECT_AND_PADDING(HTTP2InProxyInChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2ProxyReceiver)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2PingReceiver)
;
return (HTTP2IISTransportChannel *)ThisChannelPtr;
}
};
class HTTP2InProxyOutChannel : public HTTP2ProxyServerSideChannel
{
public:
HTTP2InProxyOutChannel (
IN HTTP2InProxyVirtualConnection *InProxyVirtualConnection,
IN WS_HTTP2_CONNECTION *RawConnection,
OUT RPC_STATUS *RpcStatus
) : HTTP2ProxyServerSideChannel((HTTP2VirtualConnection *)InProxyVirtualConnection,
RawConnection,
RpcStatus)
{
}
virtual RPC_STATUS LastPacketSentNotification (
IN HTTP2SendContext *LastSendContext
);
inline RPC_STATUS Unplug (
void
)
{
return GetPlugChannel()->Unplug();
}
RPC_STATUS SetRawConnectionKeepAlive (
IN ULONG KeepAliveInterval // in milliseconds
);
inline RPC_STATUS FlowControlAckNotify (
IN ULONG BytesReceivedForAck,
IN ULONG WindowForAck
)
{
RPC_STATUS RpcStatus;
// Need to synchronize with aborts (rule 9).
RpcStatus = BeginSimpleSubmitAsync();
if (RpcStatus == RPC_S_OK)
{
RpcStatus = GetFlowControlSenderChannel()->FlowControlAckNotify(BytesReceivedForAck,
WindowForAck
);
FinishSubmitAsync();
}
return RpcStatus;
}
inline void SetPeerReceiveWindow (
IN ULONG PeerReceiveWindow
)
{
GetFlowControlSenderChannel()->SetPeerReceiveWindow (
PeerReceiveWindow
);
}
inline ULONG GetPeerReceiveWindow (
void
)
{
return GetFlowControlSenderChannel()->GetPeerReceiveWindow ();
}
protected:
inline WS_HTTP2_CONNECTION *GetRawConnection (
void
)
{
BYTE *ThisChannelPtr = (BYTE *)this;
ThisChannelPtr += SIZE_OF_OBJECT_AND_PADDING(HTTP2InProxyOutChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2ProxyPlugChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2FlowControlSender)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2ProxySocketTransportChannel)
;
return (WS_HTTP2_CONNECTION *)ThisChannelPtr;
}
inline HTTP2FlowControlSender *GetFlowControlSenderChannel (
void
)
{
BYTE *ThisChannelPtr = (BYTE *)this;
ThisChannelPtr += SIZE_OF_OBJECT_AND_PADDING(HTTP2InProxyOutChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2ProxyPlugChannel)
;
return (HTTP2FlowControlSender *)ThisChannelPtr;
}
inline HTTP2PlugChannel *GetPlugChannel (
void
)
{
BYTE *ThisChannelPtr = (BYTE *)this;
ThisChannelPtr += SIZE_OF_OBJECT_AND_PADDING(HTTP2InProxyOutChannel);
return (HTTP2PlugChannel *)ThisChannelPtr;
}
};
class HTTP2InProxyVirtualConnection : public HTTP2ProxyVirtualConnection
{
public:
HTTP2InProxyVirtualConnection (
OUT RPC_STATUS *RpcStatus
) : HTTP2ProxyVirtualConnection(RpcStatus)
{
ConnectionTimeout = 0;
RpcpMemorySet(&ClientAddress, 0, sizeof(ClientAddress));
}
virtual RPC_STATUS InitializeProxyFirstLeg (
IN USHORT *ServerAddress,
IN USHORT *ServerPort,
IN void *ConnectionParameter,
IN I_RpcProxyCallbackInterface *ProxyCallbackInterface,
OUT void **IISContext
);
virtual RPC_STATUS StartProxy (
void
);
RPC_STATUS InitializeProxySecondLeg (
void
);
virtual RPC_STATUS ReceiveComplete (
IN RPC_STATUS EventStatus,
IN BYTE *Buffer,
IN UINT BufferLength,
IN int ChannelId
);
virtual BOOL IsInProxy (
void
)
{
return TRUE;
}
virtual void EnableIISSessionClose (
void
);
virtual void DisconnectChannels (
IN BOOL ExemptChannel,
IN int ExemptChannelId
);
inline HTTP2InProxyInChannel *LockDefaultInChannel (
OUT HTTP2ChannelPointer **ChannelPointer
)
{
return (HTTP2InProxyInChannel *)HTTP2VirtualConnection::LockDefaultInChannel(ChannelPointer);
}
inline HTTP2InProxyOutChannel *LockDefaultOutChannel (
OUT HTTP2ChannelPointer **ChannelPointer
)
{
return (HTTP2InProxyOutChannel *)HTTP2VirtualConnection::LockDefaultOutChannel(ChannelPointer);
}
private:
RPC_STATUS AllocateAndInitializeInChannel (
IN void *ConnectionParameter,
OUT HTTP2InProxyInChannel **ReturnInChannel,
OUT void **IISContext
);
RPC_STATUS AllocateAndInitializeOutChannel (
OUT HTTP2InProxyOutChannel **ReturnOutChannel
);
RPC_STATUS ConnectToServer (
void
);
RPC_STATUS SendD1_B2ToServer (
void
);
RPC_STATUS SendD2_A2ToServer (
void
);
ULONG ChannelLifetime;
ULONG CurrentClientKeepAliveInterval;
ULONG DefaultClientKeepAliveInterval;
ChannelSettingClientAddress ClientAddress; // needed only while processing D1/B1
HTTP2Cookie AssociationGroupId; // needed only while processing D1/B1
QUEUE NonDefaultChannelBufferQueue; // used only in Opened_A5W state
};
class HTTP2OutProxyVirtualConnection;
class HTTP2OutProxyInChannel : public HTTP2ProxyServerSideChannel
{
public:
HTTP2OutProxyInChannel (
IN HTTP2OutProxyVirtualConnection *OutProxyVirtualConnection,
WS_HTTP2_CONNECTION *RawConnection,
OUT RPC_STATUS *RpcStatus
) : HTTP2ProxyServerSideChannel((HTTP2VirtualConnection *)OutProxyVirtualConnection,
RawConnection,
RpcStatus)
{
}
virtual RPC_STATUS ForwardFlowControlAck (
IN ULONG BytesReceivedForAck,
IN ULONG WindowForAck
);
inline void BytesConsumedNotification (
IN ULONG Bytes,
IN BOOL OwnsMutex,
OUT BOOL *IssueAck,
OUT ULONG *BytesReceivedForAck,
OUT ULONG *WindowForAck
)
{
GetProxyReceiver()->BytesConsumedNotification (Bytes,
OwnsMutex,
IssueAck,
BytesReceivedForAck,
WindowForAck
);
}
protected:
inline HTTP2ProxyReceiver *GetProxyReceiver (
void
)
{
BYTE *ThisChannelPtr = (BYTE *)this;
ThisChannelPtr += SIZE_OF_OBJECT_AND_PADDING(HTTP2OutProxyInChannel);
return (HTTP2ProxyReceiver *)ThisChannelPtr;
}
};
// at the current size of the ping packet, this is 43 ping packets
const ULONG AccumulatedPingTrafficNotifyThreshold = 1032;
class HTTP2OutProxyOutChannel : public HTTP2Channel
{
public:
HTTP2OutProxyOutChannel (
IN HTTP2OutProxyVirtualConnection *OutProxyVirtualConnection,
OUT RPC_STATUS *RpcStatus
) : HTTP2Channel(HTTP2Channel::ProxyChannelType, RpcStatus)
{
SetParent((HTTP2VirtualConnection *)OutProxyVirtualConnection);
AccumulatedPingTraffic = 0;
}
virtual RPC_STATUS LastPacketSentNotification (
IN HTTP2SendContext *LastSendContext
);
inline RPC_STATUS Unplug (
void
)
{
HTTP2PlugChannel *PlugChannel;
PlugChannel = GetPlugChannel();
return PlugChannel->Unplug();
}
inline void SetStrongPlug (
void
)
{
HTTP2PlugChannel *PlugChannel;
PlugChannel = GetPlugChannel();
return PlugChannel->SetStrongPlug();
}
virtual void PingTrafficSentNotify (
IN ULONG PingTrafficSize
);
BOOL PingTrafficSentNotifyServer (
IN ULONG PingTrafficSize
);
inline RPC_STATUS SetConnectionTimeout (
IN ULONG ConnectionTimeout
)
{
return GetPingOriginatorChannel()->SetConnectionTimeout(ConnectionTimeout);
}
inline RPC_STATUS FlowControlAckNotify (
IN ULONG BytesReceivedForAck,
IN ULONG WindowForAck
)
{
return GetFlowControlSenderChannel()->FlowControlAckNotify(BytesReceivedForAck,
WindowForAck
);
}
inline void SetPeerReceiveWindow (
IN ULONG PeerReceiveWindow
)
{
GetFlowControlSenderChannel()->SetPeerReceiveWindow (
PeerReceiveWindow
);
}
inline ULONG GetPeerReceiveWindow (
void
)
{
return GetFlowControlSenderChannel()->GetPeerReceiveWindow ();
}
inline void GetFlowControlSenderBufferQueue (
OUT LIST_ENTRY *NewBufferHead
)
{
GetFlowControlSenderChannel()->GetBufferQueue(NewBufferHead);
}
inline void EnableIISSessionClose (
void
)
{
GetIISTransportChannel()->EnableIISSessionClose();
}
private:
inline HTTP2PlugChannel *GetPlugChannel (
void
)
{
BYTE *Channel = (BYTE *)this;
Channel += SIZE_OF_OBJECT_AND_PADDING(HTTP2OutProxyOutChannel);
return (HTTP2PlugChannel *)Channel;
}
inline HTTP2PingOriginator *GetPingOriginatorChannel (
void
)
{
BYTE *Channel = (BYTE *)this;
Channel += SIZE_OF_OBJECT_AND_PADDING(HTTP2OutProxyOutChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2ProxyPlugChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2FlowControlSender)
;
return (HTTP2PingOriginator *)Channel;
}
inline HTTP2FlowControlSender *GetFlowControlSenderChannel (
void
)
{
BYTE *Channel = (BYTE *)this;
Channel += SIZE_OF_OBJECT_AND_PADDING(HTTP2OutProxyOutChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2ProxyPlugChannel)
;
return (HTTP2FlowControlSender *)Channel;
}
inline HTTP2IISTransportChannel *GetIISTransportChannel (
void
)
{
BYTE *ThisChannelPtr = (BYTE *)this;
ThisChannelPtr += SIZE_OF_OBJECT_AND_PADDING(HTTP2OutProxyOutChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2ProxyPlugChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2FlowControlSender)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2PingOriginator)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2PingReceiver)
;
return (HTTP2IISTransportChannel *)ThisChannelPtr;
}
ULONG AccumulatedPingTraffic;
};
extern ULONG HTTP2OutProxyReceiveWindow;
typedef enum tagOutProxyLastPacketType
{
oplptInvalid = 0,
oplptD4_A10,
oplptD5_B3
} OutProxyLastPacketType;
class HTTP2OutProxyVirtualConnection : public HTTP2ProxyVirtualConnection
{
public:
HTTP2OutProxyVirtualConnection (
OUT RPC_STATUS *RpcStatus
) : HTTP2ProxyVirtualConnection(RpcStatus)
{
ConnectionTimeout = 0;
ProxyReceiveWindowSize = HTTP2OutProxyReceiveWindow;
}
virtual RPC_STATUS InitializeProxyFirstLeg (
IN USHORT *ServerAddress,
IN USHORT *ServerPort,
IN void *ConnectionParameter,
IN I_RpcProxyCallbackInterface *ProxyCallbackInterface,
OUT void **IISContext
);
virtual RPC_STATUS StartProxy (
void
);
RPC_STATUS InitializeProxySecondLeg (
void
);
virtual RPC_STATUS ReceiveComplete (
IN RPC_STATUS EventStatus,
IN BYTE *Buffer,
IN UINT BufferLength,
IN int ChannelId
);
virtual BOOL IsInProxy (
void
)
{
return FALSE;
}
virtual void EnableIISSessionClose (
void
);
BOOL PingTrafficSentNotifyServer (
IN ULONG PingTrafficSize
);
inline HTTP2OutProxyInChannel *LockDefaultInChannel (
OUT HTTP2ChannelPointer **ChannelPointer
)
{
return (HTTP2OutProxyInChannel *)HTTP2VirtualConnection::LockDefaultInChannel(ChannelPointer);
}
inline HTTP2OutProxyOutChannel *LockDefaultOutChannel (
OUT HTTP2ChannelPointer **ChannelPointer
)
{
return (HTTP2OutProxyOutChannel *)HTTP2VirtualConnection::LockDefaultOutChannel(ChannelPointer);
}
virtual void LastPacketSentNotification (
IN int ChannelId,
IN HTTP2SendContext *LastSendContext
);
private:
RPC_STATUS ConnectToServer (
);
RPC_STATUS SendHeaderToClient (
void
);
RPC_STATUS SendD1_A3ToClient (
void
);
RPC_STATUS SendD1_A2ToServer (
IN ULONG ChannelLifetime
);
RPC_STATUS SendD4_A4ToServer (
IN ULONG ChannelLifetime
);
RPC_STATUS AllocateAndInitializeInChannel (
OUT HTTP2OutProxyInChannel **ReturnOutChannel
);
RPC_STATUS AllocateAndInitializeOutChannel (
IN void *ConnectionParameter,
OUT HTTP2OutProxyOutChannel **ReturnInChannel,
OUT void **IISContext
);
ULONG ProxyReceiveWindowSize;
};
class HTTP2ServerVirtualConnection; // forward
class HTTP2ServerChannel : public HTTP2Channel
{
public:
HTTP2ServerChannel (
OUT RPC_STATUS *Status
) : HTTP2Channel (0, // InitialFlag
Status)
{
}
HTTP2ServerVirtualConnection *LockParentPointer (
void
)
{
return (HTTP2ServerVirtualConnection *)HTTP2Channel::LockParentPointer();
}
virtual void AbortConnection (
IN RPC_STATUS AbortReason
);
};
class HTTP2ServerInChannel : public HTTP2ServerChannel
{
public:
HTTP2ServerInChannel (
OUT RPC_STATUS *RpcStatus
) : HTTP2ServerChannel (RpcStatus)
{
}
RPC_STATUS QueryLocalAddress (
IN OUT void *Buffer,
IN OUT unsigned long *BufferSize,
OUT unsigned long *AddressFormat
);
virtual RPC_STATUS ForwardFlowControlAck (
IN ULONG BytesReceivedForAck,
IN ULONG WindowForAck
);
inline BOOL IsDataReceivePosted (
void
)
{
return GetEndpointReceiver()->IsDataReceivePosted();
}
inline RPC_STATUS TransferReceiveStateToNewChannel (
OUT HTTP2ServerInChannel *NewChannel
)
{
return GetEndpointReceiver()->TransferStateToNewReceiver (
NewChannel->GetEndpointReceiver()
);
}
protected:
inline WS_HTTP2_CONNECTION *GetRawConnection (
void
)
{
BYTE *ThisChannelPtr = (BYTE *)this;
ThisChannelPtr += SIZE_OF_OBJECT_AND_PADDING(HTTP2ServerInChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2EndpointReceiver)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2SocketTransportChannel);
return (WS_HTTP2_CONNECTION *)ThisChannelPtr;
}
inline HTTP2EndpointReceiver *GetEndpointReceiver (
void
)
{
BYTE *ThisChannelPtr = (BYTE *)this;
ThisChannelPtr += SIZE_OF_OBJECT_AND_PADDING(HTTP2ServerInChannel);
return (HTTP2EndpointReceiver *)ThisChannelPtr;
}
};
class HTTP2ServerOutChannel : public HTTP2ServerChannel
{
public:
HTTP2ServerOutChannel (
OUT RPC_STATUS *RpcStatus
) : HTTP2ServerChannel (RpcStatus),
DataSendsPending (0)
{
PingOriginator = NULL;
CachedLastSendContextUsed = FALSE;
}
virtual RPC_STATUS Send (
IN OUT HTTP2SendContext *SendContext
);
virtual RPC_STATUS SendComplete (
IN RPC_STATUS EventStatus,
IN OUT HTTP2SendContext *SendContext
);
void HTTP2ServerOutChannel::SendCancelled (
IN HTTP2SendContext *SendContext
);
virtual RPC_STATUS SyncSend (
IN HTTP2TrafficType TrafficType,
IN ULONG BufferLength,
IN BYTE *Buffer,
IN BOOL fDisableCancelCheck,
IN ULONG Timeout,
IN BASE_ASYNC_OBJECT *Connection,
IN HTTP2SendContext *SendContext
);
virtual RPC_STATUS SetKeepAliveTimeout (
IN BOOL TurnOn,
IN BOOL bProtectIO,
IN KEEPALIVE_TIMEOUT_UNITS Units,
IN OUT KEEPALIVE_TIMEOUT KATime,
IN ULONG KAInterval = 5000 OPTIONAL
);
virtual RPC_STATUS LastPacketSentNotification (
IN HTTP2SendContext *LastSendContext
);
inline RPC_STATUS Unplug (
void
)
{
HTTP2PlugChannel *PlugChannel;
PlugChannel = GetPlugChannel();
return PlugChannel->Unplug();
}
RPC_STATUS GetChannelOriginatorBufferQueue (
OUT LIST_ENTRY *NewBufferHead
);
inline void PlugDataOriginatorChannel (
void
)
{
GetDataOriginatorChannel()->PlugChannel();
}
inline RPC_STATUS RestartDataOriginatorChannel (
void
)
{
return GetDataOriginatorChannel()->RestartChannel();
}
inline RPC_STATUS NotifyDataOriginatorForTrafficSent (
IN ULONG TrafficSentSize
)
{
return GetDataOriginatorChannel()->NotifyTrafficSent(TrafficSentSize);
}
inline RPC_STATUS FlowControlAckNotify (
IN ULONG BytesReceivedForAck,
IN ULONG WindowForAck
)
{
return GetFlowControlSenderChannel()->FlowControlAckNotify(BytesReceivedForAck,
WindowForAck
);
}
inline void SetPeerReceiveWindow (
IN ULONG PeerReceiveWindow
)
{
GetFlowControlSenderChannel()->SetPeerReceiveWindow (
PeerReceiveWindow
);
}
inline ULONG GetPeerReceiveWindow (
void
)
{
return GetFlowControlSenderChannel()->GetPeerReceiveWindow ();
}
RPC_STATUS GetFlowControlSenderBufferQueue (
OUT LIST_ENTRY *NewBufferHead
);
HTTP2SendContext *GetLastSendContext (
void
);
inline void FreeLastSendContext (
IN HTTP2SendContext *SendContext
)
{
if (SendContext == GetCachedLastSendContext())
CachedLastSendContextUsed = FALSE;
else
delete SendContext;
}
inline void DrainPendingSends (
void
)
{
#if DBG
int Retries = 0;
#endif
while (DataSendsPending.GetInteger() > 0)
{
#if DBG
Retries ++;
if (Retries > 100000)
{
ASSERT(!"Cannot drain pending sends on channel");
Retries = 0;
}
#endif
Sleep(2);
}
}
private:
inline HTTP2PlugChannel *GetPlugChannel (
void
)
{
BYTE *Channel = (BYTE *)this;
Channel += SIZE_OF_OBJECT_AND_PADDING(HTTP2ServerOutChannel);
return (HTTP2PlugChannel *)Channel;
}
inline HTTP2ChannelDataOriginator *GetDataOriginatorChannel (
void
)
{
BYTE *Channel = (BYTE *)this;
Channel += SIZE_OF_OBJECT_AND_PADDING(HTTP2ServerOutChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2PlugChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2FlowControlSender)
;
return (HTTP2ChannelDataOriginator *)Channel;
}
inline HTTP2FlowControlSender *GetFlowControlSenderChannel (
void
)
{
BYTE *Channel = (BYTE *)this;
Channel += SIZE_OF_OBJECT_AND_PADDING(HTTP2ServerOutChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2PlugChannel)
;
return (HTTP2FlowControlSender *)Channel;
}
inline HTTP2SendContext *GetCachedLastSendContext (
void
)
{
BYTE *Channel = (BYTE *)this;
Channel += SIZE_OF_OBJECT_AND_PADDING(HTTP2ServerOutChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2PlugChannel)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2FlowControlSender)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2ChannelDataOriginator)
+ SIZE_OF_OBJECT_AND_PADDING(HTTP2SocketTransportChannel)
+ SIZE_OF_OBJECT_AND_PADDING(WS_HTTP2_CONNECTION)
;
return (HTTP2SendContext *)Channel;
}
RPC_STATUS UnaffinitizeSendContextList (
IN LIST_ENTRY *ListHead
);
HTTP2PingOriginator *PingOriginator;
BOOL CachedLastSendContextUsed; // non-zero if the cached last send context is
// currently used.
INTERLOCKED_INTEGER DataSendsPending;
};
const ULONG BytesSentByProxyTimeInterval = 4 * 60 * 1000; // 4 minutes in millisecons
const ULONG MaxBytesSentByProxy = 8 * 1024; // 8K is the maximum we allow the proxy per unit of time
typedef struct tagHTTP2OutProxyTransportSettings
{
ULONG ReceiveWindow;
ULONG ChannelLifetime;
} HTTP2OutProxyTransportSettings;
class HTTP2ServerVirtualConnection : public HTTP2TimeoutTargetConnection
{
public:
HTTP2ServerVirtualConnection (
IN HTTP2ServerCookie *NewCookie,
IN ULONG ProtocolVersion,
OUT RPC_STATUS *RpcStatus
) : InChannelState(RpcStatus),
OutChannelState(RpcStatus)
{
InProxyReceiveWindows[0] = 0;
InProxyReceiveWindows[1] = 0;
OutProxySettings[0].ReceiveWindow = 0;
OutProxySettings[0].ChannelLifetime = 0;
OutProxySettings[1].ReceiveWindow = 0;
OutProxySettings[1].ChannelLifetime = 0;
InProxyConnectionTimeout = 0;
this->ProtocolVersion = ProtocolVersion;
InChannelState.State = http2svClosed;
RpcpMemorySet(&ClientAddress, 0, sizeof(ClientAddress));
EmbeddedConnectionCookie.SetConnection(this);
EmbeddedConnectionCookie.SetCookie(NewCookie->GetCookie());
BytesSentByProxyForInterval = 0;
BytesSentByProxyTimeIntervalStart = 0;
LastBufferToFree = NULL;
}
virtual RPC_STATUS SendComplete (
IN RPC_STATUS EventStatus,
IN OUT HTTP2SendContext *SendContext,
IN int ChannelId
);
virtual RPC_STATUS ReceiveComplete (
IN RPC_STATUS EventStatus,
IN BYTE *Buffer,
IN UINT BufferLength,
IN int ChannelId
);
virtual RPC_STATUS SyncSend (
IN ULONG BufferLength,
IN BYTE *Buffer,
IN BOOL fDisableShutdownCheck,
IN BOOL fDisableCancelCheck,
IN ULONG Timeout
);
virtual void Abort (
void
);
virtual void Close (
IN BOOL DontFlush
);
virtual RPC_STATUS QueryClientAddress (
OUT RPC_CHAR **pNetworkAddress
);
virtual RPC_STATUS QueryLocalAddress (
IN OUT void *Buffer,
IN OUT unsigned long *BufferSize,
OUT unsigned long *AddressFormat
);
virtual RPC_STATUS QueryClientId(
OUT RPC_CLIENT_PROCESS_IDENTIFIER *ClientProcess
);
virtual RPC_STATUS QueryClientIpAddress (
IN OUT RPC_CLIENT_IP_ADDRESS *ClientIpAddress
);
virtual void LastPacketSentNotification (
IN int ChannelId,
IN HTTP2SendContext *LastSendContext
);
virtual RPC_STATUS RecycleChannel (
IN BOOL IsFromUpcall
);
// must initialize the type member and migrate the
// WS_HTTP2_INITIAL_CONNECTION after morphing it into
// WS_HTTP2_CONNECTION
static RPC_STATUS InitializeServerConnection (
IN BYTE *Packet,
IN ULONG PacketLength,
IN WS_HTTP2_INITIAL_CONNECTION *Connection,
OUT HTTP2ServerVirtualConnection **ServerVirtualConnection,
OUT BOOL *VirtualConnectionCreated
);
static RPC_STATUS AllocateAndInitializeInChannel (
IN OUT WS_HTTP2_INITIAL_CONNECTION **Connection,
OUT HTTP2ServerInChannel **ReturnInChannel
);
static RPC_STATUS AllocateAndInitializeOutChannel (
IN OUT WS_HTTP2_INITIAL_CONNECTION **Connection,
IN ULONG OutChannelLifetime,
OUT HTTP2ServerOutChannel **ReturnOutChannel
);
inline HTTP2ServerInChannel *LockDefaultInChannel (
OUT HTTP2ChannelPointer **ChannelPointer
)
{
return (HTTP2ServerInChannel *)HTTP2VirtualConnection::LockDefaultInChannel(ChannelPointer);
}
inline HTTP2ServerOutChannel *LockDefaultOutChannel (
OUT HTTP2ChannelPointer **ChannelPointer
)
{
return (HTTP2ServerOutChannel *)HTTP2VirtualConnection::LockDefaultOutChannel(ChannelPointer);
}
virtual void TimeoutExpired (
IN TimerContext *pTimer
);
inline void SetLastBufferToFree (
IN void *NewLastBufferToFree
)
{
ASSERT(LastBufferToFree == NULL);
LastBufferToFree = NewLastBufferToFree;
}
inline BOOL IsLastBufferToFreeSet (
void
)
{
return (LastBufferToFree != NULL);
}
inline void *GetAndResetLastBufferToFree (
void
)
{
void *LocalLastBufferToFree;
ASSERT(LastBufferToFree != NULL);
LocalLastBufferToFree = LastBufferToFree;
LastBufferToFree = NULL;
return LocalLastBufferToFree;
}
private:
void DrainOutChannelPendingSends (
void
);
HTTP2State InChannelState; // used as virtual connection state while
// the channels are not fully established
HTTP2State OutChannelState;
ULONG ProtocolVersion;
// accessed through the default out channel selector
HTTP2OutProxyTransportSettings OutProxySettings[2];
ChannelSettingClientAddress ClientAddress;
ULONG InProxyConnectionTimeout; // valid b/n D1_B2 and D1_C1
// accessed through the default in channel selector
ULONG InProxyReceiveWindows[2];
HTTP2Cookie AssociationGroupId;
ULONG BytesSentByProxyForInterval; // the number of bytes sent by the proxy independently
// for the current time interval
ULONG BytesSentByProxyTimeIntervalStart; // the start of the current time interval
void *LastBufferToFree; // temporary storage for the last buffer to free
// Used b/n SetLastBufferToFree and the last send only.
// Must be freed using RpcFreeBuffer/I_RpcTransConnectionFreePacket
};
#endif // __HTTP2_HXX__