/* Destroy a connection. This removes the connection from the list of connections. */ PUBLIC void httpDestroyConn(HttpConn *conn) { if (!conn->destroyed && !conn->borrowed) { HTTP_NOTIFY(conn, HTTP_EVENT_DESTROY, 0); if (httpServerConn(conn)) { httpMonitorEvent(conn, HTTP_COUNTER_ACTIVE_CONNECTIONS, -1); if (conn->activeRequest) { httpMonitorEvent(conn, HTTP_COUNTER_ACTIVE_REQUESTS, -1); conn->activeRequest = 0; } } httpRemoveConn(conn); conn->input = 0; if (conn->tx) { httpClosePipeline(conn); } if (conn->sock) { mprCloseSocket(conn->sock, 0); } if (conn->dispatcher && conn->dispatcher->flags & MPR_DISPATCHER_AUTO) { mprDestroyDispatcher(conn->dispatcher); } conn->destroyed = 1; } }
void httpMatchHost(HttpConn *conn) { MprSocket *listenSock; HttpEndpoint *endpoint; HttpHost *host; Http *http; http = conn->http; listenSock = conn->sock->listenSock; if ((endpoint = httpLookupEndpoint(http, listenSock->ip, listenSock->port)) == 0) { mprError("No listening endpoint for request from %s:%d", listenSock->ip, listenSock->port); mprCloseSocket(conn->sock, 0); return; } if (httpHasNamedVirtualHosts(endpoint)) { host = httpLookupHostOnEndpoint(endpoint, conn->rx->hostHeader); } else { host = mprGetFirstItem(endpoint->hosts); } if (host == 0) { httpSetConnHost(conn, 0); httpError(conn, HTTP_CODE_NOT_FOUND, "No host to serve request. Searching for %s", conn->rx->hostHeader); conn->host = mprGetFirstItem(endpoint->hosts); return; } if (conn->rx->traceLevel >= 0) { mprLog(conn->rx->traceLevel, "Select host: \"%s\"", host->name); } conn->host = host; }
void httpDestroyEndpoint(HttpEndpoint *endpoint) { destroyEndpointConnections(endpoint); if (endpoint->sock) { mprCloseSocket(endpoint->sock, 0); endpoint->sock = 0; } httpRemoveEndpoint(MPR->httpService, endpoint); }
void httpStopEndpoint(HttpEndpoint *endpoint) { HttpHost *host; int next; for (ITERATE_ITEMS(endpoint->hosts, host, next)) { httpStopHost(host); } if (endpoint->sock) { mprCloseSocket(endpoint->sock, 0); endpoint->sock = 0; } }
/* * Cleanup a connection. Invoked automatically whenever the connection is freed. */ static int connectionDestructor(MaConn *conn) { mprAssert(conn); mprAssert(conn->host); mprAssert(conn->sock); /* * Must remove from the connection list first. This ensures that the host timer will not find the connection * nor mess with it anymore. */ maRemoveConn(conn->host, conn); if (conn->sock) { mprLog(conn, 4, "Closing connection fd %d", conn->sock->fd); mprCloseSocket(conn->sock, conn->connectionFailed ? 0 : MPR_SOCKET_GRACEFUL); mprFree(conn->sock); } return 0; }
static HttpConn *openConnection(HttpConn *conn, struct MprSsl *ssl) { Http *http; HttpUri *uri; MprSocket *sp; char *ip; int port, rc; assert(conn); http = conn->http; uri = conn->tx->parsedUri; if (!uri->host) { ip = (http->proxyHost) ? http->proxyHost : http->defaultClientHost; port = (http->proxyHost) ? http->proxyPort : http->defaultClientPort; } else { ip = (http->proxyHost) ? http->proxyHost : uri->host; port = (http->proxyHost) ? http->proxyPort : uri->port; } if (port == 0) { port = (uri->secure) ? 443 : 80; } if (conn && conn->sock) { if (conn->keepAliveCount-- <= 0 || port != conn->port || strcmp(ip, conn->ip) != 0 || uri->secure != (conn->sock->ssl != 0) || conn->sock->ssl != ssl) { /* Cannot reuse current socket. Close and open a new one below. */ mprCloseSocket(conn->sock, 0); conn->sock = 0; } else { httpTrace(conn, "connection.reuse", "context", "keepAlive:%d", conn->keepAliveCount); } } if (conn->sock) { return conn; } /* New socket */ if ((sp = mprCreateSocket()) == 0) { httpError(conn, HTTP_ABORT | HTTP_CODE_COMMS_ERROR, "Cannot create socket for %s", httpUriToString(uri, 0)); return 0; } if ((rc = mprConnectSocket(sp, ip, port, MPR_SOCKET_NODELAY)) < 0) { httpError(conn, HTTP_ABORT | HTTP_CODE_COMMS_ERROR, "Cannot open socket on %s:%d", ip, port); return 0; } conn->sock = sp; conn->ip = sclone(ip); conn->port = port; conn->keepAliveCount = (conn->limits->keepAliveMax) ? conn->limits->keepAliveMax : 0; #if ME_COM_SSL /* Must be done even if using keep alive for repeat SSL requests */ if (uri->secure) { char *peerName; if (ssl == 0) { ssl = mprCreateSsl(0); } peerName = isdigit(uri->host[0]) ? 0 : uri->host; if (mprUpgradeSocket(sp, ssl, peerName) < 0) { conn->errorMsg = sp->errorMsg; httpTrace(conn, "connection.upgrade.error", "error", "msg:'Cannot perform SSL upgrade. %s'", conn->errorMsg); return 0; } if (sp->peerCert) { httpTrace(conn, "context", "connection.ssl", "msg:'Connection secured with peer certificate', " \ "secure:true,cipher:'%s',peerName:'%s',subject:'%s',issuer:'%s'", sp->cipher, sp->peerName, sp->peerCert, sp->peerCertIssuer); } } #endif #if ME_HTTP_WEB_SOCKETS if (uri->webSockets && httpUpgradeWebSocket(conn) < 0) { conn->errorMsg = sp->errorMsg; return 0; } #endif httpTrace(conn, "connection.peer", "context", "peer:'%s:%d'", conn->ip, conn->port); return conn; }
/* Accept a new client connection on a new socket. If multithreaded, this will come in on a worker thread dedicated to this connection. This is called from the listen wait handler. */ HttpConn *httpAcceptConn(HttpEndpoint *endpoint, MprEvent *event) { HttpConn *conn; MprSocket *sock; MprDispatcher *dispatcher; MprEvent e; int level; mprAssert(endpoint); mprAssert(event); /* This will block in sync mode until a connection arrives */ if ((sock = mprAcceptSocket(endpoint->sock)) == 0) { if (endpoint->sock->handler) { mprEnableSocketEvents(endpoint->sock, MPR_READABLE); } return 0; } if (endpoint->ssl) { if (mprUpgradeSocket(sock, endpoint->ssl, 1) < 0) { mprCloseSocket(sock, 0); return 0; } } if (endpoint->sock->handler) { /* Re-enable events on the listen socket */ mprEnableSocketEvents(endpoint->sock, MPR_READABLE); } dispatcher = event->dispatcher; if (mprShouldDenyNewRequests()) { mprCloseSocket(sock, 0); return 0; } if ((conn = httpCreateConn(endpoint->http, endpoint, dispatcher)) == 0) { mprCloseSocket(sock, 0); return 0; } conn->notifier = endpoint->notifier; conn->async = endpoint->async; conn->endpoint = endpoint; conn->sock = sock; conn->port = sock->port; conn->ip = sclone(sock->ip); conn->secure = (endpoint->ssl != 0); if (!httpValidateLimits(endpoint, HTTP_VALIDATE_OPEN_CONN, conn)) { conn->endpoint = 0; httpDestroyConn(conn); return 0; } mprAssert(conn->state == HTTP_STATE_BEGIN); httpSetState(conn, HTTP_STATE_CONNECTED); if ((level = httpShouldTrace(conn, HTTP_TRACE_RX, HTTP_TRACE_CONN, NULL)) >= 0) { mprLog(level, "### Incoming connection from %s:%d to %s:%d %s", conn->ip, conn->port, sock->acceptIp, sock->acceptPort, conn->secure ? "(secure)" : ""); } e.mask = MPR_READABLE; e.timestamp = conn->http->now; (conn->ioCallback)(conn, &e); return conn; }
PUBLIC int httpConnect(HttpStream *stream, cchar *method, cchar *url, MprSsl *ssl) { HttpNet *net; HttpTx *tx; HttpUri *uri; cchar *ip, *protocol; int port; assert(stream); assert(method && *method); assert(url && *url); net = stream->net; if (httpServerStream(stream)) { mprLog("client error", 0, "Cannot call httpConnect() in a server"); return MPR_ERR_BAD_STATE; } if (net->protocol <= 0) { mprLog("client error", 0, "HTTP protocol to use has not been defined"); return MPR_ERR_BAD_STATE; } if (stream->tx == 0 || stream->state != HTTP_STATE_BEGIN) { httpResetClientStream(stream, 0); } tx = stream->tx; tx->method = supper(method); stream->authRequested = 0; stream->startMark = mprGetHiResTicks(); if ((uri = tx->parsedUri = httpCreateUri(url, HTTP_COMPLETE_URI_PATH)) == 0) { return MPR_ERR_BAD_ARGS; } ssl = uri->secure ? (ssl ? ssl : mprCreateSsl(0)) : 0; httpGetUriAddress(uri, &ip, &port); if (net->sock) { if (net->error) { mprCloseSocket(net->sock, 0); net->sock = 0; } else if (canUse(net, stream, uri, ssl, ip, port)) { httpLog(net->trace, "client.connection.reuse", "context", "reuse:%d", stream->keepAliveCount); } else { if (net->protocol >= 2) { if (mprGetListLength(net->streams) > 1) { httpError(stream, HTTP_CODE_COMMS_ERROR, "Cannot use network for %s due to other existing requests", ip); return MPR_ERR_CANT_FIND; } } else { mprCloseSocket(net->sock, 0); net->sock = 0; } } } if (!net->sock) { if (httpConnectNet(net, ip, port, ssl) < 0) { return MPR_ERR_CANT_CONNECT; } stream->net = net; stream->sock = net->sock; stream->ip = net->ip; stream->port = net->port; stream->keepAliveCount = (net->protocol >= 2) ? 0 : stream->limits->keepAliveMax; #if ME_HTTP_WEB_SOCKETS if (net->protocol == 1 && uri->webSockets && httpUpgradeWebSocket(stream) < 0) { stream->errorMsg = net->errorMsg = net->sock->errorMsg; return 0; } #endif } httpCreatePipeline(stream); setDefaultHeaders(stream); httpSetState(stream, HTTP_STATE_CONNECTED); protocol = net->protocol < 2 ? "HTTP/1.1" : "HTTP/2"; httpLog(net->trace, "client.request", "request", "method='%s', url='%s', protocol='%s'", tx->method, url, protocol); return 0; }
/* Accept a new client connection on a new socket. This will come in on a worker thread with a new dispatcher dedicated to this connection. */ PUBLIC HttpConn *httpAcceptConn(HttpEndpoint *endpoint, MprEvent *event) { Http *http; HttpConn *conn; HttpAddress *address; MprSocket *sock; int64 value; assert(event); assert(event->dispatcher); assert(endpoint); sock = event->sock; http = endpoint->http; if (mprShouldDenyNewRequests()) { mprCloseSocket(sock, 0); return 0; } if ((conn = httpCreateConn(endpoint, event->dispatcher)) == 0) { mprCloseSocket(sock, 0); return 0; } conn->notifier = endpoint->notifier; conn->async = endpoint->async; conn->endpoint = endpoint; conn->sock = sock; conn->port = sock->port; conn->ip = sclone(sock->ip); if ((value = httpMonitorEvent(conn, HTTP_COUNTER_ACTIVE_CONNECTIONS, 1)) > conn->limits->connectionsMax) { httpTrace(conn, "connection.accept.error", "error", "msg:'Too many concurrent connections',active:%d,max:%d", (int) value, conn->limits->connectionsMax); httpDestroyConn(conn); return 0; } if (mprGetHashLength(http->addresses) > conn->limits->clientMax) { httpTrace(conn, "connection.accept.error", "error", "msg:'Too many concurrent clients',active:%d,max:%d", mprGetHashLength(http->addresses), conn->limits->clientMax); httpDestroyConn(conn); return 0; } address = conn->address; if (address && address->banUntil) { if (address->banUntil < http->now) { httpTrace(conn, "monitor.ban.stop", "context", "client:'%s'", conn->ip); address->banUntil = 0; } else { if (address->banStatus) { httpError(conn, HTTP_CLOSE | address->banStatus, "Connection refused, client banned: %s", address->banMsg ? address->banMsg : ""); } else { httpDestroyConn(conn); return 0; } } } if (endpoint->ssl) { if (mprUpgradeSocket(sock, endpoint->ssl, 0) < 0) { httpDisconnect(conn); httpTrace(conn, "connection.upgrade.error", "error", "msg:'Cannot upgrade socket. %s'", sock->errorMsg); httpMonitorEvent(conn, HTTP_COUNTER_SSL_ERRORS, 1); httpDestroyConn(conn); return 0; } } assert(conn->state == HTTP_STATE_BEGIN); httpSetState(conn, HTTP_STATE_CONNECTED); httpTrace(conn, "connection.accept.new", "context", "peer:'%s',endpoint:'%s:%d'", conn->ip, sock->acceptIp, sock->acceptPort); event->mask = MPR_READABLE; event->timestamp = conn->http->now; (conn->ioCallback)(conn, event); return conn; }