Darwin-Streaming-Server/RTSPClientLib/ClientSocket.cpp

359 lines
11 KiB
C++
Raw Normal View History

/*
*
* @APPLE_LICENSE_HEADER_START@
*
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*
*/
/*
File: ClientSocket.cpp
*/
#ifndef __Win32__
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/tcp.h>
#include <sys/uio.h>
#endif
#include "ClientSocket.h"
#include "OSMemory.h"
#include "base64.h"
#include "MyAssert.h"
#define CLIENT_SOCKET_DEBUG 0
ClientSocket::ClientSocket()
: fHostAddr(0),
fHostPort(0),
fEventMask(0),
fSocketP(NULL),
fSendBuffer(fSendBuf, 0),
fSentLength(0)
{}
OS_Error ClientSocket::Open(TCPSocket* inSocket)
{
OS_Error theErr = OS_NoErr;
if (!inSocket->IsBound())
{
theErr = inSocket->Open();
if (theErr == OS_NoErr)
theErr = inSocket->Bind(0, 0);
if (theErr != OS_NoErr)
return theErr;
inSocket->NoDelay();
#if __FreeBSD__ || __MacOSX__
// no KeepAlive -- probably should be off for all platforms.
#else
inSocket->KeepAlive();
#endif
}
return theErr;
}
OS_Error ClientSocket::Connect(TCPSocket* inSocket)
{
OS_Error theErr = this->Open(inSocket);
Assert(theErr == OS_NoErr);
if (theErr != OS_NoErr)
return theErr;
if (!inSocket->IsConnected())
{
theErr = inSocket->Connect(fHostAddr, fHostPort);
if ((theErr == EINPROGRESS) || (theErr == EAGAIN))
{
fSocketP = inSocket;
fEventMask = EV_RE | EV_WR;
return theErr;
}
}
return theErr;
}
OS_Error ClientSocket::Send(char* inData, const UInt32 inLength)
{
iovec theVec[1];
theVec[0].iov_base = (char*)inData;
theVec[0].iov_len = inLength;
return this->SendV(theVec, 1);
}
OS_Error ClientSocket::SendSendBuffer(TCPSocket* inSocket)
{
OS_Error theErr = OS_NoErr;
UInt32 theLengthSent = 0;
if (fSendBuffer.Len == 0)
return OS_NoErr;
do
{
// theLengthSent should be reset to zero before passing its pointer to Send function
// otherwise the old value will be used and it will go into an infinite loop sometimes
theLengthSent = 0;
//
// Loop, trying to send the entire message.
theErr = inSocket->Send(fSendBuffer.Ptr + fSentLength, fSendBuffer.Len - fSentLength, &theLengthSent);
fSentLength += theLengthSent;
} while (theLengthSent > 0);
if (theErr == OS_NoErr)
fSendBuffer.Len = fSentLength = 0; // Message was sent
else
{
// Message wasn't entirely sent. Caller should wait for a read event on the POST socket
fSocketP = inSocket;
fEventMask = EV_WR;
}
return theErr;
}
TCPClientSocket::TCPClientSocket(UInt32 inSocketType)
: fSocket(NULL, inSocketType)
{
//
// It is necessary to open the socket right when we construct the
// object because the QTSSSplitterModule that uses this class uses
// the socket file descriptor in the QTSS_CreateStreamFromSocket call.
fSocketP = &fSocket;
this->Open(&fSocket);
}
void TCPClientSocket::SetOptions(int sndBufSize,int rcvBufSize)
{ //set options on the socket
//qtss_printf("TCPClientSocket::SetOptions sndBufSize=%d,rcvBuf=%d,keepAlive=%d,noDelay=%d\n",sndBufSize,rcvBufSize,(int)keepAlive,(int)noDelay);
int err = 0;
err = ::setsockopt(fSocket.GetSocketFD(), SOL_SOCKET, SO_SNDBUF, (char*)&sndBufSize, sizeof(int));
AssertV(err == 0, OSThread::GetErrno());
err = ::setsockopt(fSocket.GetSocketFD(), SOL_SOCKET, SO_RCVBUF, (char*)&rcvBufSize, sizeof(int));
AssertV(err == 0, OSThread::GetErrno());
#if __FreeBSD__ || __MacOSX__
struct timeval time;
//int len = sizeof(time);
time.tv_sec = 0;
time.tv_usec = 0;
err = ::setsockopt(fSocket.GetSocketFD(), SOL_SOCKET, SO_RCVTIMEO, (char*)&time, sizeof(time));
AssertV(err == 0, OSThread::GetErrno());
err = ::setsockopt(fSocket.GetSocketFD(), SOL_SOCKET, SO_SNDTIMEO, (char*)&time, sizeof(time));
AssertV(err == 0, OSThread::GetErrno());
#endif
}
OS_Error TCPClientSocket::SendV(iovec* inVec, UInt32 inNumVecs)
{
if (fSendBuffer.Len == 0)
{
for (UInt32 count = 0; count < inNumVecs; count++)
{
::memcpy(fSendBuffer.Ptr + fSendBuffer.Len, inVec[count].iov_base, inVec[count].iov_len);
fSendBuffer.Len += inVec[count].iov_len;
Assert(fSendBuffer.Len < ClientSocket::kSendBufferLen);
}
}
OS_Error theErr = this->Connect(&fSocket);
if (theErr != OS_NoErr)
return theErr;
return this->SendSendBuffer(&fSocket);
}
OS_Error TCPClientSocket::Read(void* inBuffer, const UInt32 inLength, UInt32* outRcvLen)
{
this->Connect(&fSocket);
OS_Error theErr = fSocket.Read(inBuffer, inLength, outRcvLen);
if (theErr != OS_NoErr)
fEventMask = EV_RE;
return theErr;
}
HTTPClientSocket::HTTPClientSocket(const StrPtrLen& inURL, UInt32 inCookie, UInt32 inSocketType)
: fCookie(inCookie),
fSocketType(inSocketType),
fGetReceived(0),
fGetSocket(NULL, inSocketType),
fPostSocket(NULL)
{
fURL.Ptr = NEW char[inURL.Len + 1];
fURL.Len = inURL.Len;
::memcpy(fURL.Ptr, inURL.Ptr, inURL.Len);
fURL.Ptr[fURL.Len] = '\0';
}
HTTPClientSocket::~HTTPClientSocket()
{
delete [] fURL.Ptr;
delete fPostSocket;
}
OS_Error HTTPClientSocket::Read(void* inBuffer, const UInt32 inLength, UInt32* outRcvLen)
{
//
// Bring up the GET connection if we need to
if (!fGetSocket.IsConnected())
{
#if CLIENT_SOCKET_DEBUG
qtss_printf("HTTPClientSocket::Read: Sending GET\n");
#endif
qtss_sprintf(fSendBuffer.Ptr, "GET %s HTTP/1.0\r\nX-SessionCookie: %"_U32BITARG_"\r\nAccept: application/x-rtsp-rtp-interleaved\r\nUser-Agent: QTSS/2.0\r\n\r\n", fURL.Ptr, fCookie);
fSendBuffer.Len = ::strlen(fSendBuffer.Ptr);
Assert(fSentLength == 0);
}
OS_Error theErr = this->Connect(&fGetSocket);
if (theErr != OS_NoErr)
return theErr;
if (fSendBuffer.Len > 0)
{
theErr = this->SendSendBuffer(&fGetSocket);
if (theErr != OS_NoErr)
return theErr;
fSentLength = 1; // So we know to execute the receive code below.
}
// We are done sending the GET. If we need to receive the GET response, do that here
if (fSentLength > 0)
{
*outRcvLen = 0;
do
{
// Loop, trying to receive the entire response.
theErr = fGetSocket.Read(&fSendBuffer.Ptr[fGetReceived], kSendBufferLen - fGetReceived, outRcvLen);
fGetReceived += *outRcvLen;
// Check to see if we've gotten a \r\n\r\n. If we have, then we've received
// the entire GET
fSendBuffer.Ptr[fGetReceived] = '\0';
char* theGetEnd = ::strstr(fSendBuffer.Ptr, "\r\n\r\n");
if (theGetEnd != NULL)
{
// We got the entire GET response, so we are ready to move onto
// real RTSP response data. First skip past the \r\n\r\n
theGetEnd += 4;
#if CLIENT_SOCKET_DEBUG
qtss_printf("HTTPClientSocket::Read: Received GET response\n");
#endif
// Whatever remains is part of an RTSP request, so move that to
// the beginning of the buffer and blow away the GET
*outRcvLen = fGetReceived - (theGetEnd - fSendBuffer.Ptr);
::memcpy(inBuffer, theGetEnd, *outRcvLen);
fGetReceived = fSentLength = 0;
return OS_NoErr;
}
Assert(fGetReceived < inLength);
} while (*outRcvLen > 0);
#if CLIENT_SOCKET_DEBUG
qtss_printf("HTTPClientSocket::Read: Waiting for GET response\n");
#endif
// Message wasn't entirely received. Caller should wait for a read event on the GET socket
Assert(theErr != OS_NoErr);
fSocketP = &fGetSocket;
fEventMask = EV_RE;
return theErr;
}
theErr = fGetSocket.Read(&((char*)inBuffer)[fGetReceived], inLength - fGetReceived, outRcvLen);
if (theErr != OS_NoErr)
{
#if CLIENT_SOCKET_DEBUG
//qtss_printf("HTTPClientSocket::Read: Waiting for data\n");
#endif
fSocketP = &fGetSocket;
fEventMask = EV_RE;
}
#if CLIENT_SOCKET_DEBUG
//else
//qtss_printf("HTTPClientSocket::Read: Got some data\n");
#endif
return theErr;
}
OS_Error HTTPClientSocket::SendV(iovec* inVec, UInt32 inNumVecs)
{
//
// Bring up the POST connection if we need to
if (fPostSocket == NULL)
fPostSocket = NEW TCPSocket(NULL, fSocketType);
if (!fPostSocket->IsConnected())
{
#if CLIENT_SOCKET_DEBUG
qtss_printf("HTTPClientSocket::Send: Sending POST\n");
#endif
qtss_sprintf(fSendBuffer.Ptr, "POST %s HTTP/1.0\r\nX-SessionCookie: %"_U32BITARG_"\r\nAccept: application/x-rtsp-rtp-interleaved\r\nUser-Agent: QTSS/2.0\r\n\r\n", fURL.Ptr, fCookie);
fSendBuffer.Len = ::strlen(fSendBuffer.Ptr);
this->EncodeVec(inVec, inNumVecs);
}
OS_Error theErr = this->Connect(fPostSocket);
if (theErr != OS_NoErr)
return theErr;
//
// If we have nothing to send currently, this should be a new message, in which case
// we can encode it and send it
if (fSendBuffer.Len == 0)
this->EncodeVec(inVec, inNumVecs);
#if CLIENT_SOCKET_DEBUG
//qtss_printf("HTTPClientSocket::Send: Sending data\n");
#endif
return this->SendSendBuffer(fPostSocket);
}
void HTTPClientSocket::EncodeVec(iovec* inVec, UInt32 inNumVecs)
{
for (UInt32 count = 0; count < inNumVecs; count++)
{
fSendBuffer.Len += ::Base64encode(fSendBuffer.Ptr + fSendBuffer.Len, (char*)inVec[count].iov_base, inVec[count].iov_len);
Assert(fSendBuffer.Len < ClientSocket::kSendBufferLen);
fSendBuffer.Len = ::strlen(fSendBuffer.Ptr); //Don't trust what the above function returns for a length
}
}