1 /* 2 * Copyright 2003-2021 The Music Player Daemon Project 3 * http://www.musicpd.org 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 20 #ifndef MPD_OUTPUT_HTTPD_CLIENT_HXX 21 #define MPD_OUTPUT_HTTPD_CLIENT_HXX 22 23 #include "Page.hxx" 24 #include "event/BufferedSocket.hxx" 25 #include "util/Compiler.h" 26 27 #include <boost/intrusive/link_mode.hpp> 28 #include <boost/intrusive/list_hook.hpp> 29 30 #include <cstddef> 31 #include <list> 32 #include <queue> 33 34 class UniqueSocketDescriptor; 35 class HttpdOutput; 36 37 class HttpdClient final 38 : BufferedSocket, 39 public boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>> { 40 /** 41 * The httpd output object this client is connected to. 42 */ 43 HttpdOutput &httpd; 44 45 /** 46 * The current state of the client. 47 */ 48 enum class State { 49 /** reading the request line */ 50 REQUEST, 51 52 /** reading the request headers */ 53 HEADERS, 54 55 /** sending the HTTP response */ 56 RESPONSE, 57 } state = State::REQUEST; 58 59 /** 60 * A queue of #Page objects to be sent to the client. 61 */ 62 std::queue<PagePtr, std::list<PagePtr>> pages; 63 64 /** 65 * The sum of all page sizes in #pages. 66 */ 67 size_t queue_size = 0; 68 69 /** 70 * The #page which is currently being sent to the client. 71 */ 72 PagePtr current_page; 73 74 /** 75 * The amount of bytes which were already sent from 76 * #current_page. 77 */ 78 size_t current_position; 79 80 /** 81 * Is this a HEAD request? 82 */ 83 bool head_method = false; 84 85 /** 86 * Should we reject this request? 87 */ 88 bool should_reject = false; 89 90 /* ICY */ 91 92 /** 93 * Do we support sending Icy-Metadata to the client? This is 94 * disabled if the httpd audio output uses encoder tags. 95 */ 96 bool metadata_supported; 97 98 /** 99 * If we should sent icy metadata. 100 */ 101 bool metadata_requested = false; 102 103 /** 104 * If the current metadata was already sent to the client. 105 * 106 * Initialized to `true` because there is no metadata #Page 107 * pending to be sent. 108 */ 109 bool metadata_sent = true; 110 111 /** 112 * The amount of streaming data between each metadata block 113 */ 114 unsigned metaint = 8192; /*TODO: just a std value */ 115 116 /** 117 * The metadata as #Page which is currently being sent to the client. 118 */ 119 PagePtr metadata; 120 121 /* 122 * The amount of bytes which were already sent from the metadata. 123 */ 124 size_t metadata_current_position = 0; 125 126 /** 127 * The amount of streaming data sent to the client 128 * since the last icy information was sent. 129 */ 130 unsigned metadata_fill = 0; 131 132 public: 133 /** 134 * @param httpd the HTTP output device 135 * @param _fd the socket file descriptor 136 */ 137 HttpdClient(HttpdOutput &httpd, UniqueSocketDescriptor _fd, 138 EventLoop &_loop, 139 bool _metadata_supported); 140 141 /** 142 * Note: this does not remove the client from the 143 * #HttpdOutput object. 144 */ 145 ~HttpdClient() noexcept; 146 147 /** 148 * Frees the client and removes it from the server's client list. 149 * 150 * Caller must lock the mutex. 151 */ 152 void Close() noexcept; 153 154 void LockClose() noexcept; 155 156 /** 157 * Clears the page queue. 158 */ 159 void CancelQueue() noexcept; 160 161 /** 162 * Handle a line of the HTTP request. 163 */ 164 bool HandleLine(const char *line) noexcept; 165 166 /** 167 * Switch the client to #State::RESPONSE. 168 */ 169 void BeginResponse() noexcept; 170 171 /** 172 * Sends the status line and response headers to the client. 173 */ 174 bool SendResponse() noexcept; 175 176 gcc_pure 177 ssize_t GetBytesTillMetaData() const noexcept; 178 179 ssize_t TryWritePage(const Page &page, size_t position) noexcept; 180 ssize_t TryWritePageN(const Page &page, 181 size_t position, ssize_t n) noexcept; 182 183 bool TryWrite() noexcept; 184 185 /** 186 * Appends a page to the client's queue. 187 */ 188 void PushPage(PagePtr page) noexcept; 189 190 /** 191 * Sends the passed metadata. 192 */ 193 void PushMetaData(PagePtr page) noexcept; 194 195 private: 196 void ClearQueue() noexcept; 197 198 protected: 199 /* virtual methods from class BufferedSocket */ 200 void OnSocketReady(unsigned flags) noexcept override; 201 202 InputResult OnSocketInput(void *data, size_t length) noexcept override; 203 void OnSocketError(std::exception_ptr ep) noexcept override; 204 void OnSocketClosed() noexcept override; 205 }; 206 207 #endif 208