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