| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <https/ClientSocket.h> |
| |
| #include <https/HTTPServer.h> |
| #include <https/RunLoop.h> |
| #include <https/SafeCallbackable.h> |
| #include <https/ServerSocket.h> |
| |
| #include <glog/logging.h> |
| |
| #include <cstdlib> |
| |
| ClientSocket::ClientSocket( |
| std::shared_ptr<RunLoop> rl, |
| HTTPServer *server, |
| ServerSocket *parent, |
| const sockaddr_in &addr, |
| int sock) |
| : mRunLoop(rl), |
| mServer(server), |
| mParent(parent), |
| mRemoteAddr(addr), |
| mInBufferLen(0), |
| mSendPending(false), |
| mDisconnecting(false) { |
| if (parent->transportType() == ServerSocket::TransportType::TLS) { |
| mImplSSL = std::make_shared<SSLSocket>( |
| mRunLoop, |
| sock, |
| *server->certificate_pem_path(), |
| *server->private_key_pem_path()); |
| } else { |
| mImplPlain = std::make_shared<PlainSocket>(mRunLoop, sock); |
| } |
| } |
| |
| void ClientSocket::run() { |
| getImpl()->postRecv(makeSafeCallback(this, &ClientSocket::handleIncomingData)); |
| } |
| |
| int ClientSocket::fd() const { |
| return getImpl()->fd(); |
| } |
| |
| void ClientSocket::setWebSocketHandler( |
| std::shared_ptr<WebSocketHandler> handler) { |
| mWebSocketHandler = handler; |
| mWebSocketHandler->setClientSocket(shared_from_this()); |
| } |
| |
| void ClientSocket::handleIncomingData() { |
| mInBuffer.resize(mInBufferLen + 1024); |
| |
| ssize_t n; |
| do { |
| n = getImpl()->recv(mInBuffer.data() + mInBufferLen, 1024); |
| } while (n < 0 && errno == EINTR); |
| |
| if (n == 0) { |
| if (errno == 0) { |
| // Don't process any data if there was an actual failure. |
| // This could be an authentication failure for example... |
| // We shouldn't trust anything the client says. |
| (void)handleRequest(true /* sawEOS */); |
| } |
| |
| disconnect(); |
| return; |
| } else if (n < 0) { |
| LOG(ERROR) |
| << "recv returned error " |
| << errno |
| << " (" |
| << strerror(errno) |
| << ")"; |
| |
| mParent->onClientSocketClosed(fd()); |
| return; |
| } |
| |
| mInBufferLen += static_cast<size_t>(n); |
| const bool closeConnection = handleRequest(false /* sawEOS */); |
| |
| if (closeConnection) { |
| disconnect(); |
| } else { |
| getImpl()->postRecv( |
| makeSafeCallback(this, &ClientSocket::handleIncomingData)); |
| } |
| } |
| |
| void ClientSocket::disconnect() { |
| if (mDisconnecting) { |
| return; |
| } |
| |
| mDisconnecting = true; |
| |
| finishDisconnect(); |
| } |
| |
| void ClientSocket::finishDisconnect() { |
| if (!mSendPending) { |
| // Our output queue may now be empty, but the underlying socket |
| // implementation may still buffer something that we need to flush |
| // first. |
| getImpl()->postFlush( |
| makeSafeCallback<ClientSocket>(this, [](ClientSocket *me) { |
| me->mParent->onClientSocketClosed(me->fd()); |
| })); |
| } |
| } |
| |
| bool ClientSocket::handleRequest(bool isEOS) { |
| if (mWebSocketHandler) { |
| ssize_t n = mWebSocketHandler->handleRequest( |
| mInBuffer.data(), mInBufferLen, isEOS); |
| |
| LOG(VERBOSE) |
| << "handleRequest returned " |
| << n |
| << " when called with " |
| << mInBufferLen |
| << ", eos=" |
| << isEOS; |
| |
| if (n > 0) { |
| mInBuffer.erase(mInBuffer.begin(), mInBuffer.begin() + n); |
| mInBufferLen -= n; |
| } |
| |
| // NOTE: Do not return true, i.e. disconnect, if the json handler |
| // returns 0 bytes read, it simply means we need more data to continue. |
| return n < 0; |
| } |
| |
| size_t len = mInBufferLen; |
| |
| if (!isEOS) { |
| static const char kPattern[] = "\r\n\r\n"; |
| |
| // Don't count the trailing NUL. |
| static constexpr size_t kPatternLength = sizeof(kPattern) - 1; |
| |
| size_t i = 0; |
| while (i + kPatternLength <= mInBufferLen |
| && memcmp(mInBuffer.data() + i, kPattern, kPatternLength)) { |
| ++i; |
| } |
| |
| if (i + kPatternLength > mInBufferLen) { |
| return false; |
| } |
| |
| // Found a match. |
| len = i + kPatternLength; |
| } |
| |
| const bool closeConnection = |
| mServer->handleSingleRequest(this, mInBuffer.data(), len, isEOS); |
| |
| mInBuffer.clear(); |
| mInBufferLen = 0; |
| |
| return closeConnection; |
| } |
| |
| void ClientSocket::queueOutputData(const uint8_t *data, size_t size) { |
| std::copy(data, data + size, std::back_inserter(mOutBuffer)); |
| |
| if (!mSendPending) { |
| mSendPending = true; |
| getImpl()->postSend(makeSafeCallback(this, &ClientSocket::sendOutputData)); |
| } |
| } |
| |
| sockaddr_in ClientSocket::remoteAddr() const { |
| return mRemoteAddr; |
| } |
| |
| void ClientSocket::queueResponse( |
| const std::string &response, const std::string &body) { |
| std::copy(response.begin(), response.end(), std::back_inserter(mOutBuffer)); |
| std::copy(body.begin(), body.end(), std::back_inserter(mOutBuffer)); |
| |
| if (!mSendPending) { |
| mSendPending = true; |
| getImpl()->postSend(makeSafeCallback(this, &ClientSocket::sendOutputData)); |
| } |
| } |
| |
| void ClientSocket::sendOutputData() { |
| mSendPending = false; |
| |
| const size_t size = mOutBuffer.size(); |
| size_t offset = 0; |
| |
| while (offset < size) { |
| ssize_t n = getImpl()->send(mOutBuffer.data() + offset, size - offset); |
| |
| if (n < 0) { |
| if (errno == EINTR) { |
| continue; |
| } |
| |
| assert(!"Should not be here"); |
| } else if (n == 0) { |
| // The remote seems gone, clear the output buffer and disconnect. |
| offset = size; |
| mDisconnecting = true; |
| break; |
| } |
| |
| offset += static_cast<size_t>(n); |
| } |
| |
| mOutBuffer.erase(mOutBuffer.begin(), mOutBuffer.begin() + offset); |
| |
| if (!mOutBuffer.empty()) { |
| mSendPending = true; |
| getImpl()->postSend(makeSafeCallback(this, &ClientSocket::sendOutputData)); |
| return; |
| } |
| |
| if (mDisconnecting) { |
| finishDisconnect(); |
| } |
| } |
| |