diff options
Diffstat (limited to '')
-rw-r--r-- | src/output/HttpdClient.cxx | 467 |
1 files changed, 0 insertions, 467 deletions
diff --git a/src/output/HttpdClient.cxx b/src/output/HttpdClient.cxx deleted file mode 100644 index dc337053d..000000000 --- a/src/output/HttpdClient.cxx +++ /dev/null @@ -1,467 +0,0 @@ -/* - * Copyright (C) 2003-2013 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "HttpdClient.hxx" -#include "HttpdInternal.hxx" -#include "util/ASCII.hxx" -#include "Page.hxx" -#include "IcyMetaDataServer.hxx" -#include "system/SocketError.hxx" -#include "Log.hxx" - -#include <glib.h> - -#include <assert.h> -#include <string.h> -#include <stdio.h> - -HttpdClient::~HttpdClient() -{ - if (state == RESPONSE) { - if (current_page != nullptr) - current_page->Unref(); - - for (auto page : pages) - page->Unref(); - } - - if (metadata) - metadata->Unref(); -} - -void -HttpdClient::Close() -{ - httpd->RemoveClient(*this); -} - -void -HttpdClient::LockClose() -{ - const ScopeLock protect(httpd->mutex); - Close(); -} - -void -HttpdClient::BeginResponse() -{ - assert(state != RESPONSE); - - state = RESPONSE; - current_page = nullptr; - - if (!head_method) - httpd->SendHeader(*this); -} - -/** - * Handle a line of the HTTP request. - */ -bool -HttpdClient::HandleLine(const char *line) -{ - assert(state != RESPONSE); - - if (state == REQUEST) { - if (memcmp(line, "HEAD /", 6) == 0) { - line += 6; - head_method = true; - } else if (memcmp(line, "GET /", 5) == 0) { - line += 5; - } else { - /* only GET is supported */ - LogWarning(httpd_output_domain, - "malformed request line from client"); - return false; - } - - line = strchr(line, ' '); - if (line == nullptr || memcmp(line + 1, "HTTP/", 5) != 0) { - /* HTTP/0.9 without request headers */ - - if (head_method) - return false; - - BeginResponse(); - return true; - } - - /* after the request line, request headers follow */ - state = HEADERS; - return true; - } else { - if (*line == 0) { - /* empty line: request is finished */ - - BeginResponse(); - return true; - } - - if (StringEqualsCaseASCII(line, "Icy-MetaData: 1", 15) || - StringEqualsCaseASCII(line, "Icy-MetaData:1", 14)) { - /* Send icy metadata */ - metadata_requested = metadata_supported; - return true; - } - - if (StringEqualsCaseASCII(line, "transferMode.dlna.org: Streaming", 32)) { - /* Send as dlna */ - dlna_streaming_requested = true; - /* metadata is not supported by dlna streaming, so disable it */ - metadata_supported = false; - metadata_requested = false; - return true; - } - - /* expect more request headers */ - return true; - } -} - -/** - * Sends the status line and response headers to the client. - */ -bool -HttpdClient::SendResponse() -{ - char buffer[1024]; - assert(state == RESPONSE); - - if (dlna_streaming_requested) { - snprintf(buffer, sizeof(buffer), - "HTTP/1.1 206 OK\r\n" - "Content-Type: %s\r\n" - "Content-Length: 10000\r\n" - "Content-RangeX: 0-1000000/1000000\r\n" - "transferMode.dlna.org: Streaming\r\n" - "Accept-Ranges: bytes\r\n" - "Connection: close\r\n" - "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n" - "contentFeatures.dlna.org: DLNA.ORG_OP=01;DLNA.ORG_CI=0\r\n" - "\r\n", - httpd->content_type); - - } else if (metadata_requested) { - char *metadata_header = - icy_server_metadata_header(httpd->name, httpd->genre, - httpd->website, - httpd->content_type, - metaint); - - g_strlcpy(buffer, metadata_header, sizeof(buffer)); - - delete[] metadata_header; - - } else { /* revert to a normal HTTP request */ - snprintf(buffer, sizeof(buffer), - "HTTP/1.1 200 OK\r\n" - "Content-Type: %s\r\n" - "Connection: close\r\n" - "Pragma: no-cache\r\n" - "Cache-Control: no-cache, no-store\r\n" - "\r\n", - httpd->content_type); - } - - ssize_t nbytes = SocketMonitor::Write(buffer, strlen(buffer)); - if (gcc_unlikely(nbytes < 0)) { - const SocketErrorMessage msg; - FormatWarning(httpd_output_domain, - "failed to write to client: %s", - (const char *)msg); - Close(); - return false; - } - - return true; -} - -HttpdClient::HttpdClient(HttpdOutput *_httpd, int _fd, EventLoop &_loop, - bool _metadata_supported) - :BufferedSocket(_fd, _loop), - httpd(_httpd), - state(REQUEST), - head_method(false), - dlna_streaming_requested(false), - metadata_supported(_metadata_supported), - metadata_requested(false), metadata_sent(true), - metaint(8192), /*TODO: just a std value */ - metadata(nullptr), - metadata_current_position(0), metadata_fill(0) -{ -} - -size_t -HttpdClient::GetQueueSize() const -{ - if (state != RESPONSE) - return 0; - - size_t size = 0; - for (auto page : pages) - size += page->size; - return size; -} - -void -HttpdClient::CancelQueue() -{ - if (state != RESPONSE) - return; - - for (auto page : pages) - page->Unref(); - pages.clear(); - - if (current_page == nullptr) - CancelWrite(); -} - -ssize_t -HttpdClient::TryWritePage(const Page &page, size_t position) -{ - assert(position < page.size); - - return Write(page.data + position, page.size - position); -} - -ssize_t -HttpdClient::TryWritePageN(const Page &page, size_t position, ssize_t n) -{ - return n >= 0 - ? Write(page.data + position, n) - : TryWritePage(page, position); -} - -ssize_t -HttpdClient::GetBytesTillMetaData() const -{ - if (metadata_requested && - current_page->size - current_position > metaint - metadata_fill) - return metaint - metadata_fill; - - return -1; -} - -inline bool -HttpdClient::TryWrite() -{ - const ScopeLock protect(httpd->mutex); - - assert(state == RESPONSE); - - if (current_page == nullptr) { - if (pages.empty()) { - /* another thread has removed the event source - while this thread was waiting for - httpd->mutex */ - CancelWrite(); - return true; - } - - current_page = pages.front(); - pages.pop_front(); - current_position = 0; - } - - const ssize_t bytes_to_write = GetBytesTillMetaData(); - if (bytes_to_write == 0) { - if (!metadata_sent) { - ssize_t nbytes = TryWritePage(*metadata, - metadata_current_position); - if (nbytes < 0) { - auto e = GetSocketError(); - if (IsSocketErrorAgain(e)) - return true; - - if (!IsSocketErrorClosed(e)) { - SocketErrorMessage msg(e); - FormatWarning(httpd_output_domain, - "failed to write to client: %s", - (const char *)msg); - } - - Close(); - return false; - } - - metadata_current_position += nbytes; - - if (metadata->size - metadata_current_position == 0) { - metadata_fill = 0; - metadata_current_position = 0; - metadata_sent = true; - } - } else { - guchar empty_data = 0; - - ssize_t nbytes = Write(&empty_data, 1); - if (nbytes < 0) { - auto e = GetSocketError(); - if (IsSocketErrorAgain(e)) - return true; - - if (!IsSocketErrorClosed(e)) { - SocketErrorMessage msg(e); - FormatWarning(httpd_output_domain, - "failed to write to client: %s", - (const char *)msg); - } - - Close(); - return false; - } - - metadata_fill = 0; - metadata_current_position = 0; - } - } else { - ssize_t nbytes = - TryWritePageN(*current_page, current_position, - bytes_to_write); - if (nbytes < 0) { - auto e = GetSocketError(); - if (IsSocketErrorAgain(e)) - return true; - - if (!IsSocketErrorClosed(e)) { - SocketErrorMessage msg(e); - FormatWarning(httpd_output_domain, - "failed to write to client: %s", - (const char *)msg); - } - - Close(); - return false; - } - - current_position += nbytes; - assert(current_position <= current_page->size); - - if (metadata_requested) - metadata_fill += nbytes; - - if (current_position >= current_page->size) { - current_page->Unref(); - current_page = nullptr; - - if (pages.empty()) - /* all pages are sent: remove the - event source */ - CancelWrite(); - } - } - - return true; -} - -void -HttpdClient::PushPage(Page *page) -{ - if (state != RESPONSE) - /* the client is still writing the HTTP request */ - return; - - page->Ref(); - pages.push_back(page); - - ScheduleWrite(); -} - -void -HttpdClient::PushMetaData(Page *page) -{ - if (metadata) { - metadata->Unref(); - metadata = nullptr; - } - - g_return_if_fail (page); - - page->Ref(); - metadata = page; - metadata_sent = false; -} - -bool -HttpdClient::OnSocketReady(unsigned flags) -{ - if (!BufferedSocket::OnSocketReady(flags)) - return false; - - if (flags & WRITE) - if (!TryWrite()) - return false; - - return true; -} - -BufferedSocket::InputResult -HttpdClient::OnSocketInput(void *data, size_t length) -{ - if (state == RESPONSE) { - LogWarning(httpd_output_domain, - "unexpected input from client"); - LockClose(); - return InputResult::CLOSED; - } - - char *line = (char *)data; - char *newline = (char *)memchr(line, '\n', length); - if (newline == nullptr) - return InputResult::MORE; - - ConsumeInput(newline + 1 - line); - - if (newline > line && newline[-1] == '\r') - --newline; - - /* terminate the string at the end of the line */ - *newline = 0; - - if (!HandleLine(line)) { - LockClose(); - return InputResult::CLOSED; - } - - if (state == RESPONSE) { - if (!SendResponse()) - return InputResult::CLOSED; - - if (head_method) { - LockClose(); - return InputResult::CLOSED; - } - } - - return InputResult::AGAIN; -} - -void -HttpdClient::OnSocketError(Error &&error) -{ - LogError(error); -} - -void -HttpdClient::OnSocketClosed() -{ - LockClose(); -} |