blob: bace14de20fe23a62d7ea6fe365fb0dbc01fa27f [file] [log] [blame]
Austin Schuh9d823002019-04-14 12:53:17 -07001// Copyright (c) 2013-2017, Matt Godbolt
Austin Schuh24adb6b2015-09-06 17:37:40 -07002// All rights reserved.
Austin Schuh9d823002019-04-14 12:53:17 -07003//
4// Redistribution and use in source and binary forms, with or without
Austin Schuh24adb6b2015-09-06 17:37:40 -07005// modification, are permitted provided that the following conditions are met:
Austin Schuh9d823002019-04-14 12:53:17 -07006//
7// Redistributions of source code must retain the above copyright notice, this
Austin Schuh24adb6b2015-09-06 17:37:40 -07008// list of conditions and the following disclaimer.
Austin Schuh9d823002019-04-14 12:53:17 -07009//
10// Redistributions in binary form must reproduce the above copyright notice,
11// this list of conditions and the following disclaimer in the documentation
Austin Schuh24adb6b2015-09-06 17:37:40 -070012// and/or other materials provided with the distribution.
Austin Schuh9d823002019-04-14 12:53:17 -070013//
14// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
Austin Schuh24adb6b2015-09-06 17:37:40 -070024// POSSIBILITY OF SUCH DAMAGE.
25
26#include "internal/Config.h"
27#include "internal/Embedded.h"
28#include "internal/HeaderMap.h"
29#include "internal/HybiAccept.h"
30#include "internal/HybiPacketDecoder.h"
31#include "internal/LogStream.h"
32#include "internal/PageRequest.h"
Austin Schuh9d823002019-04-14 12:53:17 -070033#include "internal/RaiiFd.h"
Austin Schuh24adb6b2015-09-06 17:37:40 -070034
35#include "md5/md5.h"
36
37#include "seasocks/Connection.h"
38#include "seasocks/Credentials.h"
39#include "seasocks/Logger.h"
40#include "seasocks/PageHandler.h"
41#include "seasocks/Server.h"
42#include "seasocks/StringUtil.h"
43#include "seasocks/ToString.h"
Austin Schuh9d823002019-04-14 12:53:17 -070044#include "seasocks/ResponseWriter.h"
45#include "seasocks/ZlibContext.h"
Austin Schuh24adb6b2015-09-06 17:37:40 -070046
Austin Schuh9d823002019-04-14 12:53:17 -070047#include <sys/socket.h>
Austin Schuh24adb6b2015-09-06 17:37:40 -070048#include <sys/stat.h>
49#include <sys/types.h>
50
Austin Schuh9d823002019-04-14 12:53:17 -070051#include <algorithm>
52#include <cassert>
53#include <cctype>
54#include <cerrno>
Austin Schuh24adb6b2015-09-06 17:37:40 -070055#include <fcntl.h>
56#include <fstream>
57#include <iostream>
58#include <limits>
Austin Schuh9d823002019-04-14 12:53:17 -070059#include <memory>
Austin Schuh24adb6b2015-09-06 17:37:40 -070060#include <sstream>
Austin Schuh9d823002019-04-14 12:53:17 -070061#include <cstdio>
62#include <cstring>
Austin Schuh24adb6b2015-09-06 17:37:40 -070063#include <unistd.h>
Austin Schuh9d823002019-04-14 12:53:17 -070064#include <byteswap.h>
Austin Schuh24adb6b2015-09-06 17:37:40 -070065#include <unordered_map>
Austin Schuh9d823002019-04-14 12:53:17 -070066#include <memory>
Austin Schuh24adb6b2015-09-06 17:37:40 -070067
68namespace {
69
70uint32_t parseWebSocketKey(const std::string& key) {
71 uint32_t keyNumber = 0;
72 uint32_t numSpaces = 0;
73 for (auto c : key) {
74 if (c >= '0' && c <= '9') {
75 keyNumber = keyNumber * 10 + c - '0';
76 } else if (c == ' ') {
77 ++numSpaces;
78 }
79 }
80 return numSpaces > 0 ? keyNumber / numSpaces : 0;
81}
82
Austin Schuh9d823002019-04-14 12:53:17 -070083char* extractLine(uint8_t*& first, uint8_t* last, char** colon = nullptr) {
Austin Schuh24adb6b2015-09-06 17:37:40 -070084 for (uint8_t* ptr = first; ptr < last - 1; ++ptr) {
85 if (ptr[0] == '\r' && ptr[1] == '\n') {
86 ptr[0] = 0;
87 uint8_t* result = first;
88 first = ptr + 2;
Austin Schuh9d823002019-04-14 12:53:17 -070089 return reinterpret_cast<char*>(result);
Austin Schuh24adb6b2015-09-06 17:37:40 -070090 }
Austin Schuh9d823002019-04-14 12:53:17 -070091 if (colon && ptr[0] == ':' && *colon == nullptr) {
92 *colon = reinterpret_cast<char*>(ptr);
Austin Schuh24adb6b2015-09-06 17:37:40 -070093 }
94 }
Austin Schuh9d823002019-04-14 12:53:17 -070095 return nullptr;
Austin Schuh24adb6b2015-09-06 17:37:40 -070096}
97
Austin Schuh24adb6b2015-09-06 17:37:40 -070098const std::unordered_map<std::string, std::string> contentTypes = {
Austin Schuh9d823002019-04-14 12:53:17 -070099 {"txt", "text/plain"},
100 {"css", "text/css"},
101 {"csv", "text/csv"},
102 {"htm", "text/html"},
103 {"html", "text/html"},
104 {"xml", "text/xml"},
105 {"js", "text/javascript"}, // Technically it should be application/javascript (RFC 4329), but IE8 struggles with that
106 {"xhtml", "application/xhtml+xml"},
107 {"json", "application/json"},
108 {"pdf", "application/pdf"},
109 {"zip", "application/zip"},
110 {"tar", "application/x-tar"},
111 {"gif", "image/gif"},
112 {"jpeg", "image/jpeg"},
113 {"jpg", "image/jpeg"},
114 {"tiff", "image/tiff"},
115 {"tif", "image/tiff"},
116 {"png", "image/png"},
117 {"svg", "image/svg+xml"},
118 {"ico", "image/x-icon"},
119 {"swf", "application/x-shockwave-flash"},
120 {"mp3", "audio/mpeg"},
121 {"wav", "audio/x-wav"},
122 {"ttf", "font/ttf"},
Austin Schuh24adb6b2015-09-06 17:37:40 -0700123};
124
125std::string getExt(const std::string& path) {
126 auto lastDot = path.find_last_of('.');
Austin Schuh9d823002019-04-14 12:53:17 -0700127 if (lastDot != std::string::npos) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700128 return path.substr(lastDot + 1);
129 }
130 return "";
131}
132
133const std::string& getContentType(const std::string& path) {
134 auto it = contentTypes.find(getExt(path));
135 if (it != contentTypes.end()) {
136 return it->second;
137 }
138 static const std::string defaultType("text/html");
139 return defaultType;
140}
141
142// Cacheability is only set for resources that *REQUIRE* caching for browser support reasons.
143// It's off for everything else to save on browser reload headaches during development, at
144// least until we support ETags or If-Modified-Since: type checking, which we may never do.
145bool isCacheable(const std::string& path) {
146 std::string extension = getExt(path);
147 if (extension == "mp3" || extension == "wav") {
148 return true;
149 }
150 return false;
151}
152
Austin Schuh9d823002019-04-14 12:53:17 -0700153constexpr size_t ReadWriteBufferSize = 16 * 1024;
154constexpr size_t MaxWebsocketMessageSize = 16384;
155constexpr size_t MaxHeadersSize = 64 * 1024;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700156
157class PrefixWrapper : public seasocks::Logger {
158 std::string _prefix;
159 std::shared_ptr<Logger> _logger;
Austin Schuh9d823002019-04-14 12:53:17 -0700160
Austin Schuh24adb6b2015-09-06 17:37:40 -0700161public:
162 PrefixWrapper(const std::string& prefix, std::shared_ptr<Logger> logger)
Austin Schuh9d823002019-04-14 12:53:17 -0700163 : _prefix(prefix), _logger(logger) {
164 }
Austin Schuh24adb6b2015-09-06 17:37:40 -0700165
Austin Schuh9d823002019-04-14 12:53:17 -0700166 virtual void log(Level level, const char* message) override {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700167 _logger->log(level, (_prefix + message).c_str());
168 }
169};
170
Austin Schuh9d823002019-04-14 12:53:17 -0700171bool hasConnectionType(const std::string& connection, const std::string& type) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700172 for (auto conType : seasocks::split(connection, ',')) {
173 while (!conType.empty() && isspace(conType[0]))
174 conType = conType.substr(1);
175 if (seasocks::caseInsensitiveSame(conType, type))
176 return true;
177 }
178 return false;
179}
180
Austin Schuh9d823002019-04-14 12:53:17 -0700181} // namespace
Austin Schuh24adb6b2015-09-06 17:37:40 -0700182
183namespace seasocks {
184
Austin Schuh9d823002019-04-14 12:53:17 -0700185struct Connection::Writer : ResponseWriter {
186 Connection* _connection;
187 explicit Writer(Connection& connection)
188 : _connection(&connection) {
189 }
190
191 void detach() {
192 _connection = nullptr;
193 }
194
195 void begin(ResponseCode responseCode, TransferEncoding encoding) override {
196 if (_connection)
197 _connection->begin(responseCode, encoding);
198 }
199 void header(const std::string& header, const std::string& value) override {
200 if (_connection)
201 _connection->header(header, value);
202 }
203 void payload(const void* data, size_t size, bool flush) override {
204 if (_connection)
205 _connection->payload(data, size, flush);
206 }
207 void finish(bool keepConnectionOpen) override {
208 if (_connection)
209 _connection->finish(keepConnectionOpen);
210 }
211 void error(ResponseCode responseCode, const std::string& payload) override {
212 if (_connection)
213 _connection->error(responseCode, payload);
214 }
215
216 bool isActive() const override {
217 return _connection;
218 }
219};
220
Austin Schuh24adb6b2015-09-06 17:37:40 -0700221Connection::Connection(
Austin Schuh9d823002019-04-14 12:53:17 -0700222 std::shared_ptr<Logger> logger,
223 ServerImpl& server,
224 int fd,
225 const sockaddr_in& address)
226 : _logger(std::make_shared<PrefixWrapper>(formatAddress(address) + " : ", logger)),
227 _server(server),
228 _fd(fd),
229 _shutdown(false),
230 _hadSendError(false),
231 _closeOnEmpty(false),
232 _registeredForWriteEvents(false),
233 _address(address),
234 _bytesSent(0),
235 _bytesReceived(0),
236 _shutdownByUser(false),
237 _transferEncoding(TransferEncoding::Raw),
238 _chunk(0u),
239 _writer(std::make_shared<Writer>(*this)),
240 _state(State::READING_HEADERS) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700241}
242
243Connection::~Connection() {
244 _server.checkThread();
245 finalise();
246}
247
248void Connection::close() {
249 // This is the user-side close requests ONLY! You should Call closeInternal
250 _shutdownByUser = true;
251 closeInternal();
252}
253
254void Connection::closeWhenEmpty() {
255 if (_outBuf.empty()) {
256 closeInternal();
257 } else {
258 _closeOnEmpty = true;
259 }
260}
261
262void Connection::closeInternal() {
263 // It only actually only calls shutdown on the socket,
264 // leaving the close of the FD and the cleanup until the destructor runs.
265 _server.checkThread();
266 if (_fd != -1 && !_shutdown && ::shutdown(_fd, SHUT_RDWR) == -1) {
267 LS_WARNING(_logger, "Unable to shutdown socket : " << getLastError());
268 }
269 _shutdown = true;
270}
271
272
273void Connection::finalise() {
Austin Schuh9d823002019-04-14 12:53:17 -0700274 if (_response) {
275 _response->cancel();
276 _response.reset();
277 _writer->detach();
278 _writer.reset();
279 }
Austin Schuh24adb6b2015-09-06 17:37:40 -0700280 if (_webSocketHandler) {
281 _webSocketHandler->onDisconnect(this);
282 _webSocketHandler.reset();
283 }
284 if (_fd != -1) {
285 _server.remove(this);
286 LS_DEBUG(_logger, "Closing socket");
287 ::close(_fd);
288 }
289 _fd = -1;
290}
291
Austin Schuh9d823002019-04-14 12:53:17 -0700292ssize_t Connection::safeSend(const void* data, size_t size) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700293 if (_fd == -1 || _hadSendError || _shutdown) {
294 // Ignore further writes to the socket, it's already closed or has been shutdown
295 return -1;
296 }
Austin Schuh9d823002019-04-14 12:53:17 -0700297 auto sendResult = ::send(_fd, data, size, MSG_NOSIGNAL);
Austin Schuh24adb6b2015-09-06 17:37:40 -0700298 if (sendResult == -1) {
299 if (errno == EAGAIN || errno == EWOULDBLOCK) {
300 // Treat this as if zero bytes were written.
301 return 0;
302 }
303 LS_WARNING(_logger, "Unable to write to socket : " << getLastError() << " - disabling further writes");
304 closeInternal();
305 } else {
306 _bytesSent += sendResult;
307 }
308 return sendResult;
309}
310
311bool Connection::write(const void* data, size_t size, bool flushIt) {
312 if (closed() || _closeOnEmpty) {
313 return false;
314 }
315 if (size) {
Austin Schuh9d823002019-04-14 12:53:17 -0700316 ssize_t bytesSent = 0;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700317 if (_outBuf.empty() && flushIt) {
318 // Attempt fast path, send directly.
319 bytesSent = safeSend(data, size);
320 if (bytesSent == static_cast<int>(size)) {
321 // We sent directly.
322 return true;
323 }
324 if (bytesSent == -1) {
325 return false;
326 }
327 }
328 size_t bytesToBuffer = size - bytesSent;
329 size_t endOfBuffer = _outBuf.size();
330 size_t newBufferSize = endOfBuffer + bytesToBuffer;
Austin Schuh9d823002019-04-14 12:53:17 -0700331 if (newBufferSize >= _server.clientBufferSize()) {
332 LS_WARNING(_logger, "Closing connection: buffer size too large ("
333 << newBufferSize << " >= " << _server.clientBufferSize() << ")");
Austin Schuh24adb6b2015-09-06 17:37:40 -0700334 closeInternal();
335 return false;
336 }
337 _outBuf.resize(newBufferSize);
338 memcpy(&_outBuf[endOfBuffer], reinterpret_cast<const uint8_t*>(data) + bytesSent, bytesToBuffer);
339 }
340 if (flushIt) {
341 return flush();
342 }
343 return true;
344}
345
346bool Connection::bufferLine(const char* line) {
Austin Schuh9d823002019-04-14 12:53:17 -0700347 static const char crlf[] = {'\r', '\n'};
348 if (!write(line, strlen(line), false))
349 return false;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700350 return write(crlf, 2, false);
351}
352
353bool Connection::bufferLine(const std::string& line) {
354 std::string lineAndCrlf = line + "\r\n";
355 return write(lineAndCrlf.c_str(), lineAndCrlf.length(), false);
356}
357
358void Connection::handleDataReadyForRead() {
359 if (closed()) {
360 return;
361 }
362 size_t curSize = _inBuf.size();
363 _inBuf.resize(curSize + ReadWriteBufferSize);
Austin Schuh9d823002019-04-14 12:53:17 -0700364 auto result = ::read(_fd, &_inBuf[curSize], ReadWriteBufferSize);
Austin Schuh24adb6b2015-09-06 17:37:40 -0700365 if (result == -1) {
366 LS_WARNING(_logger, "Unable to read from socket : " << getLastError());
367 return;
368 }
369 if (result == 0) {
370 LS_DEBUG(_logger, "Remote end closed connection");
371 closeInternal();
372 return;
373 }
374 _bytesReceived += result;
375 _inBuf.resize(curSize + result);
376 handleNewData();
377}
378
379void Connection::handleDataReadyForWrite() {
380 if (closed()) {
381 return;
382 }
383 flush();
384}
385
386bool Connection::flush() {
387 if (_outBuf.empty()) {
388 return true;
389 }
Austin Schuh9d823002019-04-14 12:53:17 -0700390 auto numSent = safeSend(&_outBuf[0], _outBuf.size());
Austin Schuh24adb6b2015-09-06 17:37:40 -0700391 if (numSent == -1) {
392 return false;
393 }
394 _outBuf.erase(_outBuf.begin(), _outBuf.begin() + numSent);
Austin Schuh9d823002019-04-14 12:53:17 -0700395 if (!_outBuf.empty() && !_registeredForWriteEvents) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700396 if (!_server.subscribeToWriteEvents(this)) {
397 return false;
398 }
399 _registeredForWriteEvents = true;
400 } else if (_outBuf.empty() && _registeredForWriteEvents) {
401 if (!_server.unsubscribeFromWriteEvents(this)) {
402 return false;
403 }
404 _registeredForWriteEvents = false;
405 }
406 if (_outBuf.empty() && !closed() && _closeOnEmpty) {
407 LS_DEBUG(_logger, "Ready for close, now empty");
408 closeInternal();
409 }
410 return true;
411}
412
413bool Connection::closed() const {
414 return _fd == -1 || _shutdown;
415}
416
417void Connection::handleNewData() {
418 switch (_state) {
Austin Schuh9d823002019-04-14 12:53:17 -0700419 case State::READING_HEADERS:
420 handleHeaders();
421 break;
422 case State::READING_WEBSOCKET_KEY3:
423 handleWebSocketKey3();
424 break;
425 case State::HANDLING_HIXIE_WEBSOCKET:
426 handleHixieWebSocket();
427 break;
428 case State::HANDLING_HYBI_WEBSOCKET:
429 handleHybiWebSocket();
430 break;
431 case State::BUFFERING_POST_DATA:
432 handleBufferingPostData();
433 break;
434 case State::AWAITING_RESPONSE_BEGIN:
435 case State::SENDING_RESPONSE_BODY:
436 case State::SENDING_RESPONSE_HEADERS:
437 break;
438 default:
439 assert(false);
440 break;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700441 }
442}
443
444void Connection::handleHeaders() {
445 if (_inBuf.size() < 4) {
446 return;
447 }
448 for (size_t i = 0; i <= _inBuf.size() - 4; ++i) {
449 if (_inBuf[i] == '\r' &&
Austin Schuh9d823002019-04-14 12:53:17 -0700450 _inBuf[i + 1] == '\n' &&
451 _inBuf[i + 2] == '\r' &&
452 _inBuf[i + 3] == '\n') {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700453 if (!processHeaders(&_inBuf[0], &_inBuf[i + 2])) {
454 closeInternal();
455 return;
456 }
457 _inBuf.erase(_inBuf.begin(), _inBuf.begin() + i + 4);
458 handleNewData();
459 return;
460 }
461 }
462 if (_inBuf.size() > MaxHeadersSize) {
463 sendUnsupportedError("Headers too big");
464 }
465}
466
467void Connection::handleWebSocketKey3() {
468 constexpr auto WebSocketKeyLen = 8u;
469 if (_inBuf.size() < WebSocketKeyLen) {
470 return;
471 }
472
473 struct {
474 uint32_t key1;
475 uint32_t key2;
476 char key3[WebSocketKeyLen];
477 } md5Source;
478
479 auto key1 = parseWebSocketKey(_request->getHeader("Sec-WebSocket-Key1"));
480 auto key2 = parseWebSocketKey(_request->getHeader("Sec-WebSocket-Key2"));
481
482 LS_DEBUG(_logger, "Got a hixie websocket with key1=0x" << std::hex << key1 << ", key2=0x" << key2);
483
484 md5Source.key1 = htonl(key1);
485 md5Source.key2 = htonl(key2);
486 memcpy(&md5Source.key3, &_inBuf[0], WebSocketKeyLen);
487
488 uint8_t digest[16];
489 md5_state_t md5state;
490 md5_init(&md5state);
491 md5_append(&md5state, reinterpret_cast<const uint8_t*>(&md5Source), sizeof(md5Source));
492 md5_finish(&md5state, digest);
493
494 LS_DEBUG(_logger, "Attempting websocket upgrade");
495
496 bufferResponseAndCommonHeaders(ResponseCode::WebSocketProtocolHandshake);
497 bufferLine("Upgrade: websocket");
498 bufferLine("Connection: Upgrade");
499 bool allowCrossOrigin = _server.isCrossOriginAllowed(_request->getRequestUri());
500 if (_request->hasHeader("Origin") && allowCrossOrigin) {
Austin Schuh9d823002019-04-14 12:53:17 -0700501 bufferLine("Sec-WebSocket-Origin: " + _request->getHeader("Origin"));
Austin Schuh24adb6b2015-09-06 17:37:40 -0700502 }
503 if (_request->hasHeader("Host")) {
504 auto host = _request->getHeader("Host");
505 if (!allowCrossOrigin) {
506 bufferLine("Sec-WebSocket-Origin: http://" + host);
507 }
508 bufferLine("Sec-WebSocket-Location: ws://" + host + _request->getRequestUri());
509 }
Austin Schuh9d823002019-04-14 12:53:17 -0700510 pickProtocol();
Austin Schuh24adb6b2015-09-06 17:37:40 -0700511 bufferLine("");
512
513 write(&digest, 16, true);
514
Austin Schuh9d823002019-04-14 12:53:17 -0700515 _state = State::HANDLING_HIXIE_WEBSOCKET;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700516 _inBuf.erase(_inBuf.begin(), _inBuf.begin() + 8);
517 if (_webSocketHandler) {
518 _webSocketHandler->onConnect(this);
519 }
520}
521
Austin Schuh9d823002019-04-14 12:53:17 -0700522void Connection::pickProtocol() {
523 static std::string protocolHeader = "Sec-WebSocket-Protocol";
524 if (!_request->hasHeader(protocolHeader) || !_webSocketHandler)
525 return;
526 // Ideally we need o support this header being set multiple times...but the headers don't support that.
527 auto protocols = split(_request->getHeader(protocolHeader), ',');
528 LS_DEBUG(_logger, "Requested protocols:");
529 std::transform(protocols.begin(), protocols.end(), protocols.begin(), trimWhitespace);
530 for (auto&& p : protocols) {
531 LS_DEBUG(_logger, " " + p);
532 }
533 auto choice = _webSocketHandler->chooseProtocol(protocols);
534 if (choice >= 0 && choice < static_cast<ssize_t>(protocols.size())) {
535 LS_DEBUG(_logger, "Chose protocol " + protocols[choice]);
536 bufferLine(protocolHeader + ": " + protocols[choice]);
537 }
538}
539
Austin Schuh24adb6b2015-09-06 17:37:40 -0700540void Connection::handleBufferingPostData() {
541 if (_request->consumeContent(_inBuf)) {
Austin Schuh9d823002019-04-14 12:53:17 -0700542 _state = State::READING_HEADERS;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700543 if (!handlePageRequest()) {
544 closeInternal();
545 }
546 }
547}
548
549void Connection::send(const char* webSocketResponse) {
550 _server.checkThread();
551 if (_shutdown) {
552 if (_shutdownByUser) {
553 LS_ERROR(_logger, "Server wrote to connection after closing it");
554 }
555 return;
556 }
557 auto messageLength = strlen(webSocketResponse);
Austin Schuh9d823002019-04-14 12:53:17 -0700558 if (_state == State::HANDLING_HIXIE_WEBSOCKET) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700559 uint8_t zero = 0;
Austin Schuh9d823002019-04-14 12:53:17 -0700560 if (!write(&zero, 1, false))
561 return;
562 if (!write(webSocketResponse, messageLength, false))
563 return;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700564 uint8_t effeff = 0xff;
565 write(&effeff, 1, true);
566 return;
567 }
Austin Schuh9d823002019-04-14 12:53:17 -0700568 sendHybi(static_cast<uint8_t>(HybiPacketDecoder::Opcode::Text),
569 reinterpret_cast<const uint8_t*>(webSocketResponse), messageLength);
Austin Schuh24adb6b2015-09-06 17:37:40 -0700570}
571
Austin Schuh9d823002019-04-14 12:53:17 -0700572void Connection::send(const uint8_t* webSocketResponse, size_t length) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700573 _server.checkThread();
574 if (_shutdown) {
575 if (_shutdownByUser) {
576 LS_ERROR(_logger, "Client wrote to connection after closing it");
577 }
578 return;
579 }
Austin Schuh9d823002019-04-14 12:53:17 -0700580 if (_state == State::HANDLING_HIXIE_WEBSOCKET) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700581 LS_ERROR(_logger, "Hixie does not support binary");
582 return;
583 }
Austin Schuh9d823002019-04-14 12:53:17 -0700584 sendHybi(static_cast<uint8_t>(HybiPacketDecoder::Opcode::Binary), webSocketResponse, length);
Austin Schuh24adb6b2015-09-06 17:37:40 -0700585}
586
Austin Schuh9d823002019-04-14 12:53:17 -0700587void Connection::sendHybi(uint8_t opcode, const uint8_t* webSocketResponse, size_t messageLength) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700588 uint8_t firstByte = 0x80 | opcode;
Austin Schuh9d823002019-04-14 12:53:17 -0700589 if (_perMessageDeflate)
590 firstByte |= 0x40;
591 if (!write(&firstByte, 1, false))
592 return;
593
594 if (_perMessageDeflate) {
595 std::vector<uint8_t> compressed;
596
597 zlibContext.deflate(webSocketResponse, messageLength, compressed);
598
599 LS_DEBUG(_logger, "Compression result: " << messageLength << " bytes -> " << compressed.size() << " bytes");
600 sendHybiData(compressed.data(), compressed.size());
601 } else {
602 sendHybiData(webSocketResponse, messageLength);
603 }
604}
605
606void Connection::sendHybiData(const uint8_t* webSocketResponse, size_t messageLength) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700607 if (messageLength < 126) {
608 uint8_t nextByte = messageLength; // No MASK bit set.
Austin Schuh9d823002019-04-14 12:53:17 -0700609 if (!write(&nextByte, 1, false))
610 return;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700611 } else if (messageLength < 65536) {
612 uint8_t nextByte = 126; // No MASK bit set.
Austin Schuh9d823002019-04-14 12:53:17 -0700613 if (!write(&nextByte, 1, false))
614 return;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700615 auto lengthBytes = htons(messageLength);
Austin Schuh9d823002019-04-14 12:53:17 -0700616 if (!write(&lengthBytes, 2, false))
617 return;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700618 } else {
619 uint8_t nextByte = 127; // No MASK bit set.
Austin Schuh9d823002019-04-14 12:53:17 -0700620 if (!write(&nextByte, 1, false))
621 return;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700622 uint64_t lengthBytes = __bswap_64(messageLength);
Austin Schuh9d823002019-04-14 12:53:17 -0700623 if (!write(&lengthBytes, 8, false))
624 return;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700625 }
626 write(webSocketResponse, messageLength, true);
627}
628
629std::shared_ptr<Credentials> Connection::credentials() const {
630 _server.checkThread();
631 return _request ? _request->credentials() : std::shared_ptr<Credentials>();
632}
633
634void Connection::handleHixieWebSocket() {
635 if (_inBuf.empty()) {
636 return;
637 }
638 size_t messageStart = 0;
639 while (messageStart < _inBuf.size()) {
640 if (_inBuf[messageStart] != 0) {
Austin Schuh9d823002019-04-14 12:53:17 -0700641 LS_WARNING(_logger, "Error in WebSocket input stream (got " << (int) _inBuf[messageStart] << ")");
Austin Schuh24adb6b2015-09-06 17:37:40 -0700642 closeInternal();
643 return;
644 }
645 // TODO: UTF-8
646 size_t endOfMessage = 0;
647 for (size_t i = messageStart + 1; i < _inBuf.size(); ++i) {
648 if (_inBuf[i] == 0xff) {
649 endOfMessage = i;
650 break;
651 }
652 }
653 if (endOfMessage != 0) {
654 _inBuf[endOfMessage] = 0;
655 handleWebSocketTextMessage(reinterpret_cast<const char*>(&_inBuf[messageStart + 1]));
656 messageStart = endOfMessage + 1;
657 } else {
658 break;
659 }
660 }
661 if (messageStart != 0) {
662 _inBuf.erase(_inBuf.begin(), _inBuf.begin() + messageStart);
663 }
664 if (_inBuf.size() > MaxWebsocketMessageSize) {
665 LS_WARNING(_logger, "WebSocket message too long");
666 closeInternal();
667 }
668}
669
670void Connection::handleHybiWebSocket() {
671 if (_inBuf.empty()) {
672 return;
673 }
674 HybiPacketDecoder decoder(*_logger, _inBuf);
675 bool done = false;
676 while (!done) {
677 std::vector<uint8_t> decodedMessage;
Austin Schuh9d823002019-04-14 12:53:17 -0700678 bool deflateNeeded = false;
679
680 auto messageState = decoder.decodeNextMessage(decodedMessage, deflateNeeded);
681
682 if (deflateNeeded) {
683 if (!_perMessageDeflate) {
684 LS_WARNING(_logger, "Received deflated hybi frame but deflate wasn't negotiated");
685 closeInternal();
686 return;
687 }
688
689 size_t compressed_size = decodedMessage.size();
690
691 std::vector<uint8_t> decompressed;
692 int zlibError;
693
694 // Note: inflate() alters decodedMessage
695 bool success = zlibContext.inflate(decodedMessage, decompressed, zlibError);
696
697 if (!success) {
698 LS_WARNING(_logger, "Decompression error from zlib: " << zlibError);
699 closeInternal();
700 return;
701 }
702
703 LS_DEBUG(_logger, "Decompression result: " << compressed_size << " bytes -> " << decodedMessage.size() << " bytes");
704
705 decodedMessage.swap(decompressed);
706 }
707
708
709 switch (messageState) {
710 default:
711 closeInternal();
712 LS_WARNING(_logger, "Unknown HybiPacketDecoder state");
713 return;
714 case HybiPacketDecoder::MessageState::Error:
715 closeInternal();
716 return;
717 case HybiPacketDecoder::MessageState::TextMessage:
718 decodedMessage.push_back(0); // avoids a copy
719 handleWebSocketTextMessage(reinterpret_cast<const char*>(&decodedMessage[0]));
720 break;
721 case HybiPacketDecoder::MessageState::BinaryMessage:
722 handleWebSocketBinaryMessage(decodedMessage);
723 break;
724 case HybiPacketDecoder::MessageState::Ping:
725 sendHybi(static_cast<uint8_t>(HybiPacketDecoder::Opcode::Pong),
726 &decodedMessage[0], decodedMessage.size());
727 break;
728 case HybiPacketDecoder::MessageState::Pong:
729 // Pongs can be sent unsolicited (MSIE and Edge do this)
730 // The spec says to ignore them.
731 break;
732 case HybiPacketDecoder::MessageState::NoMessage:
733 done = true;
734 break;
735 case HybiPacketDecoder::MessageState::Close:
736 LS_DEBUG(_logger, "Received WebSocket close");
737 closeInternal();
738 return;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700739 }
740 }
741 if (decoder.numBytesDecoded() != 0) {
742 _inBuf.erase(_inBuf.begin(), _inBuf.begin() + decoder.numBytesDecoded());
743 }
744 if (_inBuf.size() > MaxWebsocketMessageSize) {
745 LS_WARNING(_logger, "WebSocket message too long");
746 closeInternal();
747 }
748}
749
750void Connection::handleWebSocketTextMessage(const char* message) {
751 LS_DEBUG(_logger, "Got text web socket message: '" << message << "'");
752 if (_webSocketHandler) {
753 _webSocketHandler->onData(this, message);
754 }
755}
756
757void Connection::handleWebSocketBinaryMessage(const std::vector<uint8_t>& message) {
758 LS_DEBUG(_logger, "Got binary web socket message (size: " << message.size() << ")");
759 if (_webSocketHandler) {
760 _webSocketHandler->onData(this, &message[0], message.size());
761 }
762}
763
764bool Connection::sendError(ResponseCode errorCode, const std::string& body) {
Austin Schuh9d823002019-04-14 12:53:17 -0700765 assert(_state != State::HANDLING_HIXIE_WEBSOCKET);
Austin Schuh24adb6b2015-09-06 17:37:40 -0700766 auto errorNumber = static_cast<int>(errorCode);
767 auto message = ::name(errorCode);
768 bufferResponseAndCommonHeaders(errorCode);
769 auto errorContent = findEmbeddedContent("/_error.html");
770 std::string document;
771 if (errorContent) {
772 document.assign(errorContent->data, errorContent->data + errorContent->length);
773 replace(document, "%%ERRORCODE%%", toString(errorNumber));
774 replace(document, "%%MESSAGE%%", message);
775 replace(document, "%%BODY%%", body);
776 } else {
777 std::stringstream documentStr;
778 documentStr << "<html><head><title>" << errorNumber << " - " << message << "</title></head>"
Austin Schuh9d823002019-04-14 12:53:17 -0700779 << "<body><h1>" << errorNumber << " - " << message << "</h1>"
780 << "<div>" << body << "</div><hr/><div><i>Powered by "
781 "<a href=\"https://github.com/mattgodbolt/seasocks\">Seasocks</a></i></div></body></html>";
Austin Schuh24adb6b2015-09-06 17:37:40 -0700782 document = documentStr.str();
783 }
784 bufferLine("Content-Length: " + toString(document.length()));
785 bufferLine("Connection: close");
786 bufferLine("");
787 bufferLine(document);
788 if (!flush()) {
789 return false;
790 }
791 closeWhenEmpty();
792 return true;
793}
794
795bool Connection::sendUnsupportedError(const std::string& reason) {
796 return sendError(ResponseCode::NotImplemented, reason);
797}
798
799bool Connection::send404() {
800 auto path = getRequestUri();
801 auto embedded = findEmbeddedContent(path);
802 if (embedded) {
803 return sendData(getContentType(path), embedded->data, embedded->length);
804 } else if (strcmp(path.c_str(), "/_livestats.js") == 0) {
805 auto stats = _server.getStatsDocument();
806 return sendData("text/javascript", stats.c_str(), stats.length());
807 } else {
808 return sendError(ResponseCode::NotFound, "Unable to find resource for: " + path);
809 }
810}
811
812bool Connection::sendBadRequest(const std::string& reason) {
813 return sendError(ResponseCode::BadRequest, reason);
814}
815
816bool Connection::sendISE(const std::string& error) {
817 return sendError(ResponseCode::InternalServerError, error);
818}
819
820bool Connection::processHeaders(uint8_t* first, uint8_t* last) {
821 // Ideally we'd copy off [first, last] now into a header structure here.
822 // Be careful about lifetimes though and multiple requests coming in, should
823 // we ever support HTTP pipelining and/or long-lived requests.
824 char* requestLine = extractLine(first, last);
Austin Schuh9d823002019-04-14 12:53:17 -0700825 assert(requestLine != nullptr);
Austin Schuh24adb6b2015-09-06 17:37:40 -0700826
827 LS_ACCESS(_logger, "Request: " << requestLine);
828
829 const char* verbText = shift(requestLine);
830 if (!verbText) {
831 return sendBadRequest("Malformed request line");
832 }
833 auto verb = Request::verb(verbText);
834 if (verb == Request::Verb::Invalid) {
835 return sendBadRequest("Malformed request line");
836 }
837 const char* requestUri = shift(requestLine);
Austin Schuh9d823002019-04-14 12:53:17 -0700838 if (requestUri == nullptr) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700839 return sendBadRequest("Malformed request line");
840 }
841
842 const char* httpVersion = shift(requestLine);
Austin Schuh9d823002019-04-14 12:53:17 -0700843 if (httpVersion == nullptr) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700844 return sendBadRequest("Malformed request line");
845 }
846 if (strcmp(httpVersion, "HTTP/1.1") != 0) {
847 return sendUnsupportedError("Unsupported HTTP version");
848 }
849 if (*requestLine != 0) {
850 return sendBadRequest("Trailing crap after http version");
851 }
852
853 HeaderMap headers(31);
854 while (first < last) {
Austin Schuh9d823002019-04-14 12:53:17 -0700855 char* colonPos = nullptr;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700856 char* headerLine = extractLine(first, last, &colonPos);
Austin Schuh9d823002019-04-14 12:53:17 -0700857 assert(headerLine != nullptr);
858 if (colonPos == nullptr) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700859 return sendBadRequest("Malformed header");
860 }
861 *colonPos = 0;
862 const char* key = headerLine;
863 const char* value = skipWhitespace(colonPos + 1);
864 LS_DEBUG(_logger, "Key: " << key << " || " << value);
Austin Schuh24adb6b2015-09-06 17:37:40 -0700865 headers.emplace(key, value);
Austin Schuh24adb6b2015-09-06 17:37:40 -0700866 }
867
Austin Schuh9d823002019-04-14 12:53:17 -0700868 if (headers.count("Connection") && headers.count("Upgrade") && hasConnectionType(headers["Connection"], "Upgrade") && caseInsensitiveSame(headers["Upgrade"], "websocket")) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700869 LS_INFO(_logger, "Websocket request for " << requestUri << "'");
870 if (verb != Request::Verb::Get) {
871 return sendBadRequest("Non-GET WebSocket request");
872 }
873 _webSocketHandler = _server.getWebSocketHandler(requestUri);
874 if (!_webSocketHandler) {
875 LS_WARNING(_logger, "Couldn't find WebSocket end point for '" << requestUri << "'");
876 return send404();
877 }
878 verb = Request::Verb::WebSocket;
Austin Schuh9d823002019-04-14 12:53:17 -0700879
880 if (_server.server().getPerMessageDeflateEnabled() && headers.count("Sec-WebSocket-Extensions")) {
881 parsePerMessageDeflateHeader(headers["Sec-WebSocket-Extensions"]);
882 }
Austin Schuh24adb6b2015-09-06 17:37:40 -0700883 }
884
Austin Schuh9d823002019-04-14 12:53:17 -0700885 _request = std::make_unique<PageRequest>(_address, requestUri, _server.server(),
886 verb, std::move(headers));
Austin Schuh24adb6b2015-09-06 17:37:40 -0700887
Austin Schuh9d823002019-04-14 12:53:17 -0700888 const EmbeddedContent* embedded = findEmbeddedContent(requestUri);
Austin Schuh24adb6b2015-09-06 17:37:40 -0700889 if (verb == Request::Verb::Get && embedded) {
890 // MRG: one day, this could be a request handler.
891 return sendData(getContentType(requestUri), embedded->data, embedded->length);
Austin Schuh9d823002019-04-14 12:53:17 -0700892 } else if (verb == Request::Verb::Head && embedded) {
893 return sendHeader(getContentType(requestUri), embedded->length);
Austin Schuh24adb6b2015-09-06 17:37:40 -0700894 }
895
Austin Schuh9d823002019-04-14 12:53:17 -0700896 if (_request->contentLength() > _server.clientBufferSize()) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700897 return sendBadRequest("Content length too long");
898 }
899 if (_request->contentLength() == 0) {
900 return handlePageRequest();
901 }
Austin Schuh9d823002019-04-14 12:53:17 -0700902 _state = State::BUFFERING_POST_DATA;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700903 return true;
904}
905
906bool Connection::handlePageRequest() {
907 std::shared_ptr<Response> response;
908 try {
909 response = _server.handle(*_request);
910 } catch (const std::exception& e) {
911 LS_ERROR(_logger, "page error: " << e.what());
912 return sendISE(e.what());
913 } catch (...) {
914 LS_ERROR(_logger, "page error: (unknown)");
915 return sendISE("(unknown)");
916 }
917 auto uri = _request->getRequestUri();
918 if (!response && _request->verb() == Request::Verb::WebSocket) {
919 _webSocketHandler = _server.getWebSocketHandler(uri.c_str());
Austin Schuh9d823002019-04-14 12:53:17 -0700920 const auto webSocketVersion = std::stoi(_request->getHeader("Sec-WebSocket-Version"));
Austin Schuh24adb6b2015-09-06 17:37:40 -0700921 if (!_webSocketHandler) {
922 LS_WARNING(_logger, "Couldn't find WebSocket end point for '" << uri << "'");
923 return send404();
924 }
925 if (webSocketVersion == 0) {
926 // Hixie
Austin Schuh9d823002019-04-14 12:53:17 -0700927 _state = State::READING_WEBSOCKET_KEY3;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700928 return true;
929 }
930 auto hybiKey = _request->getHeader("Sec-WebSocket-Key");
931 return handleHybiHandshake(webSocketVersion, hybiKey);
932 }
933 return sendResponse(response);
934}
935
936bool Connection::sendResponse(std::shared_ptr<Response> response) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700937 if (response == Response::unhandled()) {
938 return sendStaticData();
939 }
Austin Schuh9d823002019-04-14 12:53:17 -0700940 assert(_response.get() == nullptr);
941 _state = State::AWAITING_RESPONSE_BEGIN;
942 _transferEncoding = TransferEncoding::Raw;
943 _chunk = 0;
944 _response = response;
945 _response->handle(_writer);
Austin Schuh24adb6b2015-09-06 17:37:40 -0700946 return true;
947}
948
Austin Schuh9d823002019-04-14 12:53:17 -0700949void Connection::error(ResponseCode responseCode, const std::string& payload) {
950 _server.checkThread();
951 if (_state != State::AWAITING_RESPONSE_BEGIN) {
952 LS_ERROR(_logger, "error() called when in wrong state");
953 return;
954 }
955 if (isOk(responseCode)) {
956 LS_ERROR(_logger, "error() called with a non-error code");
957 }
958 if (responseCode == ResponseCode::NotFound) {
959 // TODO: better here; we use this purely to serve our own embedded content.
960 send404();
961 } else {
962 sendError(responseCode, payload);
963 }
964}
965
966void Connection::begin(ResponseCode responseCode, TransferEncoding encoding) {
967 _server.checkThread();
968 if (_state != State::AWAITING_RESPONSE_BEGIN) {
969 LS_ERROR(_logger, "begin() called when in wrong state");
970 return;
971 }
972 _state = State::SENDING_RESPONSE_HEADERS;
973 bufferResponseAndCommonHeaders(responseCode);
974 _transferEncoding = encoding;
975 if (_transferEncoding == TransferEncoding::Chunked) {
976 bufferLine("Transfer-encoding: chunked");
977 }
978}
979
980void Connection::header(const std::string& header, const std::string& value) {
981 _server.checkThread();
982 if (_state != State::SENDING_RESPONSE_HEADERS) {
983 LS_ERROR(_logger, "header() called when in wrong state");
984 return;
985 }
986 bufferLine(header + ": " + value);
987}
988void Connection::payload(const void* data, size_t size, bool flush) {
989 _server.checkThread();
990 if (_state == State::SENDING_RESPONSE_HEADERS) {
991 bufferLine("");
992 _state = State::SENDING_RESPONSE_BODY;
993 } else if (_state != State::SENDING_RESPONSE_BODY) {
994 LS_ERROR(_logger, "payload() called when in wrong state");
995 return;
996 }
997 if (size && _transferEncoding == TransferEncoding::Chunked) {
998 writeChunkHeader(size);
999 }
1000 write(data, size, flush);
1001}
1002
1003void Connection::writeChunkHeader(size_t size) {
1004 std::ostringstream lengthStr;
1005 if (_chunk)
1006 lengthStr << "\r\n";
1007 lengthStr << std::hex << size << "\r\n";
1008 auto length = lengthStr.str();
1009 _chunk++;
1010 write(length.c_str(), length.size(), false);
1011}
1012
1013void Connection::finish(bool keepConnectionOpen) {
1014 _server.checkThread();
1015 if (_state == State::SENDING_RESPONSE_HEADERS) {
1016 bufferLine("");
1017 } else if (_state != State::SENDING_RESPONSE_BODY) {
1018 LS_ERROR(_logger, "finish() called when in wrong state");
1019 return;
1020 }
1021 if (_transferEncoding == TransferEncoding::Chunked) {
1022 writeChunkHeader(0);
1023 write("\r\n", 2, false);
1024 }
1025
1026 flush();
1027
1028 if (!keepConnectionOpen) {
1029 closeWhenEmpty();
1030 }
1031
1032 _state = State::READING_HEADERS;
1033 _response.reset();
1034}
1035
Austin Schuh24adb6b2015-09-06 17:37:40 -07001036bool Connection::handleHybiHandshake(
Austin Schuh9d823002019-04-14 12:53:17 -07001037 int webSocketVersion,
1038 const std::string& webSocketKey) {
Austin Schuh24adb6b2015-09-06 17:37:40 -07001039 if (webSocketVersion != 8 && webSocketVersion != 13) {
1040 return sendBadRequest("Invalid websocket version");
1041 }
1042 LS_DEBUG(_logger, "Got a hybi-8 websocket with key=" << webSocketKey);
1043
1044 LS_DEBUG(_logger, "Attempting websocket upgrade");
1045
1046 bufferResponseAndCommonHeaders(ResponseCode::WebSocketProtocolHandshake);
1047 bufferLine("Upgrade: websocket");
1048 bufferLine("Connection: Upgrade");
1049 bufferLine("Sec-WebSocket-Accept: " + getAcceptKey(webSocketKey));
Austin Schuh9d823002019-04-14 12:53:17 -07001050 if (_perMessageDeflate)
1051 bufferLine("Sec-WebSocket-Extensions: permessage-deflate");
1052 pickProtocol();
Austin Schuh24adb6b2015-09-06 17:37:40 -07001053 bufferLine("");
1054 flush();
1055
1056 if (_webSocketHandler) {
1057 _webSocketHandler->onConnect(this);
1058 }
Austin Schuh9d823002019-04-14 12:53:17 -07001059 _state = State::HANDLING_HYBI_WEBSOCKET;
Austin Schuh24adb6b2015-09-06 17:37:40 -07001060 return true;
1061}
1062
Austin Schuh9d823002019-04-14 12:53:17 -07001063void Connection::parsePerMessageDeflateHeader(const std::string& header) {
1064 for (auto& extField : seasocks::split(header, ';')) {
1065 while (!extField.empty() && isspace(extField[0])) {
1066 extField = extField.substr(1);
1067 }
1068
1069 if (seasocks::caseInsensitiveSame(extField, "permessage-deflate")) {
1070 LS_INFO(_logger, "Enabling per-message deflate");
1071 _perMessageDeflate = true;
1072 zlibContext.initialise();
1073 }
1074 }
1075}
1076
Austin Schuh24adb6b2015-09-06 17:37:40 -07001077bool Connection::parseRange(const std::string& rangeStr, Range& range) const {
1078 size_t minusPos = rangeStr.find('-');
1079 if (minusPos == std::string::npos) {
1080 LS_WARNING(_logger, "Bad range: '" << rangeStr << "'");
1081 return false;
1082 }
1083 if (minusPos == 0) {
1084 // A range like "-500" means 500 bytes from end of file to end.
Austin Schuh9d823002019-04-14 12:53:17 -07001085 range.start = std::stoi(rangeStr);
Austin Schuh24adb6b2015-09-06 17:37:40 -07001086 range.end = std::numeric_limits<long>::max();
1087 return true;
1088 } else {
Austin Schuh9d823002019-04-14 12:53:17 -07001089 range.start = std::stoi(rangeStr.substr(0, minusPos));
1090 if (minusPos == rangeStr.size() - 1) {
Austin Schuh24adb6b2015-09-06 17:37:40 -07001091 range.end = std::numeric_limits<long>::max();
1092 } else {
Austin Schuh9d823002019-04-14 12:53:17 -07001093 range.end = std::stoi(rangeStr.substr(minusPos + 1));
Austin Schuh24adb6b2015-09-06 17:37:40 -07001094 }
1095 return true;
1096 }
1097 return false;
1098}
1099
1100bool Connection::parseRanges(const std::string& range, std::list<Range>& ranges) const {
1101 static const std::string expectedPrefix = "bytes=";
1102 if (range.length() < expectedPrefix.length() || range.substr(0, expectedPrefix.length()) != expectedPrefix) {
1103 LS_WARNING(_logger, "Bad range request prefix: '" << range << "'");
1104 return false;
1105 }
1106 auto rangesText = split(range.substr(expectedPrefix.length()), ',');
Austin Schuh9d823002019-04-14 12:53:17 -07001107 for (auto& it : rangesText) {
Austin Schuh24adb6b2015-09-06 17:37:40 -07001108 Range r;
Austin Schuh9d823002019-04-14 12:53:17 -07001109 if (!parseRange(it, r)) {
Austin Schuh24adb6b2015-09-06 17:37:40 -07001110 return false;
1111 }
1112 ranges.push_back(r);
1113 }
1114 return !ranges.empty();
1115}
1116
1117// Sends HTTP 200 or 206, content-length, and range info as needed. Returns the actual file ranges
1118// needing sending.
1119std::list<Connection::Range> Connection::processRangesForStaticData(const std::list<Range>& origRanges, long fileSize) {
1120 if (origRanges.empty()) {
1121 // Easy case: a non-range request.
1122 bufferResponseAndCommonHeaders(ResponseCode::Ok);
1123 bufferLine("Content-Length: " + toString(fileSize));
Austin Schuh9d823002019-04-14 12:53:17 -07001124 return {Range{0, fileSize - 1}};
Austin Schuh24adb6b2015-09-06 17:37:40 -07001125 }
1126
1127 // Partial content request.
1128 bufferResponseAndCommonHeaders(ResponseCode::PartialContent);
1129 int contentLength = 0;
1130 std::ostringstream rangeLine;
1131 rangeLine << "Content-Range: bytes ";
1132 std::list<Range> sendRanges;
Austin Schuh9d823002019-04-14 12:53:17 -07001133 for (auto actualRange : origRanges) {
Austin Schuh24adb6b2015-09-06 17:37:40 -07001134 if (actualRange.start < 0) {
1135 actualRange.start += fileSize;
1136 }
1137 if (actualRange.start >= fileSize) {
1138 actualRange.start = fileSize - 1;
1139 }
1140 if (actualRange.end >= fileSize) {
1141 actualRange.end = fileSize - 1;
1142 }
1143 contentLength += actualRange.length();
1144 sendRanges.push_back(actualRange);
1145 rangeLine << actualRange.start << "-" << actualRange.end;
1146 }
1147 rangeLine << "/" << fileSize;
1148 bufferLine(rangeLine.str());
1149 bufferLine("Content-Length: " + toString(contentLength));
1150 return sendRanges;
1151}
1152
1153bool Connection::sendStaticData() {
1154 // TODO: fold this into the handler way of doing things.
1155 std::string path = _server.getStaticPath() + getRequestUri();
1156 auto rangeHeader = getHeader("Range");
1157 // Trim any trailing queries.
1158 size_t queryPos = path.find('?');
Austin Schuh9d823002019-04-14 12:53:17 -07001159 if (queryPos != std::string::npos) {
Austin Schuh24adb6b2015-09-06 17:37:40 -07001160 path.resize(queryPos);
1161 }
1162 if (*path.rbegin() == '/') {
1163 path += "index.html";
1164 }
Austin Schuh9d823002019-04-14 12:53:17 -07001165
1166 RaiiFd input{::open(path.c_str(), O_RDONLY)};
1167 struct stat fileStat;
1168 if (!input.ok() || ::fstat(input, &fileStat) == -1) {
Austin Schuh24adb6b2015-09-06 17:37:40 -07001169 return send404();
1170 }
1171 std::list<Range> ranges;
1172 if (!rangeHeader.empty() && !parseRanges(rangeHeader, ranges)) {
1173 return sendBadRequest("Bad range header");
1174 }
Austin Schuh9d823002019-04-14 12:53:17 -07001175 ranges = processRangesForStaticData(ranges, fileStat.st_size);
Austin Schuh24adb6b2015-09-06 17:37:40 -07001176 bufferLine("Content-Type: " + getContentType(path));
1177 bufferLine("Connection: keep-alive");
1178 bufferLine("Accept-Ranges: bytes");
Austin Schuh9d823002019-04-14 12:53:17 -07001179 bufferLine("Last-Modified: " + webtime(fileStat.st_mtime));
Austin Schuh24adb6b2015-09-06 17:37:40 -07001180 if (!isCacheable(path)) {
1181 bufferLine("Cache-Control: no-store");
1182 bufferLine("Pragma: no-cache");
1183 bufferLine("Expires: " + now());
1184 }
1185 bufferLine("");
1186 if (!flush()) {
1187 return false;
1188 }
1189
Austin Schuh9d823002019-04-14 12:53:17 -07001190 for (auto range : ranges) {
1191 if (::lseek(input, range.start, SEEK_SET) == -1) {
Austin Schuh24adb6b2015-09-06 17:37:40 -07001192 // We've (probably) already sent data.
1193 return false;
1194 }
Austin Schuh9d823002019-04-14 12:53:17 -07001195 auto bytesLeft = range.length();
Austin Schuh24adb6b2015-09-06 17:37:40 -07001196 while (bytesLeft) {
1197 char buf[ReadWriteBufferSize];
1198 auto bytesRead = ::read(input, buf, std::min(sizeof(buf), bytesLeft));
1199 if (bytesRead <= 0) {
1200 const static std::string unexpectedEof("Unexpected EOF");
1201 LS_ERROR(_logger, "Error reading file: " << (bytesRead == 0 ? unexpectedEof : getLastError()));
1202 // We can't send an error document as we've sent the header.
1203 return false;
1204 }
1205 bytesLeft -= bytesRead;
1206 if (!write(buf, bytesRead, true)) {
1207 return false;
1208 }
1209 }
1210 }
1211 return true;
1212}
1213
Austin Schuh9d823002019-04-14 12:53:17 -07001214bool Connection::sendHeader(const std::string& type, size_t size) {
1215 bufferResponseAndCommonHeaders(ResponseCode::Ok);
1216 bufferLine("Content-Type: " + type);
1217 bufferLine("Content-Length: " + toString(size));
1218 bufferLine("Connection: keep-alive");
1219 return bufferLine("");
1220}
1221
Austin Schuh24adb6b2015-09-06 17:37:40 -07001222bool Connection::sendData(const std::string& type, const char* start, size_t size) {
1223 bufferResponseAndCommonHeaders(ResponseCode::Ok);
1224 bufferLine("Content-Type: " + type);
1225 bufferLine("Content-Length: " + toString(size));
1226 bufferLine("Connection: keep-alive");
1227 bufferLine("");
1228 bool result = write(start, size, true);
1229 return result;
1230}
1231
1232void Connection::bufferResponseAndCommonHeaders(ResponseCode code) {
1233 auto responseCodeInt = static_cast<int>(code);
1234 auto responseCodeName = ::name(code);
1235 auto response = std::string("HTTP/1.1 " + toString(responseCodeInt) + " " + responseCodeName);
1236 LS_ACCESS(_logger, "Response: " << response);
1237 bufferLine(response);
Austin Schuh9d823002019-04-14 12:53:17 -07001238 bufferLine("Server: " + std::string(Config::version));
Austin Schuh24adb6b2015-09-06 17:37:40 -07001239 bufferLine("Date: " + now());
1240 bufferLine("Access-Control-Allow-Origin: *");
1241}
1242
1243void Connection::setLinger() {
1244 if (_fd == -1) {
1245 return;
1246 }
1247 const int secondsToLinger = 1;
Austin Schuh9d823002019-04-14 12:53:17 -07001248 struct linger linger = {true, secondsToLinger};
Austin Schuh24adb6b2015-09-06 17:37:40 -07001249 if (::setsockopt(_fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)) == -1) {
1250 LS_INFO(_logger, "Unable to set linger on socket");
1251 }
1252}
1253
1254bool Connection::hasHeader(const std::string& header) const {
1255 return _request ? _request->hasHeader(header) : false;
1256}
1257
1258std::string Connection::getHeader(const std::string& header) const {
1259 return _request ? _request->getHeader(header) : "";
1260}
1261
1262const std::string& Connection::getRequestUri() const {
1263 static const std::string empty;
1264 return _request ? _request->getRequestUri() : empty;
1265}
1266
Austin Schuh9d823002019-04-14 12:53:17 -07001267Server& Connection::server() const {
1268 return _server.server();
1269}
1270
1271} // seasocks