1
0
mirror of https://github.com/stefanocasazza/ULib.git synced 2025-09-28 19:05:55 +08:00
ULib/src/ulib/net/server/client_image.cpp

2141 lines
65 KiB
C++

// ============================================================================
//
// = LIBRARY
// ULib - c++ library
//
// = FILENAME
// client_image.cpp - Handles accepted TCP/IP connections
//
// = AUTHOR
// Stefano Casazza
//
// ============================================================================
#include <ulib/utility/uhttp.h>
#include <ulib/utility/websocket.h>
#ifndef U_HTTP2_DISABLE
# include <ulib/utility/http2.h>
#endif
#ifdef U_SERVER_CHECK_TIME_BETWEEN_REQUEST
# include <ulib/net/client/client.h>
# define U_NUM_CLIENT_THRESHOLD 128
#endif
#ifdef HAVE_SCHED_GETCPU
# include <sched.h>
#endif
bool UClientImage_Base::bIPv6;
bool UClientImage_Base::bnoheader;
bool UClientImage_Base::bsendGzipBomb;
char UClientImage_Base::cbuffer[128];
long UClientImage_Base::time_run;
long UClientImage_Base::time_between_request = 10;
uint32_t UClientImage_Base::resto;
uint32_t UClientImage_Base::rstart;
uint32_t UClientImage_Base::nrequest;
uint32_t UClientImage_Base::size_request;
UString* UClientImage_Base::body;
UString* UClientImage_Base::rbuffer;
UString* UClientImage_Base::wbuffer;
UString* UClientImage_Base::request;
UString* UClientImage_Base::request_uri;
UString* UClientImage_Base::environment;
UTimeVal* UClientImage_Base::chronometer;
struct iovec UClientImage_Base::iov_vec[4];
iPF UClientImage_Base::callerHandlerRead = UServer_Base::pluginsHandlerREAD;
vPF UClientImage_Base::callerHandlerRequest = UServer_Base::pluginsHandlerRequest;
bPF UClientImage_Base::callerHandlerCache = handlerCache;
bPFpc UClientImage_Base::callerIsValidMethod = isValidMethod;
bPFpcu UClientImage_Base::callerIsValidRequest = isValidRequest;
bPFpcu UClientImage_Base::callerIsValidRequestExt = isValidRequestExt;
// NB: these are for ULib Servlet Page (USP) - USP_PRINTF...
UString* UClientImage_Base::usp_value;
UString* UClientImage_Base::usp_buffer;
UString* UClientImage_Base::usp_encoded;
#ifndef U_LOG_DISABLE
int UClientImage_Base::log_request_partial;
void UClientImage_Base::logRequest()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::logRequest()")
U_INTERNAL_ASSERT(*request)
U_INTERNAL_ASSERT_POINTER(logbuf)
U_INTERNAL_ASSERT(UServer_Base::isLog())
U_INTERNAL_ASSERT(logbuf->isNullTerminated())
const char* str_partial = "";
const char* ptr = request->data();
uint32_t str_partial_len = 0, sz = request->size();
int32_t u_printf_string_max_length_save = u_printf_string_max_length;
U_INTERNAL_DUMP("u_printf_string_max_length = %d", u_printf_string_max_length)
if (u_printf_string_max_length == -1 &&
u_isPrintable(ptr, sz, true))
{
u_printf_string_max_length = u_findEndHeader1(ptr, sz);
if (u_printf_string_max_length == -1)
{
str_partial = "[partial] ";
str_partial_len = U_CONSTANT_SIZE("[partial] ");
log_request_partial = UEventFd::fd;
u_printf_string_max_length = U_min(sz,1000);
}
else
{
if (u_printf_string_max_length < (int)U_CONSTANT_SIZE("GET / HTTP/1.0\r\n\r\n")) u_printf_string_max_length = 128;
if (log_request_partial == UEventFd::fd)
{
str_partial = "[complete] ";
str_partial_len = U_CONSTANT_SIZE("[complete] ");
log_request_partial = 0;
}
}
U_INTERNAL_ASSERT_MAJOR(u_printf_string_max_length, 0)
}
U_INTERNAL_DUMP("u_printf_string_max_length = %d U_ClientImage_pipeline = %b", u_printf_string_max_length, U_ClientImage_pipeline)
UServer_Base::log->log(U_CONSTANT_TO_PARAM("received request (%u bytes) %.*s%.*s%#.*S from %v"), sz,
(U_ClientImage_pipeline ? U_CONSTANT_SIZE("[pipeline] ") : 0), "[pipeline] ",
str_partial_len, str_partial,
sz, ptr, logbuf->rep);
u_printf_string_max_length = u_printf_string_max_length_save;
}
#endif
UClientImage_Base::UClientImage_Base()
{
U_TRACE_CTOR(0, UClientImage_Base, "")
socket = U_NULLPTR;
logbuf = U_NULLPTR;
data_pending = U_NULLPTR;
if (UServer_Base::isLog()) U_NEW_STRING(logbuf, UString(200U));
reset();
flag.u = 0;
last_event = u_now->tv_sec;
// NB: array are not pointers (virtual table can shift the address of 'this')...
if (UServer_Base::pClientImage == U_NULLPTR) UServer_Base::eClientImage = (UServer_Base::pClientImage = this) + UNotifier::max_connection;
}
UClientImage_Base::~UClientImage_Base()
{
U_TRACE_DTOR(0, UClientImage_Base)
// NB: array are not pointers (virtual table can shift the address of 'this')...
U_DELETE(socket)
if (logbuf)
{
U_CHECK_MEMORY_OBJECT(logbuf->rep)
// if (logbuf->rep->memory.invariant() == false) logbuf->rep->memory._this = (void*)U_CHECK_MEMORY_SENTINEL;
U_DELETE(logbuf)
}
}
void UClientImage_Base::set()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::set()")
U_INTERNAL_DUMP("this = %p socket = %p UEventFd::fd = %d", this, socket, UEventFd::fd)
U_INTERNAL_ASSERT_POINTER(socket)
U_INTERNAL_ASSERT_POINTER(UServer_Base::socket)
if (UServer_Base::bipc == false &&
UServer_Base::socket->isLocalSet())
{
socket->cLocalAddress.set(UServer_Base::socket->cLocalAddress);
}
socket->flags |= O_CLOEXEC;
if (USocket::accept4_flags & SOCK_NONBLOCK) socket->flags |= O_NONBLOCK;
#ifdef DEBUG
U_CHECK_MEMORY
U_CHECK_MEMORY_OBJECT(socket)
if (logbuf) U_CHECK_MEMORY_OBJECT(logbuf->rep)
uint32_t index = (this - UServer_Base::pClientImage);
if (index)
{
UClientImage_Base* ptr = UServer_Base::pClientImage + index-1;
U_CHECK_MEMORY_OBJECT(ptr)
U_CHECK_MEMORY_OBJECT(ptr->socket)
if (logbuf)
{
U_INTERNAL_DUMP("ptr->logbuf = %p ptr->logbuf->rep = %p ptr->logbuf->rep->memory._this = %p",
ptr->logbuf, ptr->logbuf->rep, ptr->logbuf->rep->memory._this)
U_CHECK_MEMORY_OBJECT(ptr->logbuf->rep)
if (index == (UNotifier::max_connection-1))
{
for (index = 0, ptr = UServer_Base::pClientImage; index < UNotifier::max_connection; ++index, ++ptr) (void) ptr->check_memory();
}
}
}
#endif
}
// ------------------------------------------------------------------------
#ifdef DEBUG
bool UClientImage_Base::check_memory()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::check_memory()")
U_INTERNAL_DUMP("u_check_memory_vector<T>: elem %u of %u", this-UServer_Base::pClientImage, UNotifier::max_connection)
U_INTERNAL_DUMP("this = %p socket = %p UEventFd::fd = %d", this, socket, UEventFd::fd)
U_CHECK_MEMORY
U_CHECK_MEMORY_OBJECT(socket)
if (logbuf) U_CHECK_MEMORY_OBJECT(logbuf->rep)
U_RETURN(true);
}
#endif
#if defined(DEBUG) || (defined(U_SERVER_CAPTIVE_PORTAL) && !defined(ENABLE_THREAD))
void UClientImage_Base::saveRequestResponse()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::saveRequestResponse()")
# if defined(U_STDCPP_ENABLE) && !defined(U_HTTP2_DISABLE)
U_INTERNAL_DUMP("U_http_version = %C", U_http_version)
if (U_http_version == '2') U_DUMP_OBJECT_TO_TMP(UHTTP2::pConnection->itable, request)
else
#endif
{
if (*rbuffer) U_FILE_WRITE_TO_TMP(*rbuffer, "request.%P");
#ifdef DEBUG
if (U_http_info.nResponseCode) (void) UFile::writeToTmp(iov_vec, 4, O_RDWR | O_TRUNC, U_CONSTANT_TO_PARAM("response.%P"), 0);
#endif
}
}
#endif
void UClientImage_Base::init()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::init()")
U_INTERNAL_ASSERT_EQUALS(body, U_NULLPTR)
U_INTERNAL_ASSERT_EQUALS(rbuffer, U_NULLPTR)
U_INTERNAL_ASSERT_EQUALS(wbuffer, U_NULLPTR)
U_INTERNAL_ASSERT_EQUALS(request, U_NULLPTR)
U_INTERNAL_ASSERT_EQUALS(usp_value, U_NULLPTR)
U_INTERNAL_ASSERT_EQUALS(usp_buffer, U_NULLPTR)
U_INTERNAL_ASSERT_EQUALS(usp_encoded, U_NULLPTR)
U_INTERNAL_ASSERT_EQUALS(request_uri, U_NULLPTR)
U_NEW_STRING(body, UString);
U_NEW_STRING(rbuffer, UString(8192));
U_NEW_STRING(wbuffer, UString(U_CAPACITY));
U_NEW_STRING(request, UString);
U_NEW_STRING(request_uri, UString);
U_NEW_STRING(environment, UString(U_CAPACITY));
// NB: these are for ULib Servlet Page (USP) - USP_PRINTF...
U_NEW_STRING(usp_value, UString(U_CAPACITY));
U_NEW_STRING(usp_buffer, UString(U_CAPACITY));
U_NEW_STRING(usp_encoded, UString(U_CAPACITY));
U_NEW(UTimeVal, chronometer, UTimeVal);
chronometer->start();
#if defined(DEBUG) || (defined(U_SERVER_CAPTIVE_PORTAL) && !defined(ENABLE_THREAD))
UError::callerDataDump = saveRequestResponse;
#endif
}
void UClientImage_Base::clear()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::clear()")
U_INTERNAL_ASSERT_POINTER(body)
U_INTERNAL_ASSERT_POINTER(wbuffer)
U_INTERNAL_ASSERT_POINTER(request)
U_INTERNAL_ASSERT_POINTER(rbuffer)
U_INTERNAL_ASSERT_POINTER(request_uri)
if (body)
{
U_DELETE(body)
U_DELETE(wbuffer)
U_DELETE(request)
U_DELETE(rbuffer)
U_DELETE(request_uri)
U_DELETE(environment)
U_DELETE(chronometer)
// NB: these are for ULib Servlet Page (USP) - USP_PRINTF...
U_INTERNAL_ASSERT_POINTER(usp_value)
U_INTERNAL_ASSERT_POINTER(usp_buffer)
U_INTERNAL_ASSERT_POINTER(usp_encoded)
U_DELETE(usp_value)
U_DELETE(usp_buffer)
U_DELETE(usp_encoded)
}
}
// Check whether the ip address client ought to be allowed
__pure bool UClientImage_Base::isAllowed(UVector<UIPAllow*>& vallow_IP)
{
U_TRACE(0, "UClientImage_Base::isAllowed(%p)", &vallow_IP)
if (UIPAllow::isAllowed(UServer_Base::getClientAddress(), vallow_IP)) U_RETURN(true);
U_RETURN(false);
}
#ifndef U_CACHE_REQUEST_DISABLE
__pure bool UClientImage_Base::isRequestCacheable()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::isRequestCacheable()")
if ((U_ClientImage_request & NO_CACHE) == 0 &&
U_ClientImage_request_is_cached == false &&
U_http_info.startHeader <= sizeof(cbuffer))
{
U_RETURN(true);
}
U_RETURN(false);
}
#endif
// append on the log the peer certificate of client ("issuer","serial")
bool UClientImage_Base::logCertificate()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::logCertificate()")
// NB: OpenSSL has already tested the cert validity during SSL handshake and returns a X509 ptr just if the certificate is valid...
#if defined(USE_LIBSSL) && !defined(U_LOG_DISABLE)
X509* x509 = ((USSLSocket*)socket)->getPeerCertificate();
if (x509)
{
U_INTERNAL_ASSERT_POINTER(logbuf)
U_INTERNAL_ASSERT(UServer_Base::isLog())
UCertificate::setForLog(x509, *logbuf);
U_INTERNAL_ASSERT(logbuf->isNullTerminated())
U_INTERNAL_DUMP("logbuf = %V", logbuf->rep)
U_RETURN(true);
}
#endif
U_RETURN(false);
}
bool UClientImage_Base::askForClientCertificate()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::askForClientCertificate()")
#ifdef USE_LIBSSL
U_ASSERT(((USSLSocket*)socket)->isSSL())
if (((USSLSocket*)socket)->getPeerCertificate() == U_NULLPTR)
{
U_SRV_LOG_WITH_ADDR("Ask for a client certificate to");
if (((USSLSocket*)socket)->askForClientCertificate() == false) U_RETURN(false);
}
if (logCertificate()) U_RETURN(true);
#endif
U_RETURN(false);
}
void UClientImage_Base::setSendfile(int fd, off_t lstart, off_t lcount)
{
U_TRACE(0, "UClientImage_Base::setSendfile(%d,%I,%I)", fd, lstart, lcount)
U_INTERNAL_DUMP("U_http_version = %C", U_http_version)
U_ASSERT(body->empty())
U_INTERNAL_ASSERT_DIFFERS(fd, -1)
U_INTERNAL_ASSERT_MAJOR(lcount, 0)
U_INTERNAL_ASSERT_DIFFERS(U_http_version, '2')
setRequestNoCache();
UServer_Base::pClientImage->offset = lstart;
UServer_Base::pClientImage->count = lcount;
UServer_Base::pClientImage->sfd = fd;
}
// NB: we have default as true to manage pipeline for protocol as RPC...
U_NO_EXPORT inline bool UClientImage_Base::handlerCache() { return true; }
U_NO_EXPORT inline bool UClientImage_Base::isValidMethod( const char* ptr) { return true; }
U_NO_EXPORT inline bool UClientImage_Base::isValidRequest( const char* ptr, uint32_t sz) { return true; }
U_NO_EXPORT inline bool UClientImage_Base::isValidRequestExt(const char* ptr, uint32_t sz) { return true; }
uint32_t UClientImage_Base::checkRequestToCache()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::checkRequestToCache()")
U_INTERNAL_DUMP("U_ClientImage_request_is_cached = %b", U_ClientImage_request_is_cached)
#if !defined(U_CACHE_REQUEST_DISABLE) || defined(U_SERVER_CHECK_TIME_BETWEEN_REQUEST)
U_INTERNAL_ASSERT(U_ClientImage_request_is_cached)
uint32_t sz = request->size();
const char* ptr = request->data();
U_INTERNAL_DUMP("cbuffer(%u) = %.*S", U_http_info.startHeader, U_http_info.startHeader, cbuffer)
U_INTERNAL_DUMP("request(%u) = %.*S", sz, sz, ptr)
U_INTERNAL_DUMP("U_ClientImage_pipeline = %b size_request = %u U_http_uri_offset = %u", U_ClientImage_pipeline, size_request, U_http_uri_offset)
U_INTERNAL_ASSERT_MAJOR(size_request, 0)
U_INTERNAL_ASSERT_RANGE(1,U_http_uri_offset,254)
U_INTERNAL_ASSERT_MAJOR(U_http_info.uri_len, 0)
U_INTERNAL_ASSERT_MAJOR(U_http_info.startHeader, 0)
U_INTERNAL_ASSERT_EQUALS(U_ClientImage_data_missing, false)
if (u__isblank((ptr+U_http_uri_offset)[U_http_info.startHeader]) &&
memcmp(ptr+U_http_uri_offset, cbuffer, U_http_info.startHeader) == 0)
{
if (size_request > sz &&
(callerIsValidMethod( ptr) == false ||
callerIsValidRequest(ptr, sz) == false))
{
U_RETURN(1); // partial valid (not complete)
}
if (callerHandlerCache()) U_RETURN(2);
}
#endif
U_RETURN(0);
}
// define method VIRTUAL of class UEventFd
void UClientImage_Base::handlerDelete()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::handlerDelete()")
bool bsocket_open = isOpen();
#if !defined(USE_LIBEVENT) && defined(HAVE_EPOLL_WAIT) && defined(DEBUG)
if (UNLIKELY(UNotifier::num_connection <= UNotifier::min_connection))
{
U_WARNING("handlerDelete(): "
"UEventFd::fd = %d socket->iSockDesc = %d isOpen() = %b "
"UNotifier::num_connection = %d UNotifier::min_connection = %d "
"UServer_Base::isParallelizationChild() = %b sfd = %d UEventFd::op_mask = %B",
UEventFd::fd, socket->iSockDesc, bsocket_open, UNotifier::num_connection, UNotifier::min_connection, UServer_Base::isParallelizationChild(), sfd, UEventFd::op_mask);
return;
}
#endif
U_INTERNAL_ASSERT_DIFFERS(U_ClientImage_parallelization, U_PARALLELIZATION_CHILD)
bool bdelete = (U_ClientImage_state == U_NOTIFY_DELETE);
U_INTERNAL_DUMP("U_ClientImage_state = %d %B bdelete = %b bsocket_open = %b", U_ClientImage_state, U_ClientImage_state, bdelete, bsocket_open)
if (bdelete &&
bsocket_open)
{
socket->close();
bsocket_open = false;
}
#ifndef U_LOG_DISABLE
if (UServer_Base::isLog())
{
U_INTERNAL_ASSERT_POINTER(logbuf)
U_INTERNAL_DUMP("UEventFd::fd = %d logbuf = %V", UEventFd::fd, logbuf->rep)
char buffer[32];
uint32_t len = UServer_Base::setNumConnection(buffer);
const char* agent = (bdelete || bsocket_open ? "Client" : "Server");
UServer_Base::log->log(U_CONSTANT_TO_PARAM("%.6s close connection from %v, %.*s clients still connected"), agent, logbuf->rep, len, buffer);
# ifdef DEBUG
int fd_logbuf = ::strtoul(logbuf->data(), U_NULLPTR, 10);
if (UNLIKELY(fd_logbuf != UEventFd::fd))
{
U_WARNING("handlerDelete(): "
"UEventFd::fd = %d socket->iSockDesc = %d "
"UNotifier::num_connection = %d UNotifier::min_connection = %d "
"UServer_Base::isParallelizationChild() = %b sfd = %d UEventFd::op_mask = %B fd_logbuf = %u",
UEventFd::fd, socket->iSockDesc, UNotifier::num_connection, UNotifier::min_connection,
UServer_Base::isParallelizationChild(), sfd, UEventFd::op_mask, fd_logbuf);
}
# endif
if (log_request_partial == UEventFd::fd) log_request_partial = 0;
}
#endif
#if !defined(U_LOG_DISABLE) && defined(U_LINUX) && defined(ENABLE_THREAD)
ULock::atomicDecrement(U_SRV_TOT_CONNECTION);
U_INTERNAL_DUMP("U_SRV_TOT_CONNECTION = %u", U_SRV_TOT_CONNECTION)
#endif
#ifdef U_CLASSIC_SUPPORT
if (UServer_Base::isClassic()) U_EXIT(0);
#endif
U_INTERNAL_DUMP("U_ClientImage_http = %C U_http_version = %C", U_ClientImage_http(this), U_http_version)
#ifndef U_HTTP2_DISABLE
if (U_ClientImage_http(this) == '2') UHTTP2::handlerDelete(this, bsocket_open);
#endif
#ifndef U_WEBSOCKET_PARALLELIZATION
if (U_ClientImage_http(this) == '0')
{
if (bsocket_open) (void) UWebSocket::sendClose(socket);
UWebSocket::on_message_param(U_DPAGE_CLOSE);
}
#endif
if (bsocket_open) socket->close();
--UNotifier::num_connection;
#ifndef U_LOG_DISABLE
if (UServer_Base::isLog())
{
logbuf->setEmpty();
if (UNotifier::num_connection == UNotifier::min_connection) UServer_Base::log->log(U_CONSTANT_TO_PARAM("Waiting for connection on port %u"), UServer_Base::port);
}
#endif
if (data_pending)
{
U_DELETE(data_pending)
data_pending = U_NULLPTR;
}
else if (isPendingSendfile())
{
U_INTERNAL_DUMP("sfd = %d count = %I UEventFd::op_mask = %B U_ClientImage_pclose(this) = %d %B",
sfd, count, UEventFd::op_mask, U_ClientImage_pclose(this), U_ClientImage_pclose(this))
if ((U_ClientImage_pclose(this) & U_CLOSE) != 0)
{
# ifdef DEBUG
if (UNLIKELY(sfd <= 0))
{
U_ERROR("handlerDelete(): "
"UEventFd::fd = %d socket->iSockDesc = %d "
"UNotifier::num_connection = %d UNotifier::min_connection = %d "
"U_ClientImage_parallelization = %u sfd = %d UEventFd::op_mask = %B",
UEventFd::fd, socket->iSockDesc, UNotifier::num_connection, UNotifier::min_connection,
U_ClientImage_parallelization, sfd, UEventFd::op_mask);
}
# endif
UFile::close(sfd);
}
reset();
}
flag.u = 0;
UEventFd::fd = -1;
U_INTERNAL_ASSERT_EQUALS(data_pending, U_NULLPTR)
U_INTERNAL_ASSERT_EQUALS(UEventFd::op_mask, EPOLLIN | EPOLLRDHUP | EPOLLET)
#ifdef HAVE_ACCEPT4
U_INTERNAL_ASSERT_EQUALS(((USocket::accept4_flags & SOCK_CLOEXEC) != 0),((socket->flags & O_CLOEXEC) != 0))
U_INTERNAL_ASSERT_EQUALS(((USocket::accept4_flags & SOCK_NONBLOCK) != 0),((socket->flags & O_NONBLOCK) != 0))
#endif
#ifdef USE_LIBEVENT
if (UEventFd::pevent)
{
U_INTERNAL_DUMP("UEventFd::pevent = %p", UEventFd::pevent)
UDispatcher::del(pevent);
U_DELETE(pevent)
pevent = U_NULLPTR;
}
#endif
}
int UClientImage_Base::handlerTimeout()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::handlerTimeout()")
#if !defined(USE_LIBEVENT) && defined(HAVE_EPOLL_WAIT) && defined(DEBUG)
if (UNLIKELY(socket->iSockDesc == -1))
{
U_WARNING("handlerTimeout(): "
"UEventFd::fd = %d socket->iSockDesc = %d "
"UNotifier::num_connection = %d UNotifier::min_connection = %d "
"UServer_Base::isParallelizationChild() = %b sfd = %d UEventFd::op_mask = %B",
UEventFd::fd, socket->iSockDesc, UNotifier::num_connection, UNotifier::min_connection,
UServer_Base::isParallelizationChild(), sfd, UEventFd::op_mask);
U_RETURN(U_NOTIFIER_OK);
}
if (socket->iSockDesc != UEventFd::fd)
{
U_WARNING("handlerTimeout(): UEventFd::fd = %d socket->iSockDesc = %d", UEventFd::fd, socket->iSockDesc);
}
#endif
U_INTERNAL_DUMP("U_ClientImage_idle(this) = %d %B", U_ClientImage_idle(this), U_ClientImage_idle(this))
if (U_ClientImage_idle(this) != U_YES) // U_YES = 0x0001
{
// NB: maybe we have some more data to read...
if (UNotifier::waitForRead(socket->iSockDesc, 0) == 1) U_RETURN(U_NOTIFIER_OK);
socket->iState |= USocket::TIMEOUT;
U_RETURN(U_NOTIFIER_DELETE);
}
U_RETURN(U_NOTIFIER_OK);
}
const char* UClientImage_Base::getRequestUri(uint32_t& sz)
{
U_TRACE(0, "UClientImage_Base::getRequestUri(%p)", &sz)
U_INTERNAL_DUMP("U_http_is_request_nostat = %b", U_http_is_request_nostat)
const char* ptr;
#ifdef U_ALIAS
if (*request_uri)
{
# ifndef U_CACHE_REQUEST_DISABLE
U_INTERNAL_ASSERT_EQUALS(U_ClientImage_request_is_cached, false)
# endif
sz = request_uri->size();
ptr = request_uri->data();
U_INTERNAL_DUMP("request_uri(%u) = %.*S", sz, sz, ptr)
}
else
#endif
{
sz = U_http_info.uri_len;
ptr = U_http_info.uri;
U_INTERNAL_DUMP("U_http_info.uri(%u) = %.*S", sz, sz, ptr)
#ifdef USERVER_UDP
if (sz == 0 &&
UServer_Base::budp)
{
sz = U_CONSTANT_SIZE("unknow");
ptr = "unknow";
}
#endif
}
return ptr;
}
#if defined(U_SERVER_CHECK_TIME_BETWEEN_REQUEST) || (defined(DEBUG) && !defined(U_LOG_DISABLE))
void UClientImage_Base::startRequest()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::startRequest()")
#ifdef U_SERVER_CHECK_TIME_BETWEEN_REQUEST
long time_elapsed = chronometer->restart();
U_INTERNAL_DUMP("U_ClientImage_pipeline = %b time_elapsed = %ld time_run = %ld U_ClientImage_request_is_cached = %b",
U_ClientImage_pipeline, time_elapsed, time_run, U_ClientImage_request_is_cached)
if (U_ClientImage_pipeline == false &&
U_ClientImage_parallelization == 0)
{
time_between_request = time_elapsed;
# ifdef USE_LIBSSL
if (UServer_Base::bssl) return;
# endif
# ifndef U_CACHE_REQUEST_DISABLE
if (U_ClientImage_request_is_cached) return;
# endif
if ((time_run - time_between_request) > 10L)
{
U_DEBUG("UClientImage_Base::startRequest(): time_between_request(%ld) < time_run(%ld) - isParallelizationGoingToStart(%u) = %b request = %V",
time_between_request, time_run, U_NUM_CLIENT_THRESHOLD, UServer_Base::isParallelizationGoingToStart(U_NUM_CLIENT_THRESHOLD), request->rep)
if (U_http_info.startHeader > 2 &&
UServer_Base::isParallelizationGoingToStart(U_NUM_CLIENT_THRESHOLD))
{
U_INTERNAL_DUMP("U_ClientImage_request_is_cached = %b", U_ClientImage_request_is_cached)
U_ClientImage_advise_for_parallelization = 1;
if (U_ClientImage_request_is_cached == false)
{
U_ClientImage_advise_for_parallelization = 2;
setRequestToCache();
}
return;
}
}
}
#endif
#if defined(DEBUG) && !defined(U_LOG_DISABLE)
if (UServer_Base::isLog())
{
# ifndef U_SERVER_CHECK_TIME_BETWEEN_REQUEST
(void) chronometer->restart();
# endif
}
#endif
}
#endif
void UClientImage_Base::endRequest()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::endRequest()")
if (U_http_method_type)
{
UHTTP::setEndRequestProcessing();
U_http_method_type = 0; // NB: this mark the end of http request processing...
}
if (UServer_Base::isParallelizationParent() == false)
{
# ifdef U_SERVER_CHECK_TIME_BETWEEN_REQUEST
time_run = chronometer->stop();
# ifdef DEBUG
U_INTERNAL_DUMP("U_ClientImage_pipeline = %b time_between_request = %ld time_run = %ld U_ClientImage_request_is_cached = %b",
U_ClientImage_pipeline, time_between_request, time_run, U_ClientImage_request_is_cached)
if ((time_run - time_between_request) > 10L)
{
U_DEBUG("UClientImage_Base::endRequest(): time_between_request(%ld) < time_run(%ld) - request = %V", time_between_request, time_run, request->rep)
}
# endif
# endif
# ifndef U_HTTP2_DISABLE
U_INTERNAL_DUMP("U_http_version = %C U_http_info.uri_len = %u", U_http_version, U_http_info.uri_len)
if (U_http_info.uri_len)
# endif
{
# if defined(DEBUG) && !defined(U_LOG_DISABLE)
if (UServer_Base::isLog())
{
uint32_t sz = 0;
const char* ptr;
# ifndef U_CACHE_REQUEST_DISABLE
if (U_ClientImage_request_is_cached)
{
U_INTERNAL_DUMP("U_http_uri_offset = %u U_http_info.startHeader = %u U_http_info.uri_len = %u", U_http_uri_offset, U_http_info.startHeader, U_http_info.uri_len)
U_INTERNAL_ASSERT_RANGE(1,U_http_uri_offset,254)
ptr = cbuffer; // request->c_pointer(U_http_uri_offset);
# ifdef U_ALIAS
sz = U_http_info.startHeader;
# else
sz = U_http_info.uri_len;
# endif
}
else
# endif
{
ptr = getRequestUri(sz);
}
// NB: URI requested can be URL encoded (ex: vuoto%2Etxt) so we cannot use snprintf()...
char buffer1[256];
char* ptr1 = buffer1;
U_MEMCPY(ptr1, "request \"", U_CONSTANT_SIZE("request \""));
ptr1 += U_CONSTANT_SIZE("request \"");
if (sz)
{
U_INTERNAL_DUMP("sz = %u", sz)
if (sz > (sizeof(buffer1)-64)) sz = sizeof(buffer1)-64;
U_MEMCPY(ptr1, ptr, sz);
ptr1 += sz;
}
U_MEMCPY(ptr1, "\" run in ", U_CONSTANT_SIZE("\" run in "));
ptr1 += U_CONSTANT_SIZE("\" run in ");
# ifndef U_SERVER_CHECK_TIME_BETWEEN_REQUEST
time_run = chronometer->stop();
# endif
if (time_run > 0L) ptr1 += u__snprintf(ptr1, sizeof(buffer1)-(ptr1-buffer1), U_CONSTANT_TO_PARAM("%ld ms"), time_run);
else ptr1 += u__snprintf(ptr1, sizeof(buffer1)-(ptr1-buffer1), U_CONSTANT_TO_PARAM( "%g ms"), chronometer->getTimeElapsed());
if (UServer_Base::csocket->isOpen())
{
uint32_t len = 0;
int cpu = U_SYSCALL_NO_PARAM(sched_getcpu), scpu = -1;
# ifdef SO_INCOMING_CPU
if (USocket::bincoming_cpu)
{
len = sizeof(socklen_t);
(void) UServer_Base::csocket->getSockOpt(SOL_SOCKET, SO_INCOMING_CPU, (void*)&scpu, len);
len = (USocket::incoming_cpu == scpu ? 0 : U_CONSTANT_SIZE(" [DIFFER]"));
}
# endif
U_INTERNAL_DUMP("USocket::incoming_cpu = %d USocket::bincoming_cpu = %b sched cpu = %d socket cpu = %d", USocket::incoming_cpu, USocket::bincoming_cpu, cpu, scpu)
if (len) ptr1 += u__snprintf(ptr1,sizeof(buffer1)-(ptr1-buffer1),U_CONSTANT_TO_PARAM(", CPU: %d sched(%d) socket(%d)%.*s"),USocket::incoming_cpu,cpu,scpu,len," [DIFFER]");
}
U_INTERNAL_ASSERT_MINOR((ptrdiff_t)(ptr1-buffer1), (ptrdiff_t)sizeof(buffer1))
UServer_Base::log->write(buffer1, ptr1-buffer1);
}
# endif
}
}
#ifdef U_ALIAS
U_INTERNAL_DUMP("request_uri(%u) = %V", request_uri->size(), request_uri->rep)
request_uri->clear();
#endif
}
void UClientImage_Base::manageReadBufferResize(uint32_t n)
{
U_TRACE(0, "UClientImage_Base::manageReadBufferResize(%u)", n)
U_INTERNAL_DUMP("U_ClientImage_pipeline = %b size_request = %u rbuffer->size() = %u rbuffer->capacity() = %u request->size() = %u rstart = %u",
U_ClientImage_pipeline, size_request, rbuffer->size(), rbuffer->capacity(), request->size(), rstart)
U_INTERNAL_ASSERT_MAJOR(n, 0)
ptrdiff_t diff = 0;
request->clear();
if (U_ClientImage_pipeline)
{
U_ClientImage_pipeline = false;
U_INTERNAL_ASSERT_MAJOR(rstart, 0)
}
diff = -(ptrdiff_t)rstart;
if (diff)
{
rbuffer->moveToBeginDataInBuffer(rstart);
rstart = 0;
}
if (rbuffer->space() < n)
{
const char* ptr = rbuffer->data();
UString::_reserve(*rbuffer, rbuffer->getReserveNeed(n));
diff += rbuffer->data() - ptr;
}
#ifndef U_HTTP2_DISABLE
U_INTERNAL_DUMP("U_ClientImage_http = %C U_http_version = %C", U_ClientImage_http(UServer_Base::pClientImage), U_http_version)
if (U_ClientImage_http(UServer_Base::pClientImage) != '2')
#endif
{
if (U_http_method_type)
{
U_INTERNAL_DUMP("diff = %d", diff)
U_INTERNAL_ASSERT_POINTER(U_http_info.uri)
U_http_info.uri += diff;
if (U_http_info.query_len) U_http_info.query += diff;
U_INTERNAL_DUMP("uri = %.*S", U_HTTP_URI_TO_TRACE)
U_INTERNAL_DUMP("query = %.*S", U_HTTP_QUERY_TO_TRACE)
U_INTERNAL_ASSERT_DIFFERS(U_http_info.uri[0], 0)
if (U_http_host_len) U_http_info.host += diff;
if (U_http_range_len) U_http_info.range += diff;
if (U_http_accept_len) U_http_info.accept += diff;
if (U_http_ip_client_len) U_http_info.ip_client += diff;
if (U_http_info.cookie_len) U_http_info.cookie += diff;
if (U_http_info.referer_len) U_http_info.referer += diff;
if (U_http_content_type_len) U_http_info.content_type += diff;
if (U_http_info.user_agent_len) U_http_info.user_agent += diff;
if (U_http_accept_language_len) U_http_info.accept_language += diff;
# if defined(USE_LIBSSL) && !defined(U_SERVER_CAPTIVE_PORTAL)
if (U_http_websocket_len) UWebSocket::upgrade_settings += diff;
# endif
U_INTERNAL_DUMP("host = %.*S", U_HTTP_HOST_TO_TRACE)
U_INTERNAL_DUMP("vhost = %.*S", U_HTTP_VHOST_TO_TRACE)
U_INTERNAL_DUMP("range = %.*S", U_HTTP_RANGE_TO_TRACE)
U_INTERNAL_DUMP("ctype = %.*S", U_HTTP_CTYPE_TO_TRACE)
U_INTERNAL_DUMP("cookie = %.*S", U_HTTP_COOKIE_TO_TRACE)
U_INTERNAL_DUMP("accept = %.*S", U_HTTP_ACCEPT_TO_TRACE)
U_INTERNAL_DUMP("referer = %.*S", U_HTTP_REFERER_TO_TRACE)
U_INTERNAL_DUMP("ip_client = %.*S", U_HTTP_IP_CLIENT_TO_TRACE)
U_INTERNAL_DUMP("user_agent = %.*S", U_HTTP_USER_AGENT_TO_TRACE)
U_INTERNAL_DUMP("accept_language = %.*S", U_HTTP_ACCEPT_LANGUAGE_TO_TRACE)
}
*request = *rbuffer;
}
U_INTERNAL_DUMP("U_ClientImage_pipeline = %b U_ClientImage_data_missing = %b", U_ClientImage_pipeline, U_ClientImage_data_missing)
}
void UClientImage_Base::resetReadBuffer()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::resetReadBuffer()")
request->clear();
U_INTERNAL_DUMP("rstart = %u size_request = %u", rstart, size_request)
if (rstart)
{
rbuffer->moveToBeginDataInBuffer(rstart);
rstart = 0;
*request = *rbuffer;
}
U_ClientImage_pipeline = false;
}
void UClientImage_Base::prepareForRead()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::prepareForRead()")
u_clientimage_info.flag.u = 0; // NB: U_ClientImage_parallelization is reset by this...
#ifdef USERVER_UDP
if (UServer_Base::budp == false)
#endif
{
#ifdef U_CLASSIC_SUPPORT
if (UServer_Base::isClassic())
{
U_ASSERT(UServer_Base::proc->child())
U_ClientImage_parallelization = U_PARALLELIZATION_CHILD;
}
#endif
// NB: we need to check if we were called from UServer_Base::handlerRead or directly from UNotifier...
U_INTERNAL_DUMP("UEventFd::fd = %d socket->iSockDesc = %d", UEventFd::fd, socket->iSockDesc)
if (UEventFd::fd == socket->iSockDesc)
{
// NB: called directly from UNotifier...
U_INTERNAL_ASSERT_DIFFERS(UEventFd::fd, -1)
UServer_Base::csocket = socket;
UServer_Base::pClientImage = this;
UServer_Base::client_address = socket->cRemoteAddress.pcStrAddress;
UServer_Base::client_address_len = u__strlen(UServer_Base::client_address, __PRETTY_FUNCTION__);
U_INTERNAL_DUMP("UServer_Base::client_address = %.*S", U_CLIENT_ADDRESS_TO_TRACE)
# ifdef U_EVASIVE_SUPPORT
if (UServer_Base::checkHold(socket->getClientAddress()))
{
abortive_close();
return;
}
# endif
// resetRequestFromUServer();
}
else
{
// NB: called from UServer_Base::handlerRead...
U_INTERNAL_DUMP("UServer_Base::csocket = %p socket = %p UServer_Base::pClientImage = %p this = %p", UServer_Base::csocket, socket, UServer_Base::pClientImage, this)
U_INTERNAL_ASSERT_DIFFERS(socket->iSockDesc, -1)
U_INTERNAL_ASSERT_EQUALS(UServer_Base::csocket, socket)
U_INTERNAL_ASSERT_EQUALS(UServer_Base::pClientImage, this)
UEventFd::fd = socket->iSockDesc;
// setRequestFromUServer();
}
#ifdef U_EVASIVE_SUPPORT
if (UServer_Base::checkHitSiteStats())
{
if (UHTTP::file_gzip_bomb &&
UServer_Base::bssl == false)
{
bsendGzipBomb = true;
}
else
{
abortive_close();
}
return;
}
#endif
#ifdef U_THROTTLING_SUPPORT
UServer_Base::initThrottlingClient();
#endif
}
}
bool UClientImage_Base::genericRead()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::genericRead()")
#if defined(DEBUG) || defined(U_EVASIVE_SUPPORT)
if (UNLIKELY(socket->iSockDesc == -1))
{
# ifndef U_EVASIVE_SUPPORT
U_WARNING("genericRead(): "
"UEventFd::fd = %d socket->iSockDesc = %d "
"UNotifier::num_connection = %d UNotifier::min_connection = %d "
"UServer_Base::isParallelizationChild() = %b sfd = %d UEventFd::op_mask = %B",
UEventFd::fd, socket->iSockDesc, UNotifier::num_connection, UNotifier::min_connection,
UServer_Base::isParallelizationChild(), sfd, UEventFd::op_mask);
# endif
U_ClientImage_state = U_PLUGIN_HANDLER_ERROR;
U_RETURN(false);
}
#endif
U_INTERNAL_ASSERT_EQUALS(socket->iSockDesc, UEventFd::fd)
rstart = 0;
request->clear(); // reset buffer before read
U_INTERNAL_DUMP("rbuffer(%u) = %V", rbuffer->size(), rbuffer->rep)
// NB: rbuffer string can be referenced more than one (often if U_SUBSTR_INC_REF is defined)...
if (rbuffer->uniq()) rbuffer->rep->_length = 0;
else rbuffer->_set(UStringRep::create(0U, U_CAPACITY, U_NULLPTR));
if (data_pending)
{
U_INTERNAL_DUMP("data_pending(%u) = %V", data_pending->size(), data_pending->rep)
U_MEMCPY(rbuffer->data(), data_pending->data(), rbuffer->rep->_length = data_pending->size());
}
socket->iState = USocket::CONNECT; // prepare socket before read
#ifdef USERVER_UDP
if (UServer_Base::budp)
{
uint32_t sz = rbuffer->size();
int iBytesTransferred = socket->recvFrom(rbuffer->data()+sz, rbuffer->capacity());
if (iBytesTransferred <= 0) U_RETURN(false);
rbuffer->size_adjust(sz+iBytesTransferred);
UServer_Base::setClientAddress();
# ifndef U_LOG_DISABLE
UServer_Base::logNewClient(socket, this);
# endif
}
else
#endif
{
if (USocketExt::read(socket, *rbuffer, U_SINGLE_READ, 0) == false) // NB: timeout == 0 means that we put the socket fd on epoll queue if EAGAIN...
{
U_ClientImage_state = (isOpen() ? U_PLUGIN_HANDLER_AGAIN
: U_PLUGIN_HANDLER_ERROR);
U_RETURN(false);
}
}
if (data_pending)
{
if (callerIsValidRequestExt(U_STRING_TO_PARAM(*rbuffer)) == false) // partial valid (not complete)
{
(void) data_pending->replace(*rbuffer);
U_INTERNAL_DUMP("data_pending(%u) = %V", data_pending->size(), data_pending->rep)
U_ClientImage_state = U_PLUGIN_HANDLER_AGAIN;
U_RETURN(false);
}
U_DELETE(data_pending)
data_pending = U_NULLPTR;
}
U_gettimeofday // NB: optimization if it is enough a time resolution of one second...
#if defined(U_SERVER_CHECK_TIME_BETWEEN_REQUEST) || (defined(DEBUG) && !defined(U_LOG_DISABLE))
startRequest();
#endif
#ifdef U_SERVER_CHECK_TIME_BETWEEN_REQUEST
if (U_ClientImage_advise_for_parallelization)
{
U_INTERNAL_DUMP("U_ClientImage_advise_for_parallelization = %u", U_ClientImage_advise_for_parallelization)
if (checkRequestToCache() == 2 &&
UClient_Base::csocket == U_NULLPTR &&
UServer_Base::startParallelization(U_NUM_CLIENT_THRESHOLD))
{
// parent
U_RETURN(false);
}
if (U_ClientImage_advise_for_parallelization == 2) U_ClientImage_request_is_cached = false;
}
#endif
U_ClientImage_state = 0;
U_RETURN(true);
}
int UClientImage_Base::handlerRead() // Connection-wide hooks
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::handlerRead()")
uint32_t sz;
prepareForRead();
start:
U_INTERNAL_ASSERT_EQUALS(U_ClientImage_pipeline, false)
U_INTERNAL_ASSERT_EQUALS(U_ClientImage_data_missing, false)
if (genericRead() == false)
{
if (U_ClientImage_state == U_PLUGIN_HANDLER_AGAIN &&
U_ClientImage_parallelization != U_PARALLELIZATION_CHILD)
{
# ifdef DEBUG
U_ASSERT(isOpen())
UServer_Base::nread_again++;
# endif
U_RETURN(U_NOTIFIER_OK); // NOT BLOCKING...
}
goto end;
}
#ifdef DEBUG
U_ASSERT(isOpen())
UServer_Base::nread++;
#endif
loop:
U_INTERNAL_DUMP("U_ClientImage_pipeline = %b size_request = %u rstart = %u rbuffer(%u) = %V",
U_ClientImage_pipeline, size_request, rstart, rbuffer->size(), rbuffer->rep)
if (U_ClientImage_pipeline == false) *request = *rbuffer;
else
{
pipeline:
sz = rbuffer->size();
U_INTERNAL_DUMP("size_request = %u sz = %u rstart = %u", size_request, sz, rstart)
U_ASSERT_MINOR(rstart, sz)
U_INTERNAL_ASSERT_MAJOR(rstart, 0)
U_INTERNAL_ASSERT_EQUALS(request->same(*rbuffer), false)
sz -= rstart;
if (size_request <= sz) *request = rbuffer->substr(rstart, sz);
else
{
resetReadBuffer();
U_INTERNAL_DUMP("U_ClientImage_close = %b U_ClientImage_state = %d %B",
U_ClientImage_close, U_ClientImage_state, U_ClientImage_state)
if (callerIsValidRequestExt(request->data(), sz) == false) U_ClientImage_data_missing = true; // partial valid (not complete)
}
}
U_INTERNAL_ASSERT(request->invariant())
#ifndef U_LOG_DISABLE
if (logbuf) logRequest();
#endif
U_INTERNAL_DUMP("U_ClientImage_pipeline = %b U_ClientImage_data_missing = %b", U_ClientImage_pipeline, U_ClientImage_data_missing)
if (U_ClientImage_data_missing)
{
data_missing:
U_INTERNAL_DUMP("U_ClientImage_parallelization = %u U_http_version = %C", U_ClientImage_parallelization, U_http_version)
U_INTERNAL_ASSERT_DIFFERS(U_http_version, '2')
if (U_ClientImage_parallelization == U_PARALLELIZATION_CHILD)
{
if (UNotifier::waitForRead(UServer_Base::csocket->iSockDesc, U_TIMEOUT_MS) != 1 ||
(resetReadBuffer(), USocketExt::read(UServer_Base::csocket, *rbuffer, getCountToRead(), 0)) == false)
{
if ((U_ClientImage_state & U_PLUGIN_HANDLER_ERROR) != 0) U_RETURN(U_NOTIFIER_DELETE);
goto death;
}
}
U_ClientImage_data_missing = false;
U_INTERNAL_ASSERT_EQUALS(data_pending, U_NULLPTR)
if (U_ClientImage_parallelization == U_PARALLELIZATION_CHILD) goto loop;
U_NEW_STRING(data_pending, UString((void*)U_STRING_TO_PARAM(*request)));
U_INTERNAL_DUMP("data_pending(%u) = %V", data_pending->size(), data_pending->rep)
U_ASSERT(isOpen())
U_INTERNAL_ASSERT_DIFFERS(U_ClientImage_parallelization, U_PARALLELIZATION_CHILD)
U_RETURN(U_NOTIFIER_OK);
}
#ifndef U_CACHE_REQUEST_DISABLE
if (U_ClientImage_request_is_cached)
{
sz = checkRequestToCache();
if (sz)
{
if (sz == 1) goto data_missing; // partial valid (not complete)
U_INTERNAL_ASSERT_EQUALS(sz, 2)
setRequestProcessed();
goto next2;
}
U_ClientImage_request_is_cached = false;
}
#endif
#if defined(U_SERVER_CAPTIVE_PORTAL) && defined(ENABLE_THREAD)
if (UHTTP::checkForUSP()) U_RETURN(U_NOTIFIER_OK);
if (U_ClientImage_parallelization == U_PARALLELIZATION_PARENT) U_RETURN(U_NOTIFIER_DELETE);
#endif
U_INTERNAL_ASSERT_EQUALS(UServer_Base::csocket, UServer_Base::pClientImage->socket)
resetBuffer();
#ifndef U_WEBSOCKET_PARALLELIZATION
U_INTERNAL_DUMP("U_ClientImage_http = %C U_http_version = %C", U_ClientImage_http(this), U_http_version)
if (U_ClientImage_http(this) == '0')
{
if (UWebSocket::handleDataFraming(socket) == U_WS_STATUS_CODE_OK)
{
UWebSocket::on_message();
UWebSocket::rbuffer->setEmpty();
if (U_http_info.nResponseCode == HTTP_INTERNAL_ERROR) U_RETURN(U_NOTIFIER_DELETE);
}
U_RETURN(U_NOTIFIER_OK);
}
#endif
size_request = 0;
U_ClientImage_state = callerHandlerRead();
U_INTERNAL_DUMP("socket->isClosed() = %b U_http_info.nResponseCode = %u U_ClientImage_close = %b U_ClientImage_state = %d %B",
socket->isClosed(), U_http_info.nResponseCode, U_ClientImage_close, U_ClientImage_state, U_ClientImage_state)
if (UNLIKELY(socket->isClosed()))
{
cls: if (U_ClientImage_parallelization == U_PARALLELIZATION_PARENT)
{
U_ASSERT(wbuffer->empty())
U_INTERNAL_ASSERT_EQUALS(U_ClientImage_data_missing, false)
endRequest();
U_RETURN(U_NOTIFIER_DELETE);
}
goto error;
}
U_INTERNAL_DUMP("U_ClientImage_pipeline = %b U_ClientImage_parallelization = %u U_ClientImage_data_missing = %b",
U_ClientImage_pipeline, U_ClientImage_parallelization, U_ClientImage_data_missing)
U_ASSERT(isOpen())
U_INTERNAL_ASSERT_EQUALS(U_ClientImage_state & U_PLUGIN_HANDLER_ERROR, 0)
if (U_ClientImage_data_missing) goto data_missing;
U_INTERNAL_DUMP("size_request = %u", size_request)
if (size_request)
{
# if !defined(U_LOG_DISABLE) && !defined(U_COVERITY_FALSE_POSITIVE)
U_INTERNAL_DUMP("log_request_partial = %u UEventFd::fd = %d", log_request_partial, UEventFd::fd)
if (log_request_partial == UEventFd::fd) logRequest();
# endif
# ifndef U_CACHE_REQUEST_DISABLE
next2:
# endif
sz = rbuffer->size();
if (U_ClientImage_pipeline)
{
uint32_t new_rstart = rstart + size_request;
U_INTERNAL_DUMP("rstart = %u size_request = %u new_rstart = %u sz = %u", rstart, size_request, new_rstart, sz)
U_INTERNAL_ASSERT(sz >= new_rstart)
if (sz == new_rstart) U_ClientImage_pipeline = false;
else
{
*request = rbuffer->substr(rstart, size_request);
rstart = new_rstart;
}
}
else if (size_request < sz) // we check if we have a pipeline...
{
const char* ptr1 = rbuffer->c_pointer(size_request);
if (UNLIKELY(u__isspace(*ptr1))) while (u__isspace(*++ptr1)) {}
if (ptr1 < rbuffer->pend())
{
U_ClientImage_pipeline = true;
# ifndef U_PIPELINE_HOMOGENEOUS_DISABLE
U_INTERNAL_DUMP("U_http_info.nResponseCode = %u U_ClientImage_close = %b U_ClientImage_state = %d %B",
U_http_info.nResponseCode, U_ClientImage_close, U_ClientImage_state, U_ClientImage_state)
if (callerIsValidMethod(ptr1))
{
resto = (sz % size_request);
uint32_t n = (sz / size_request);
U_INTERNAL_DUMP("n = %u resto = %u size_request = %u wbuffer(%u) = %V", n, resto, size_request, wbuffer->size(), wbuffer->rep)
if (n &&
*wbuffer &&
callerIsValidRequest(ptr1, size_request) &&
(resto == 0 || callerIsValidMethod(rbuffer->c_pointer(sz-resto))))
{
U_INTERNAL_ASSERT_EQUALS(nrequest, 0)
const char* ptr = rbuffer->data();
const char* end = ptr + sz;
while (true)
{
# if !defined(U_SERVER_CAPTIVE_PORTAL) || !defined(ENABLE_THREAD)
if (memcmp(ptr, ptr1, size_request) != 0) break;
# endif
ptr1 += size_request;
if (ptr1 >= end)
{
nrequest = n;
goto check;
}
ptr += size_request;
}
U_INTERNAL_ASSERT_MINOR(ptr1, end)
nrequest = (rbuffer->distance(ptr1) / size_request);
if (nrequest == 1) nrequest = 0;
check: U_INTERNAL_DUMP("nrequest = %u resto = %u", nrequest, resto)
U_INTERNAL_ASSERT(nrequest <= n)
if (resto ||
nrequest != n)
{
rstart = (nrequest ? nrequest : 1) * size_request;
*request = rbuffer->substr(rstart);
}
U_INTERNAL_DUMP("nrequest = %u resto = %u U_ClientImage_pipeline = %b U_ClientImage_close = %b rstart = %u request(%u) = %V",
nrequest, resto, U_ClientImage_pipeline, U_ClientImage_close, rstart, request->size(), request->rep)
# if defined(U_SERVER_CAPTIVE_PORTAL) && defined(ENABLE_THREAD)
goto write1;
# else
goto write2;
# endif
}
resto = 0;
}
# endif
}
*request = rbuffer->substr(0U, (rstart = size_request));
}
}
U_INTERNAL_DUMP("U_ClientImage_pipeline = %b size_request = %u request->size() = %u rstart = %u", U_ClientImage_pipeline, size_request, request->size(), rstart)
if (isRequestNeedProcessing())
{
U_INTERNAL_ASSERT_EQUALS(U_ClientImage_state & (U_PLUGIN_HANDLER_AGAIN | U_PLUGIN_HANDLER_ERROR), 0)
# ifdef U_WEBSOCKET_PARALLELIZATION
U_INTERNAL_ASSERT_DIFFERS(U_ClientImage_parallelization, U_PARALLELIZATION_PARENT)
# else
if (U_ClientImage_parallelization == U_PARALLELIZATION_PARENT)
{
U_ASSERT(wbuffer->empty())
U_INTERNAL_ASSERT_EQUALS(U_ClientImage_data_missing, false)
endRequest();
U_ClientImage_parallelization = 0;
U_RETURN(U_NOTIFIER_OK);
}
# endif
callerHandlerRequest();
if (UNLIKELY(socket->isClosed())) goto cls;
}
#if defined(U_SERVER_CAPTIVE_PORTAL) && defined(ENABLE_THREAD)
else
{
#ifndef U_PIPELINE_HOMOGENEOUS_DISABLE
write1:
#endif
writeResponseCompact();
U_INTERNAL_DUMP("U_ClientImage_request_is_cached = %b", U_ClientImage_request_is_cached)
if (U_ClientImage_request_is_cached == false) endRequest();
# ifndef U_PIPELINE_HOMOGENEOUS_DISABLE
if (nrequest > 1)
{
static struct iovec liov[64];
U_INTERNAL_ASSERT_RANGE(2,nrequest,64)
/**
* struct iovec {
* void* iov_base; // Starting address
* size_t iov_len; // Number of bytes to transfer
* };
*/
if (liov->iov_len == 0)
{
char* ptr = (char*)liov;
U_MEMCPY(ptr, iov_vec+1, sizeof(struct iovec)*2);
U_MEMCPY(ptr+(sizeof(struct iovec)*2), ptr, sizeof(struct iovec)*2);
U_MEMCPY(ptr+(sizeof(struct iovec)*4), ptr, sizeof(struct iovec)*4);
U_MEMCPY(ptr+(sizeof(struct iovec)*8), ptr, sizeof(struct iovec)*8);
U_MEMCPY(ptr+(sizeof(struct iovec)*16), ptr, sizeof(struct iovec)*16);
U_MEMCPY(ptr+(sizeof(struct iovec)*32), ptr, sizeof(struct iovec)*32);
}
(void) USocketExt::writev(socket, liov, nrequest * 2, nrequest * (17+51+iov_vec[2].iov_len), 0);
}
nrequest = 0;
if (resto)
{
resto = 0;
U_ASSERT_EQUALS(*request, rbuffer->substr(rstart))
goto data_missing;
}
# endif
U_RETURN(U_NOTIFIER_OK);
}
#endif
U_INTERNAL_DUMP("socket->isClosed() = %b U_http_info.nResponseCode = %u U_ClientImage_close = %b U_ClientImage_state = %d %B",
socket->isClosed(), U_http_info.nResponseCode, U_ClientImage_close, U_ClientImage_state, U_ClientImage_state)
U_INTERNAL_DUMP("wbuffer(%u) = %V", wbuffer->size(), wbuffer->rep)
U_INTERNAL_DUMP(" body(%u) = %V", body->size(), body->rep)
if (LIKELY(*wbuffer))
{
U_INTERNAL_DUMP("U_http_info.nResponseCode = %u count = %I UEventFd::op_mask = %d %B",
U_http_info.nResponseCode, count, UEventFd::op_mask, UEventFd::op_mask)
U_INTERNAL_ASSERT_DIFFERS(U_ClientImage_parallelization, U_PARALLELIZATION_PARENT)
if (count == 0)
{
#if !defined(U_PIPELINE_HOMOGENEOUS_DISABLE) && (!defined(U_SERVER_CAPTIVE_PORTAL) || !defined(ENABLE_THREAD))
write2:
#endif
if (writeResponse() == false)
{
# ifndef U_PIPELINE_HOMOGENEOUS_DISABLE
resto = 0;
# endif
goto error;
}
# ifndef U_PIPELINE_HOMOGENEOUS_DISABLE
if (resto)
{
resto = 0;
endRequest();
U_ASSERT_EQUALS(*request, rbuffer->substr(rstart))
goto data_missing;
}
# endif
}
else
{
// NB: we are managing a sendfile() request...
U_INTERNAL_ASSERT_EQUALS(nrequest, 0)
U_INTERNAL_ASSERT_DIFFERS(U_http_version, '2')
U_INTERNAL_ASSERT_EQUALS(UEventFd::op_mask, EPOLLIN | EPOLLRDHUP | EPOLLET)
if (writeResponse() == false ||
UClientImage_Base::handlerWrite() == U_NOTIFIER_DELETE)
{
U_INTERNAL_DUMP("count = %I", count)
goto error;
}
}
}
U_INTERNAL_DUMP("U_ClientImage_pipeline = %b (U_ClientImage_state & U_PLUGIN_HANDLER_ERROR) = %u", U_ClientImage_pipeline, U_ClientImage_state & U_PLUGIN_HANDLER_ERROR)
if (LIKELY((U_ClientImage_state & U_PLUGIN_HANDLER_ERROR) == 0))
{
if (U_ClientImage_pipeline)
{
endRequest();
# if defined(U_SERVER_CHECK_TIME_BETWEEN_REQUEST) || (defined(DEBUG) && !defined(U_LOG_DISABLE))
startRequest();
# endif
U_ClientImage_request = 0;
goto pipeline;
}
}
else
{
U_INTERNAL_ASSERT_EQUALS(U_ClientImage_pipeline, false)
error:
U_INTERNAL_ASSERT_DIFFERS(UEventFd::fd, -1)
U_ClientImage_close = true;
if ( UHTTP::file_data &&
u_is_usp(UHTTP::file_data->mime_index))
{
U_INTERNAL_ASSERT_POINTER(UHTTP::usp)
U_INTERNAL_ASSERT_POINTER(UHTTP::usp->runDynamicPageParam)
UHTTP::usp->runDynamicPageParam(U_DPAGE_ERROR);
}
}
#ifdef U_THROTTLING_SUPPORT
if (uri) UServer_Base::clearThrottling();
#endif
endRequest();
U_DUMP("U_ClientImage_close = %b UServer_Base::isParallelizationChild() = %b", U_ClientImage_close, UServer_Base::isParallelizationChild())
if (U_ClientImage_close)
{
end: if (U_ClientImage_parallelization == U_PARALLELIZATION_CHILD) goto death;
U_RETURN(U_NOTIFIER_DELETE);
}
// NB: maybe we have some more request to services on the same connection...
if (U_ClientImage_parallelization == U_PARALLELIZATION_CHILD)
{
U_INTERNAL_ASSERT_DIFFERS(socket->iSockDesc, -1)
if (UNotifier::waitForRead(socket->iSockDesc, U_TIMEOUT_MS) == 1)
{
U_ClientImage_request = 0;
goto start;
}
death:
UServer_Base::endNewChild(); // no return
}
last_event = u_now->tv_sec;
U_ASSERT(isOpen())
U_INTERNAL_ASSERT_DIFFERS(U_ClientImage_parallelization, U_PARALLELIZATION_CHILD)
U_INTERNAL_ASSERT_DIFFERS(U_ClientImage_parallelization, U_PARALLELIZATION_PARENT)
U_INTERNAL_DUMP("request(%u) = %V", request->size(), request->rep);
U_RETURN(U_NOTIFIER_OK);
}
bool UClientImage_Base::writeResponse()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::writeResponse()")
U_INTERNAL_DUMP("U_ClientImage_pipeline = %b U_ClientImage_close = %b nrequest = %u", U_ClientImage_pipeline, U_ClientImage_close, nrequest)
U_ASSERT(isOpen())
U_INTERNAL_ASSERT(*wbuffer)
U_INTERNAL_ASSERT_DIFFERS(U_http_version, '2')
U_INTERNAL_ASSERT_DIFFERS(U_ClientImage_parallelization, U_PARALLELIZATION_PARENT)
int idx, iovcnt;
struct iovec* iov;
bool bresult, bclose = false;
uint32_t ncount, iBytesWrite, sz1, sz2 = (U_http_method_type == HTTP_HEAD ? 0 : body->size());
#if !defined(U_PIPELINE_HOMOGENEOUS_DISABLE) || defined(U_CLIENT_RESPONSE_PARTIAL_WRITE_SUPPORT)
struct iovec liov[256];
#endif
sz1 =
ncount = wbuffer->size();
iov_vec[2].iov_len = sz1;
iov_vec[2].iov_base = (caddr_t)wbuffer->data();
if (bnoheader)
{
bnoheader = false;
U_INTERNAL_ASSERT_EQUALS(nrequest, 0)
# ifdef USERVER_UDP
if (UServer_Base::budp)
{
U_INTERNAL_ASSERT_EQUALS(iov_vec[2].iov_len, ncount)
U_SRV_LOG_WITH_ADDR("send udp response (%u bytes) %#.*S to", sz1, sz1, iov_vec[2].iov_base);
if (socket->sendTo(iov_vec[2].iov_base, sz1) == (int)sz1) U_RETURN(true);
U_RETURN(false);
}
# endif
idx = 2;
iovcnt = 1;
}
else
{
U_INTERNAL_DUMP("iov_vec[1].iov_len = %u", iov_vec[1].iov_len)
if (iov_vec[1].iov_len == 17+6+29+2+12+2) // HTTP/1.1 200 OK\r\nDate: Wed, 20 Jun 2012 11:43:17 GMT\r\nServer: ULib\r\n
{
idx = 1;
iovcnt = 2;
}
else
{
idx = 0;
iovcnt = 3;
ncount += iov_vec[0].iov_len;
iov_vec[1].iov_base = (char*)iov_vec[1].iov_base + 17; // HTTP/1.1 200 OK\r\n
U_INTERNAL_DUMP("U_http_info.nResponseCode = %u UClientImage_Base::iov_vec[0] = %.*S",
U_http_info.nResponseCode, UClientImage_Base::iov_vec[0].iov_len, UClientImage_Base::iov_vec[0].iov_base)
}
# ifdef U_SSE_ENABLE // SERVER SENT EVENTS (SSE)
U_INTERNAL_DUMP("UHTTP::sse_func = %p", UHTTP::sse_func)
if (UHTTP::sse_func != (void*)1L)
# endif
{
if (U_ClientImage_close &&
U_ClientImage_pipeline == false)
{
bclose = true;
iov_vec[1].iov_len += 17+2; // Connection: close\r\n
}
}
ncount += iov_vec[1].iov_len;
# if !defined(U_LINUX) || !defined(ENABLE_THREAD)
ULog::updateDate3(U_NULLPTR);
# endif
}
iov = iov_vec+idx;
U_INTERNAL_DUMP("sz2 = %u", sz2)
if (sz2)
{
++iovcnt;
ncount += sz2;
iov_vec[3].iov_len = sz2;
iov_vec[3].iov_base = (caddr_t)body->data();
}
#ifndef U_LOG_DISABLE
if (logbuf)
{
uint32_t msg_len = (U_ClientImage_pipeline ? U_CONSTANT_SIZE("[pipeline] ") : 0);
if (idx == 0) UServer_Base::log->log(iov, "response", ncount, "[pipeline] ", msg_len, U_CONSTANT_TO_PARAM(" to %v"), logbuf->rep);
else
{
UServer_Base::log->log(U_CONSTANT_TO_PARAM("%s send response (%u bytes) %.*s%#.*S to %v"),
UServer_Base::mod_name[0], ncount, msg_len, "[pipeline] ", iov->iov_len, iov->iov_base, logbuf->rep);
}
}
#endif
#ifndef U_PIPELINE_HOMOGENEOUS_DISABLE
if (nrequest > 1)
{
U_INTERNAL_ASSERT_RANGE(2,nrequest,256)
char* ptr = (char*)liov;
uint32_t sz = sizeof(struct iovec) * iovcnt;
iovcnt *= nrequest;
ncount *= nrequest;
copy: U_MEMCPY(ptr, iov, sz);
if (--nrequest)
{
ptr += sz;
goto copy;
}
iBytesWrite = USocketExt::writev(socket, (iov = liov), iovcnt, ncount, 0);
}
else
#endif
{
U_INTERNAL_ASSERT_MINOR(nrequest, 2)
if (nrequest == 1) nrequest = 0;
iBytesWrite = USocketExt::writev(socket, iov, iovcnt, ncount, U_ClientImage_pipeline ? U_TIMEOUT_MS : 0);
}
U_INTERNAL_ASSERT_EQUALS(nrequest, 0)
#ifdef U_THROTTLING_SUPPORT
if (iBytesWrite > 0) bytes_sent += iBytesWrite;
#endif
#ifdef DEBUG
if (iBytesWrite > 0) UServer_Base::stats_bytes += iBytesWrite;
#endif
if ((bresult = (iBytesWrite == ncount)) == false)
{
U_SRV_LOG("writev failed (remain %u bytes) - sock_fd %u", ncount - iBytesWrite, socket->iSockDesc);
if (socket->isOpen())
{
# ifndef U_CLIENT_RESPONSE_PARTIAL_WRITE_SUPPORT
resetPipelineAndSetCloseConnection();
# else
U_INTERNAL_ASSERT_EQUALS(UServer_Base::bssl, false)
U_INTERNAL_ASSERT_EQUALS(UEventFd::op_mask, EPOLLIN | EPOLLRDHUP | EPOLLET)
U_INTERNAL_ASSERT_DIFFERS(U_ClientImage_parallelization, U_PARALLELIZATION_CHILD) // NB: we must not have pending write...
sfd = UFile::mkTemp();
if ((bresult = (sfd != -1)) == false)
{
U_SRV_LOG("partial write failed: (remain %u bytes) - error on create temporary file - sock_fd %u", ncount - iBytesWrite, socket->iSockDesc);
}
else
{
count = ncount - iBytesWrite;
iBytesWrite = UFile::writev(sfd, liov, USocketExt::iov_resize(liov, iov, iovcnt, iBytesWrite));
if ((bresult = (iBytesWrite == count)))
{
U_SRV_LOG("partial write: (remain %u bytes) - create temporary file - sock_fd %u sfd %u", count, socket->iSockDesc, sfd);
setPendingSendfile(); // NB: now we have a pending sendfile...
}
else
{
U_SRV_LOG("partial write failed: (remain %u bytes) - error on write (%u bytes) on temporary file - sock_fd %u sfd %u", count, iBytesWrite, socket->iSockDesc, sfd);
UFile::close(sfd);
sfd = -1;
}
}
# endif
}
}
U_INTERNAL_DUMP("bclose = %b idx = %u bresult = %b", bclose, idx, bresult)
if (bclose ||
idx == 0)
{
if (idx == 0) iov_vec[1].iov_base = (char*)iov_vec[1].iov_base - 17; // HTTP/1.1 200 OK\r\n
setHeaderForResponse(17+6+29+2+12+2); // HTTP/1.1 200 OK\r\nDate: Wed, 20 Jun 2012 11:43:17 GMT\r\nServer: ULib\r\n
}
U_RETURN(bresult);
}
void UClientImage_Base::close()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::close()")
setRequestProcessed();
UServer_Base::csocket->close();
resetPipelineAndSetCloseConnection();
}
void UClientImage_Base::abortive_close()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::abortive_close()")
setRequestProcessed();
U_ClientImage_close = true;
if (U_ClientImage_pipeline) resetPipeline();
if (UServer_Base::csocket->isOpen()) UServer_Base::csocket->abortive_close();
}
void UClientImage_Base::resetPipeline()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::resetPipeline()")
U_INTERNAL_DUMP("U_ClientImage_pipeline = %b U_ClientImage_parallelization = %u U_ClientImage_request_is_cached = %b U_ClientImage_close = %b",
U_ClientImage_pipeline, U_ClientImage_parallelization, U_ClientImage_request_is_cached, U_ClientImage_close)
U_INTERNAL_ASSERT(U_ClientImage_pipeline)
U_INTERNAL_ASSERT(request->same(*rbuffer) == false)
U_ClientImage_pipeline = false;
#ifndef U_CACHE_REQUEST_DISABLE
if (U_ClientImage_request_is_cached == false)
#endif
size_request = 0; // NB: we don't want to process further the read buffer...
}
void UClientImage_Base::prepareForSendfile()
{
U_TRACE_NO_PARAM(0, "UClientImage::prepareForSendfile()")
U_INTERNAL_ASSERT_MAJOR(sfd, 0)
U_INTERNAL_ASSERT_MAJOR(count, 0)
U_INTERNAL_ASSERT_DIFFERS(U_http_version, '2')
U_INTERNAL_ASSERT_DIFFERS(U_ClientImage_parallelization, U_PARALLELIZATION_CHILD)
if (U_ClientImage_close)
{
U_ClientImage_close = false;
U_ClientImage_pclose(this) = U_YES;
}
if (U_ClientImage_pipeline) resetPipeline();
UEventFd::op_mask = EPOLLOUT;
if (UNotifier::isHandler(UEventFd::fd)) (void) UNotifier::modify(this);
U_INTERNAL_DUMP("offset = %I count = %I", offset, count)
}
int UClientImage_Base::handlerWrite()
{
U_TRACE_NO_PARAM(0, "UClientImage_Base::handlerWrite()")
U_INTERNAL_ASSERT_DIFFERS(U_http_version, '2')
#if !defined(USE_LIBEVENT) && defined(HAVE_EPOLL_WAIT) && defined(DEBUG)
if (UNLIKELY(count == 0))
{
U_WARNING("handlerWrite(): "
"UEventFd::fd = %d socket->iSockDesc = %d "
"UNotifier::num_connection = %d UNotifier::min_connection = %d "
"UServer_Base::isParallelizationChild() = %b sfd = %d UEventFd::op_mask = %B",
UEventFd::fd, socket->iSockDesc, UNotifier::num_connection, UNotifier::min_connection,
UServer_Base::isParallelizationChild(), sfd, UEventFd::op_mask);
if (UNotifier::num_connection > UNotifier::min_connection) U_RETURN(U_NOTIFIER_DELETE);
U_RETURN(U_NOTIFIER_OK);
}
#endif
U_ASSERT(isOpen())
U_INTERNAL_ASSERT_MAJOR(sfd, 0)
U_INTERNAL_ASSERT_MAJOR(count, 0)
bool bwrite = (UEventFd::op_mask == EPOLLOUT);
U_INTERNAL_DUMP("bwrite = %b", bwrite)
#ifdef U_THROTTLING_SUPPORT
if (UServer_Base::checkThrottlingBeforeSend(bwrite) == false) U_RETURN(U_NOTIFIER_OK);
#endif
uint32_t iBytesWrite;
write:
iBytesWrite = USocketExt::sendfile(socket, sfd, &offset, count, 0);
#ifdef U_THROTTLING_SUPPORT
if (iBytesWrite > 0) bytes_sent += iBytesWrite;
#endif
#ifdef DEBUG
if (iBytesWrite > 0) UServer_Base::stats_bytes += iBytesWrite;
#endif
if (iBytesWrite == count)
{
U_SRV_LOG_WITH_ADDR("sending sendfile response completed (%u bytes of %I) to", iBytesWrite, count);
if (bwrite)
{
UEventFd::op_mask = EPOLLIN | EPOLLRDHUP | EPOLLET;
(void) UNotifier::modify(this);
}
# ifdef DEBUG
else
{
U_INTERNAL_ASSERT_EQUALS(UEventFd::op_mask, EPOLLIN | EPOLLRDHUP | EPOLLET)
U_INTERNAL_ASSERT_DIFFERS(U_ClientImage_parallelization, U_PARALLELIZATION_PARENT)
}
# endif
offset =
count = 0;
sfd = -1;
if ((U_ClientImage_pclose(this) & U_CLOSE) != 0) UFile::close(sfd);
if ((U_ClientImage_pclose(this) & U_YES) != 0) U_RETURN(U_NOTIFIER_DELETE);
U_RETURN(U_NOTIFIER_OK);
}
if (iBytesWrite > 0)
{
U_SRV_LOG_WITH_ADDR("sent sendfile partial response (%u bytes of %I) to", iBytesWrite, count);
count -= iBytesWrite;
U_INTERNAL_ASSERT_MAJOR(count, 0)
if (isOpen() == false) goto end;
if (bwrite) U_RETURN(U_NOTIFIER_OK);
if (U_ClientImage_parallelization == U_PARALLELIZATION_CHILD)
{
wait: socket->setBlocking();
if (UNotifier::waitForWrite(socket->iSockDesc, U_TIMEOUT_MS) == 1) goto write;
goto end;
}
if (UServer_Base::startParallelization()) U_RETURN(U_NOTIFIER_DELETE); // parent
if (U_ClientImage_parallelization == U_PARALLELIZATION_CHILD) goto wait;
prepareForSendfile();
U_RETURN(U_NOTIFIER_OK);
}
end:
U_SRV_LOG("sendfile failed - sock_fd: %d sfd: %d count: %I U_ClientImage_pclose(this): %d %B", socket->iSockDesc, sfd, count, U_ClientImage_pclose(this), U_ClientImage_pclose(this));
if (U_ClientImage_parallelization != U_PARALLELIZATION_CHILD)
{
if ((U_ClientImage_pclose(this) & U_CLOSE) != 0) UFile::close(sfd);
offset =
count = 0;
sfd = -1;
U_ClientImage_pclose(this) = 0;
}
U_RETURN(U_NOTIFIER_DELETE);
}
// DEBUG
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
const char* UClientImage_Base::dump(bool _reset) const
{
*UObjectIO::os << "sfd " << sfd << '\n'
<< "bIPv6 " << bIPv6 << '\n'
<< "count " << count << '\n'
<< "offset " << offset << '\n'
<< "last_event " << last_event << '\n'
<< "socket (USocket " << (void*)socket << ")\n"
<< "body (UString " << (void*)body << ")\n"
<< "logbuf (UString " << (void*)logbuf << ")\n"
<< "rbuffer (UString " << (void*)rbuffer << ")\n"
<< "wbuffer (UString " << (void*)wbuffer << ")\n"
<< "request (UString " << (void*)request << ")\n"
<< "environment (UString " << (void*)environment << ")\n"
<< "data_pending (UString " << (void*)data_pending << ')';
if (_reset)
{
UObjectIO::output();
return UObjectIO::buffer_output;
}
return U_NULLPTR;
}
#endif