mirror of
https://github.com/celisej567/mcpe.git
synced 2025-12-31 17:49:17 +03:00
622 lines
16 KiB
C++
622 lines
16 KiB
C++
/*
|
|
* Copyright (c) 2014, Oculus VR, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
*/
|
|
|
|
#include "NativeFeatureIncludes.h"
|
|
#if _RAKNET_SUPPORT_HTTPConnection2==1 && _RAKNET_SUPPORT_TCPInterface==1
|
|
|
|
#include "HTTPConnection2.h"
|
|
#include "TCPInterface.h"
|
|
|
|
using namespace RakNet;
|
|
|
|
STATIC_FACTORY_DEFINITIONS(HTTPConnection2,HTTPConnection2);
|
|
|
|
HTTPConnection2::HTTPConnection2()
|
|
{
|
|
}
|
|
HTTPConnection2::~HTTPConnection2()
|
|
{
|
|
|
|
}
|
|
bool HTTPConnection2::TransmitRequest(const char* stringToTransmit, const char* host, unsigned short port, bool useSSL, int ipVersion, SystemAddress useAddress, void *userData)
|
|
{
|
|
Request *request = RakNet::OP_NEW<Request>(_FILE_AND_LINE_);
|
|
request->host=host;
|
|
request->chunked = false;
|
|
if (useAddress!=UNASSIGNED_SYSTEM_ADDRESS)
|
|
{
|
|
request->hostEstimatedAddress=useAddress;
|
|
if (IsConnected(useAddress)==false)
|
|
{
|
|
RakNet::OP_DELETE(request, _FILE_AND_LINE_);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (request->hostEstimatedAddress.FromString(host, '|', ipVersion)==false)
|
|
{
|
|
RakNet::OP_DELETE(request, _FILE_AND_LINE_);
|
|
return false;
|
|
}
|
|
}
|
|
request->hostEstimatedAddress.SetPortHostOrder(port);
|
|
request->port=port;
|
|
request->stringToTransmit=stringToTransmit;
|
|
request->contentLength=-1;
|
|
request->contentOffset=0;
|
|
request->useSSL=useSSL;
|
|
request->ipVersion=ipVersion;
|
|
request->userData=userData;
|
|
|
|
if (IsConnected(request->hostEstimatedAddress))
|
|
{
|
|
sentRequestsMutex.Lock();
|
|
if (sentRequests.Size()==0)
|
|
{
|
|
request->hostCompletedAddress=request->hostEstimatedAddress;
|
|
sentRequests.Push(request, _FILE_AND_LINE_);
|
|
sentRequestsMutex.Unlock();
|
|
|
|
SendRequest(request);
|
|
}
|
|
else
|
|
{
|
|
// Request pending, push it
|
|
pendingRequestsMutex.Lock();
|
|
pendingRequests.Push(request, _FILE_AND_LINE_);
|
|
pendingRequestsMutex.Unlock();
|
|
|
|
sentRequestsMutex.Unlock();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pendingRequestsMutex.Lock();
|
|
pendingRequests.Push(request, _FILE_AND_LINE_);
|
|
pendingRequestsMutex.Unlock();
|
|
|
|
if (ipVersion!=6)
|
|
{
|
|
tcpInterface->Connect(host, port, false, AF_INET);
|
|
}
|
|
else
|
|
{
|
|
#if RAKNET_SUPPORT_IPV6
|
|
tcpInterface->Connect(host, port, false, AF_INET6);
|
|
#else
|
|
RakAssert("HTTPConnection2::TransmitRequest needs define RAKNET_SUPPORT_IPV6" && 0);
|
|
#endif
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
bool HTTPConnection2::GetResponse( RakString &stringTransmitted, RakString &hostTransmitted, RakString &responseReceived, SystemAddress &hostReceived, int &contentOffset )
|
|
{
|
|
void *userData;
|
|
return GetResponse(stringTransmitted, hostTransmitted, responseReceived, hostReceived, contentOffset, &userData);
|
|
|
|
}
|
|
bool HTTPConnection2::GetResponse( RakString &stringTransmitted, RakString &hostTransmitted, RakString &responseReceived, SystemAddress &hostReceived, int &contentOffset, void **userData )
|
|
{
|
|
completedRequestsMutex.Lock();
|
|
if (completedRequests.Size()>0)
|
|
{
|
|
Request *completedRequest = completedRequests[0];
|
|
completedRequests.RemoveAtIndexFast(0);
|
|
completedRequestsMutex.Unlock();
|
|
|
|
responseReceived = completedRequest->stringReceived;
|
|
hostReceived = completedRequest->hostCompletedAddress;
|
|
stringTransmitted = completedRequest->stringToTransmit;
|
|
hostTransmitted = completedRequest->host;
|
|
contentOffset = completedRequest->contentOffset;
|
|
*userData = completedRequest->userData;
|
|
|
|
RakNet::OP_DELETE(completedRequest, _FILE_AND_LINE_);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
completedRequestsMutex.Unlock();
|
|
}
|
|
return false;
|
|
}
|
|
bool HTTPConnection2::IsBusy(void) const
|
|
{
|
|
return pendingRequests.Size()>0 || sentRequests.Size()>0;
|
|
}
|
|
bool HTTPConnection2::HasResponse(void) const
|
|
{
|
|
return completedRequests.Size()>0;
|
|
}
|
|
int ReadChunkSize( char *txtStart, char **txtEnd )
|
|
{
|
|
// char lengthStr[32];
|
|
// memset(lengthStr, 0, 32);
|
|
// memcpy(lengthStr, txtStart, txtEnd - txtStart);
|
|
return strtoul(txtStart, txtEnd,16);
|
|
// return atoi(lengthStr);
|
|
}
|
|
void ReadChunkBlock( size_t ¤tChunkSize, size_t &bytesReadSoFar, char *txtIn, RakString &txtOut)
|
|
{
|
|
size_t bytesToRead;
|
|
size_t sLen;
|
|
|
|
do
|
|
{
|
|
bytesToRead = currentChunkSize - bytesReadSoFar;
|
|
sLen = strlen(txtIn);
|
|
if (sLen < bytesToRead)
|
|
bytesToRead = sLen;
|
|
txtOut.AppendBytes(txtIn, bytesToRead);
|
|
txtIn += bytesToRead;
|
|
bytesReadSoFar += bytesToRead;
|
|
if (*txtIn == 0)
|
|
{
|
|
// currentChunkSize=0;
|
|
return;
|
|
}
|
|
// char *newLine = strstr(txtIn, "\r\n");
|
|
if (txtIn[0] && txtIn[0]=='\r' && txtIn[1] && txtIn[1]=='\n' )
|
|
txtIn += 2; // Newline
|
|
char *newLine;
|
|
currentChunkSize = ReadChunkSize(txtIn, &newLine);
|
|
RakAssert(currentChunkSize < 50000); // Sanity check
|
|
if (currentChunkSize == 0)
|
|
return;
|
|
if (newLine == 0)
|
|
return;
|
|
bytesReadSoFar=0;
|
|
txtIn = newLine + 2;
|
|
} while (txtIn);
|
|
}
|
|
PluginReceiveResult HTTPConnection2::OnReceive(Packet *packet)
|
|
{
|
|
unsigned int i;
|
|
|
|
bool locked=true;
|
|
sentRequestsMutex.Lock();
|
|
for (i=0; i < sentRequests.Size(); i++)
|
|
{
|
|
Request *sentRequest = sentRequests[i];
|
|
if (sentRequest->hostCompletedAddress==packet->systemAddress)
|
|
{
|
|
sentRequests.RemoveAtIndexFast(i);
|
|
locked=false;
|
|
sentRequestsMutex.Unlock();
|
|
|
|
/*
|
|
static FILE * pFile = 0;
|
|
if (pFile==0)
|
|
{
|
|
long lSize;
|
|
char * buffer;
|
|
size_t result;
|
|
|
|
pFile = fopen ( "string_received.txt" , "rb" );
|
|
if (pFile==NULL) {fputs ("File error",stderr); exit (1);}
|
|
|
|
// obtain file size:
|
|
fseek (pFile , 0 , SEEK_END);
|
|
lSize = ftell (pFile);
|
|
rewind (pFile);
|
|
|
|
// allocate memory to contain the whole file:
|
|
buffer = (char*) malloc (sizeof(char)*lSize);
|
|
if (buffer == NULL) {fputs ("Memory error",stderr); exit (2);}
|
|
|
|
// copy the file into the buffer:
|
|
result = fread (buffer,1,lSize,pFile);
|
|
if (result != lSize) {fputs ("Reading error",stderr); exit (3);}
|
|
|
|
packet->data=(unsigned char*) buffer;
|
|
packet->length=lSize;
|
|
}
|
|
*/
|
|
|
|
|
|
const char *isFirstChunk = strstr((char*) packet->data, "Transfer-Encoding: chunked");
|
|
if (isFirstChunk)
|
|
{
|
|
//printf((char*) packet->data);
|
|
|
|
locked=false;
|
|
sentRequestsMutex.Unlock();
|
|
|
|
sentRequest->chunked = true;
|
|
char *chunkStrStart = strstr((char*) packet->data, "\r\n\r\n");
|
|
RakAssert(chunkStrStart);
|
|
|
|
chunkStrStart += 4; // strlen("\r\n\r\n");
|
|
char *body_header; // = strstr(chunkStrStart, "\r\n");
|
|
sentRequest->thisChunkSize = ReadChunkSize(chunkStrStart, &body_header);
|
|
sentRequest->bytesReadForThisChunk = 0;
|
|
sentRequest->contentOffset = 0;
|
|
|
|
if (sentRequest->thisChunkSize == 0)
|
|
{
|
|
// Done
|
|
completedRequestsMutex.Lock();
|
|
completedRequests.Push(sentRequest, _FILE_AND_LINE_);
|
|
completedRequestsMutex.Unlock();
|
|
|
|
// If there is another command waiting for this server, send it
|
|
SendPendingRequestToConnectedSystem(packet->systemAddress);
|
|
}
|
|
else
|
|
{
|
|
|
|
// char *offset = strstr((char*) packet->data+1, "2000");
|
|
|
|
body_header+=2;
|
|
ReadChunkBlock(sentRequest->thisChunkSize, sentRequest->bytesReadForThisChunk, body_header, sentRequest->stringReceived);
|
|
|
|
if (sentRequest->thisChunkSize==0)
|
|
{
|
|
// Done
|
|
completedRequestsMutex.Lock();
|
|
completedRequests.Push(sentRequest, _FILE_AND_LINE_);
|
|
completedRequestsMutex.Unlock();
|
|
|
|
// If there is another command waiting for this server, send it
|
|
SendPendingRequestToConnectedSystem(packet->systemAddress);
|
|
}
|
|
else
|
|
{
|
|
// Not done
|
|
sentRequestsMutex.Lock();
|
|
sentRequests.Push(sentRequest, _FILE_AND_LINE_);
|
|
sentRequestsMutex.Unlock();
|
|
}
|
|
}
|
|
}
|
|
else if (sentRequest->chunked)
|
|
{
|
|
ReadChunkBlock(sentRequest->thisChunkSize, sentRequest->bytesReadForThisChunk, (char*) packet->data, sentRequest->stringReceived);
|
|
|
|
if (sentRequest->thisChunkSize==0)
|
|
{
|
|
// Done
|
|
completedRequestsMutex.Lock();
|
|
completedRequests.Push(sentRequest, _FILE_AND_LINE_);
|
|
completedRequestsMutex.Unlock();
|
|
|
|
// If there is another command waiting for this server, send it
|
|
SendPendingRequestToConnectedSystem(packet->systemAddress);
|
|
}
|
|
else
|
|
{
|
|
// Not done
|
|
sentRequestsMutex.Lock();
|
|
sentRequests.Push(sentRequest, _FILE_AND_LINE_);
|
|
sentRequestsMutex.Unlock();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
sentRequest->stringReceived+=packet->data;
|
|
|
|
if (sentRequest->contentLength==-1)
|
|
{
|
|
const char *length_header = strstr(sentRequest->stringReceived.C_String(), "Content-Length: ");
|
|
if(length_header)
|
|
{
|
|
length_header += 16; // strlen("Content-Length: ");
|
|
|
|
unsigned int clLength;
|
|
for (clLength=0; length_header[clLength] && length_header[clLength] >= '0' && length_header[clLength] <= '9'; clLength++)
|
|
;
|
|
if (clLength>0 && (length_header[clLength]=='\r' || length_header[clLength]=='\n'))
|
|
{
|
|
sentRequest->contentLength = RakString::ReadIntFromSubstring(length_header, 0, clLength);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we know the content length, find \r\n\r\n
|
|
if (sentRequest->contentLength != -1)
|
|
{
|
|
if (sentRequest->contentLength > 0)
|
|
{
|
|
const char *body_header = strstr(sentRequest->stringReceived.C_String(), "\r\n\r\n");
|
|
if (body_header)
|
|
{
|
|
body_header += 4; // strlen("\r\n\r\n");
|
|
size_t slen = strlen(body_header);
|
|
//RakAssert(slen <= (size_t) sentRequest->contentLength);
|
|
if (slen >= (size_t) sentRequest->contentLength)
|
|
{
|
|
sentRequest->contentOffset = body_header - sentRequest->stringReceived.C_String();
|
|
completedRequestsMutex.Lock();
|
|
completedRequests.Push(sentRequest, _FILE_AND_LINE_);
|
|
completedRequestsMutex.Unlock();
|
|
|
|
// If there is another command waiting for this server, send it
|
|
SendPendingRequestToConnectedSystem(packet->systemAddress);
|
|
}
|
|
else
|
|
{
|
|
sentRequestsMutex.Lock();
|
|
sentRequests.Push(sentRequest, _FILE_AND_LINE_);
|
|
sentRequestsMutex.Unlock();
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
sentRequestsMutex.Lock();
|
|
sentRequests.Push(sentRequest, _FILE_AND_LINE_);
|
|
sentRequestsMutex.Unlock();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sentRequest->contentOffset=-1;
|
|
completedRequestsMutex.Lock();
|
|
completedRequests.Push(sentRequest, _FILE_AND_LINE_);
|
|
completedRequestsMutex.Unlock();
|
|
|
|
// If there is another command waiting for this server, send it
|
|
SendPendingRequestToConnectedSystem(packet->systemAddress);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const char *firstNewlineSet = strstr(sentRequest->stringReceived.C_String(), "\r\n\r\n");
|
|
if (firstNewlineSet!=0)
|
|
{
|
|
int offset = firstNewlineSet - sentRequest->stringReceived.C_String();
|
|
if (sentRequest->stringReceived.C_String()[offset+4]==0)
|
|
sentRequest->contentOffset=-1;
|
|
else
|
|
sentRequest->contentOffset=offset+4;
|
|
completedRequestsMutex.Lock();
|
|
completedRequests.Push(sentRequest, _FILE_AND_LINE_);
|
|
completedRequestsMutex.Unlock();
|
|
|
|
// If there is another command waiting for this server, send it
|
|
SendPendingRequestToConnectedSystem(packet->systemAddress);
|
|
}
|
|
else
|
|
{
|
|
sentRequestsMutex.Lock();
|
|
sentRequests.Push(sentRequest, _FILE_AND_LINE_);
|
|
sentRequestsMutex.Unlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (locked==true)
|
|
sentRequestsMutex.Unlock();
|
|
|
|
return RR_CONTINUE_PROCESSING;
|
|
}
|
|
|
|
void HTTPConnection2::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming)
|
|
{
|
|
(void) rakNetGUID;
|
|
(void) isIncoming; // unknown
|
|
|
|
SendPendingRequestToConnectedSystem(systemAddress);
|
|
}
|
|
void HTTPConnection2::SendPendingRequestToConnectedSystem(SystemAddress sa)
|
|
{
|
|
if (sa==UNASSIGNED_SYSTEM_ADDRESS)
|
|
return;
|
|
|
|
unsigned int requestsSent=0;
|
|
|
|
// Search through requests to find a match for this instance of TCPInterface and SystemAddress
|
|
unsigned int i;
|
|
i=0;
|
|
pendingRequestsMutex.Lock();
|
|
while (i < pendingRequests.Size())
|
|
{
|
|
Request *request = pendingRequests[i];
|
|
if (request->hostEstimatedAddress==sa)
|
|
{
|
|
pendingRequests.RemoveAtIndex(i);
|
|
// Send this request
|
|
request->hostCompletedAddress=sa;
|
|
|
|
sentRequestsMutex.Lock();
|
|
sentRequests.Push(request, _FILE_AND_LINE_);
|
|
sentRequestsMutex.Unlock();
|
|
|
|
pendingRequestsMutex.Unlock();
|
|
|
|
#if OPEN_SSL_CLIENT_SUPPORT==1
|
|
if (request->useSSL)
|
|
tcpInterface->StartSSLClient(sa);
|
|
#endif
|
|
|
|
SendRequest(request);
|
|
requestsSent++;
|
|
pendingRequestsMutex.Lock();
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
i++;
|
|
}
|
|
}
|
|
pendingRequestsMutex.Unlock();
|
|
|
|
if (requestsSent==0)
|
|
{
|
|
pendingRequestsMutex.Lock();
|
|
if (pendingRequests.Size() > 0)
|
|
{
|
|
// Just assign
|
|
Request *request = pendingRequests[0];
|
|
pendingRequests.RemoveAtIndex(0);
|
|
|
|
request->hostCompletedAddress=sa;
|
|
|
|
sentRequestsMutex.Lock();
|
|
sentRequests.Push(request, _FILE_AND_LINE_);
|
|
sentRequestsMutex.Unlock();
|
|
pendingRequestsMutex.Unlock();
|
|
|
|
// Send
|
|
#if OPEN_SSL_CLIENT_SUPPORT==1
|
|
if (request->useSSL)
|
|
tcpInterface->StartSSLClient(sa);
|
|
#endif
|
|
|
|
|
|
SendRequest(request);
|
|
}
|
|
else
|
|
{
|
|
pendingRequestsMutex.Unlock();
|
|
}
|
|
}
|
|
}
|
|
void HTTPConnection2::RemovePendingRequest(SystemAddress sa)
|
|
{
|
|
unsigned int i;
|
|
i=0;
|
|
pendingRequestsMutex.Lock();
|
|
for (i=0; i < pendingRequests.Size(); i++)
|
|
{
|
|
Request *request = pendingRequests[i];
|
|
if (request->hostEstimatedAddress==sa)
|
|
{
|
|
pendingRequests.RemoveAtIndex(i);
|
|
RakNet::OP_DELETE(request, _FILE_AND_LINE_);
|
|
}
|
|
else
|
|
i++;
|
|
}
|
|
|
|
pendingRequestsMutex.Unlock();
|
|
}
|
|
void HTTPConnection2::SendNextPendingRequest(void)
|
|
{
|
|
// Send a pending request
|
|
pendingRequestsMutex.Lock();
|
|
if (pendingRequests.Size()>0)
|
|
{
|
|
Request *pendingRequest = pendingRequests.Peek();
|
|
pendingRequestsMutex.Unlock();
|
|
|
|
if (pendingRequest->ipVersion!=6)
|
|
{
|
|
tcpInterface->Connect(pendingRequest->host.C_String(), pendingRequest->port, false, AF_INET);
|
|
}
|
|
else
|
|
{
|
|
#if RAKNET_SUPPORT_IPV6
|
|
tcpInterface->Connect(pendingRequest->host.C_String(), pendingRequest->port, false, AF_INET6);
|
|
#else
|
|
RakAssert("HTTPConnection2::TransmitRequest needs define RAKNET_SUPPORT_IPV6" && 0);
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pendingRequestsMutex.Unlock();
|
|
}
|
|
}
|
|
|
|
void HTTPConnection2::OnFailedConnectionAttempt(Packet *packet, PI2_FailedConnectionAttemptReason failedConnectionAttemptReason)
|
|
{
|
|
(void) failedConnectionAttemptReason;
|
|
if (packet->systemAddress==UNASSIGNED_SYSTEM_ADDRESS)
|
|
return;
|
|
|
|
RemovePendingRequest(packet->systemAddress);
|
|
|
|
SendNextPendingRequest();
|
|
}
|
|
void HTTPConnection2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
|
|
{
|
|
(void) lostConnectionReason;
|
|
(void) rakNetGUID;
|
|
|
|
if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS)
|
|
return;
|
|
|
|
// Update sent requests to completed requests
|
|
unsigned int i;
|
|
i=0;
|
|
sentRequestsMutex.Lock();
|
|
while (i < sentRequests.Size())
|
|
{
|
|
if (sentRequests[i]->hostCompletedAddress==systemAddress)
|
|
{
|
|
Request *sentRequest = sentRequests[i];
|
|
if (sentRequest->chunked==false && sentRequest->stringReceived.IsEmpty()==false)
|
|
{
|
|
if (strstr(sentRequest->stringReceived.C_String(), "Content-Length: "))
|
|
{
|
|
char *body_header = strstr((char*) sentRequest->stringReceived.C_String(), "\r\n\r\n");
|
|
if (body_header)
|
|
{
|
|
body_header += 4; // strlen("\r\n\r\n");
|
|
sentRequest->contentOffset = body_header - sentRequest->stringReceived.C_String();
|
|
}
|
|
else
|
|
{
|
|
sentRequest->contentOffset = 0;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
sentRequest->contentOffset = 0;
|
|
}
|
|
}
|
|
|
|
|
|
completedRequestsMutex.Lock();
|
|
completedRequests.Push(sentRequests[i], _FILE_AND_LINE_);
|
|
completedRequestsMutex.Unlock();
|
|
|
|
sentRequests.RemoveAtIndexFast(i);
|
|
}
|
|
else
|
|
{
|
|
i++;
|
|
}
|
|
}
|
|
sentRequestsMutex.Unlock();
|
|
|
|
SendNextPendingRequest();
|
|
}
|
|
bool HTTPConnection2::IsConnected(SystemAddress sa)
|
|
{
|
|
SystemAddress remoteSystems[64];
|
|
unsigned short numberOfSystems=64;
|
|
tcpInterface->GetConnectionList(remoteSystems, &numberOfSystems);
|
|
for (unsigned int i=0; i < numberOfSystems; i++)
|
|
{
|
|
if (remoteSystems[i]==sa)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
void HTTPConnection2::SendRequest(Request *request)
|
|
{
|
|
tcpInterface->Send(request->stringToTransmit.C_String(), (unsigned int) request->stringToTransmit.GetLength(), request->hostCompletedAddress, false);
|
|
}
|
|
|
|
#endif // #if _RAKNET_SUPPORT_HTTPConnection2==1 && _RAKNET_SUPPORT_TCPInterface==1
|