1
/* Copyright (C) 2008 PrimeBase Technologies GmbH, Germany
3
* PrimeBase Media Stream for MySQL
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.
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.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
* Original author: Paul McCullagh
20
* Continued development: Barry Leslie
30
#include "cslib/CSConfig.h"
35
#include "cslib/CSGlobal.h"
36
#include "cslib/CSSocket.h"
37
#include "cslib/CSStrUtil.h"
38
#include "cslib/CSHTTPStream.h"
40
#include "connection_handler_ms.h"
41
#include "network_ms.h"
42
#include "open_table_ms.h"
43
#include "engine_ms.h"
44
#include "version_ms.h"
46
//#include "mysql_ms.h"
48
u_long MSConnectionHandler::gMaxKeepAlive;
50
MSConnectionHandler::MSConnectionHandler(CSThreadList *list):
52
amWaitingToListen(false),
62
void MSConnectionHandler::close()
68
MSConnectionHandler *MSConnectionHandler::newHandler(CSThreadList *list)
70
return new MSConnectionHandler(list);
73
/* Return false if not connection was openned, and the thread must quit. */
74
bool MSConnectionHandler::openStream()
81
if (!(sock = MSNetwork::openConnection(this)))
84
in = sock->getInputStream();
85
in = CSBufferedInputStream::newStream(in);
86
iInputStream = CSHTTPInputStream::newStream(in);
88
out = sock->getOutputStream();
89
out = CSBufferedOutputStream::newStream(out);
90
iOutputStream = CSHTTPOutputStream::newStream(out);
95
int MSConnectionHandler::getHTTPStatus(int err)
100
case MS_OK: code = 200; break;
101
case MS_ERR_ENGINE: code = 500; break;
102
case MS_ERR_UNKNOWN_TABLE: code = 404; break;
103
case MS_ERR_UNKNOWN_DB: code = 404; break;
104
case MS_ERR_DATABASE_DELETED: code = 404; break;
105
case MS_ERR_NOT_FOUND: code = 404; break;
106
case MS_ERR_REMOVING_REPO: code = 404; break;
107
case MS_ERR_TABLE_LOCKED: code = 412; break; // Precondition Failed
108
case MS_ERR_INCORRECT_URL: code = 404; break;
109
case MS_ERR_AUTH_FAILED: code = 403; break; // Forbidden
110
default: code = 500; break;
115
void MSConnectionHandler::writeException(const char *qualifier)
120
iOutputStream->clearHeaders();
121
iOutputStream->clearBody();
122
code = getHTTPStatus(myException.getErrorCode());
123
iOutputStream->setStatus(code);
124
iOutputStream->appendBody("<HTML><HEAD><TITLE>HTTP Error ");
125
iOutputStream->appendBody(code);
126
iOutputStream->appendBody(": ");
127
iOutputStream->appendBody(CSHTTPOutputStream::getReasonPhrase(code));
128
iOutputStream->appendBody("</TITLE></HEAD>");
129
iOutputStream->appendBody("<BODY><H2>HTTP Error ");
130
iOutputStream->appendBody(code);
131
iOutputStream->appendBody(": ");
132
iOutputStream->appendBody(CSHTTPOutputStream::getReasonPhrase(code));
133
iOutputStream->appendBody("</H2>");
135
iOutputStream->appendBody(qualifier);
136
iOutputStream->appendBody(EXCEPTION_REPLY_MESSAGE_PREFIX_TAG);
137
iOutputStream->appendBody(myException.getMessage());
138
iOutputStream->appendBody(EXCEPTION_REPLY_MESSAGE_SUFFIX_TAG);
139
iOutputStream->appendBody(myException.getStackTrace());
140
iOutputStream->appendBody(EXCEPTION_REPLY_STACK_TRACE_SUFFIX_TAG);
141
iOutputStream->appendBody("MySQL ");
142
iOutputStream->appendBody(PBMSVersion::getCString());
143
iOutputStream->appendBody(", PBMS ");
144
iOutputStream->appendBody(PBMSVersion::getCString());
145
iOutputStream->appendBody("<br>Copyright © 2009, PrimeBase Technologies GmbH</font></P></BODY></HTML>");
147
replyPending = false;
148
iOutputStream->writeHead();
149
iOutputStream->writeBody();
150
iOutputStream->flush();
154
void MSConnectionHandler::writeException()
156
writeException(NULL);
159
void MSConnectionHandler::closeStream()
171
iOutputStream->release();
172
iOutputStream = NULL;
175
iInputStream->release();
181
void MSConnectionHandler::parseRequestURI()
183
CSString *uri = iInputStream->getRequestURI();
184
uint32_t pos = 0, end;
189
pos = uri->locate(0, "://");
190
if (pos < uri->length())
193
pos = uri->skip(0, '/');
196
end = uri->locate(pos, '/');
197
//end = uri->locate(uri->nextPos(end), '/'); I am not sure why this was done.
198
//iTableURI = uri->substr(pos, end - pos);
199
iTableURI = uri->substr(pos);
204
void MSConnectionHandler::freeRequestURI()
207
iTableURI->release();
211
void MSConnectionHandler::writeFile(CSString *file_path)
219
path = CSPath::newPath(RETAIN(file_path));
222
if (path->exists()) {
223
file = path->openFile(CSFile::READONLY);
226
iOutputStream->setContentLength((uint64_t) path->getSize());
227
replyPending = false;
228
iOutputStream->writeHead();
230
CSStream::pipe(RETAIN(iOutputStream), file->getInputStream());
235
myException.initFileError(CS_CONTEXT, path->getCString(), ENOENT);
244
* Request URI: /<blob URL>
246
* Request URI: /<database>/<blob alias>
248
void MSConnectionHandler::handleGet(bool info_only)
250
const char *bad_url_comment = "Incorrect URL: ";
252
CSString *info_request;
253
CSString *ping_request;
256
self->myException.setErrorCode(0);
258
iOutputStream->clearHeaders();
259
iOutputStream->clearBody();
260
//iOutputStream->setStatus(200); This is done in the send now.
264
ping_request = iInputStream->getHeaderValue(MS_PING_REQUEST);
268
db = MSDatabase::getDatabase(ping_request, false);
271
if (db->myBlobCloud->cl_getDefaultCloudRef()) {
272
MSCloudInfo *info = db->myBlobCloud->cl_getCloudInfo();
274
iOutputStream->addHeader(MS_CLOUD_SERVER, info->getServer());
280
iOutputStream->setStatus(200);
281
iOutputStream->writeHead();
282
iOutputStream->flush();
287
info_request = iInputStream->getHeaderValue(MS_BLOB_INFO_REQUEST);
289
info_only = (info_request->compare("yes") == 0);
290
info_request->release();
295
if (iTableURI->length() == 0)
301
if (iTableURI->equals("favicon.ico")) {
302
iOutputStream->setStatus(200);
303
writeFile(iTableURI);
304
} else if (PBMSBlobURLTools::couldBeURL(iTableURI->getCString(), &blob)) {
305
uint64_t size, offset;
307
if ((! info_only) && iInputStream->getRange(&size, &offset)) {
308
if (offset >= blob.bu_blob_size) {
309
iOutputStream->setStatus(416); // Requested range not satisfiable.
310
iOutputStream->writeHead();
311
iOutputStream->flush();
315
if (size > (blob.bu_blob_size - offset))
316
size = blob.bu_blob_size - offset;
318
iOutputStream->setRange(size, offset, blob.bu_blob_size);
320
size = blob.bu_blob_size;
324
if (blob.bu_type == MS_URL_TYPE_BLOB) {
325
otab = MSTableList::getOpenTableByID(blob.bu_db_id, blob.bu_tab_id);
327
otab->sendRepoBlob(blob.bu_blob_id, offset, size, blob.bu_auth_code, info_only, iOutputStream);
330
MSRepoFile *repo_file;
332
if (!(otab = MSTableList::getOpenTableForDB(blob.bu_db_id))) {
333
char buffer[CS_EXC_MESSAGE_SIZE];
336
snprintf(id_str, 12, "%"PRIu32"", blob.bu_db_id);
338
cs_strcpy(CS_EXC_MESSAGE_SIZE, buffer, "Unknown database ID # ");
339
cs_strcat(CS_EXC_MESSAGE_SIZE, buffer, id_str);
340
CSException::throwException(CS_CONTEXT, MS_ERR_UNKNOWN_DB, buffer);
343
repo_file = otab->getDB()->getRepoFileFromPool(blob.bu_tab_id, false);
344
frompool_(repo_file);
345
repo_file->sendBlob(otab, blob.bu_blob_id, offset, size, blob.bu_auth_code, true, info_only, iOutputStream);
346
backtopool_(repo_file);
351
#ifdef HAVE_ALIAS_SUPPORT
359
db_name = iTableURI->left("/");
361
alias = iTableURI->right("/");
364
if (db_name->length() == 0 || alias->length() == 0 || alias->length() > BLOB_ALIAS_LENGTH)
367
if (!(otab = MSTableList::getOpenTableForDB(MSDatabase::getDatabaseID(db_name->getCString(), true)))) {
368
char buffer[CS_EXC_MESSAGE_SIZE];
370
cs_strcpy(CS_EXC_MESSAGE_SIZE, buffer, "Unknown database: ");
371
cs_strcat(CS_EXC_MESSAGE_SIZE, buffer, db_name->getCString());
372
CSException::throwException(CS_CONTEXT, MS_ERR_UNKNOWN_DB, buffer);
378
// lookup the blob alias in the database.
379
if (!db->findBlobWithAlias(alias->getCString(), &repo_id, &blob_id)) {
380
char buffer[CS_EXC_MESSAGE_SIZE];
382
cs_strcpy(CS_EXC_MESSAGE_SIZE, buffer, "Unknown alias: ");
383
cs_strcat(CS_EXC_MESSAGE_SIZE, buffer, alias->getCString());
384
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, buffer);
387
MSRepoFile *repo_file = db->getRepoFileFromPool(repo_id, false);
389
frompool_(repo_file);
390
repo_file->sendBlob(otab, blob_id, 0, false, info_only, iOutputStream);
391
backtopool_(repo_file);
399
char buffer[CS_EXC_MESSAGE_SIZE];
401
cs_strcpy(CS_EXC_MESSAGE_SIZE, buffer, "Bad PBMS BLOB URL: ");
402
cs_strcat(CS_EXC_MESSAGE_SIZE, buffer, iTableURI->getCString());
403
CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, buffer);
411
char buffer[CS_EXC_MESSAGE_SIZE];
413
cs_strcpy(CS_EXC_MESSAGE_SIZE, buffer, bad_url_comment);
414
cs_strcat(CS_EXC_MESSAGE_SIZE, buffer, iInputStream->getRequestURI()->getCString());
415
CSException::throwException(CS_CONTEXT, MS_ERR_INCORRECT_URL, buffer);
419
void MSConnectionHandler::handlePut()
421
MSOpenTable *otab = NULL;
422
uint32_t db_id = 0, tab_id;
425
self->myException.setErrorCode(0);
427
iOutputStream->clearHeaders();
428
iOutputStream->clearBody();
429
iOutputStream->setStatus(200);
432
if (iTableURI->length() != 0)
433
MSDatabase::convertTablePathToIDs(iTableURI->getCString(), &db_id, &tab_id, true);
436
if ((!db_id) || !(otab = MSTableList::getOpenTableByID(db_id, tab_id))) {
437
char buffer[CS_EXC_MESSAGE_SIZE];
439
cs_strcpy(CS_EXC_MESSAGE_SIZE, buffer, "Incorrect URL: ");
440
cs_strcat(CS_EXC_MESSAGE_SIZE, buffer, iInputStream->getRequestURI()->getCString());
441
CSException::throwException(CS_CONTEXT, MS_ERR_INCORRECT_URL, buffer);
445
uint64_t blob_len, cloud_blob_len = 0;
448
uint16_t metadata_size = 0;
449
CSStringBuffer *metadata;
451
new_(metadata, CSStringBuffer(80));
454
if (! iInputStream->getContentLength(&blob_len)) {
455
CSException::throwException(CS_CONTEXT, CS_ERR_MISSING_HTTP_HEADER, "Missing content length header");
459
// Collect the meta data.
460
for (uint32_t i = 0; i < iInputStream->numHeaders(); i++) {
461
CSHeader *header = iInputStream->getHeader(i);
462
const char *name = header->getNameCString();
466
if (!strcmp(name, MS_BLOB_SIZE)) { // The actual BLOB data size if it is being stored in a cloud.
467
sscanf(header->getValueCString(), "%"PRIu64"", &cloud_blob_len);
470
if (name && otab->getDB()->isValidHeaderField(name)) {
471
uint16_t rec_size, name_size, value_size;
472
const char *value = header->getValueCString();
477
name_size = strlen(name);
478
value_size = strlen(value);
480
rec_size = name_size + value_size + 2;
481
metadata->setLength(metadata_size + rec_size);
483
buf = metadata->getBuffer(metadata_size);
484
metadata_size += rec_size;
486
memcpy(buf, name, name_size);
490
memcpy(buf, value, value_size);
499
char hex_checksum[33];
502
otab->createBlob(&bh, blob_len, metadata->getBuffer(0), metadata_size, RETAIN(iInputStream), NULL, &checksum);
504
cs_bin_to_hex(33, hex_checksum, 16, checksum.val);
505
iOutputStream->addHeader(MS_CHECKSUM_TAG, hex_checksum);
506
} else { // If there is no BLOB data then the client will send it to the cloud server themselves.
508
CSException::throwException(CS_CONTEXT, CS_ERR_MISSING_HTTP_HEADER, "Missing BLOB length header for cloud BLOB.");
509
if (otab->getDB()->myBlobType == MS_CLOUD_STORAGE) {
510
CloudKeyRec cloud_key;
511
uint32_t signature_time;
513
CloudDB *cloud = otab->getDB()->myBlobCloud;
517
cloud->cl_getNewKey(&cloud_key);
518
otab->createBlob(&bh, cloud_blob_len, metadata->getBuffer(0), metadata_size, NULL, &cloud_key);
521
signature = cloud->cl_getSignature(&cloud_key, iInputStream->getHeaderValue("Content-Type"), &signature_time);
524
info = cloud->cl_getCloudInfo(cloud_key.cloud_ref);
526
iOutputStream->addHeader(MS_CLOUD_SERVER, info->getServer());
527
iOutputStream->addHeader(MS_CLOUD_BUCKET, info->getBucket());
528
iOutputStream->addHeader(MS_CLOUD_KEY, info->getPublicKey());
529
iOutputStream->addHeader(MS_CLOUD_OBJECT_KEY, cloud->cl_getObjectKey(&cloud_key));
530
iOutputStream->addHeader(MS_BLOB_SIGNATURE, signature->getCString());
534
snprintf(time_str, 20, "%"PRIu32"", signature_time);
535
iOutputStream->addHeader(MS_BLOB_DATE, time_str);
538
// If the database is not using cloud storage then the client will
539
// resend the BLOB data as a normal BLOB when it fails to get the
540
// expected cloud server infor headers back.
544
handle_len = strlen(bh.bu_data);
545
iOutputStream->setContentLength(handle_len);
547
replyPending = false;
548
iOutputStream->writeHead();
549
iOutputStream->write(bh.bu_data, handle_len);
550
iOutputStream->flush();
559
void MSConnectionHandler::serviceConnection()
562
bool threadStarted = false;
565
iInputStream->readHead();
566
if (iInputStream->expect100Continue()) {
567
iOutputStream->clearHeaders();
568
iOutputStream->clearBody();
569
iOutputStream->setStatus(100);
570
iOutputStream->setContentLength(0);
571
iOutputStream->writeHead();
572
iOutputStream->flush();
575
if (!(method = iInputStream->getMethod()))
577
if (!threadStarted /* && iInputStream->keepAlive() */) { // Ignore keepalive: Never trust the client!
578
/* Start another service handler if no threads
579
* are waiting to listen!
581
threadStarted = true;
582
if (!MSNetwork::gWaitingToListen)
583
MSNetwork::startConnectionHandler();
586
if (strcmp(method, "GET") == 0)
588
else if (strcmp(method, "PUT") == 0 ||
589
strcmp(method, "POST") == 0)
591
else if (strcmp(method, "HEAD"))
594
CSException::throwCoreError(CS_CONTEXT, CS_ERR_UNKNOWN_METHOD, method);
598
bool MSConnectionHandler::initializeWork()
604
* Return false if no connection this thread should quit!
606
bool MSConnectionHandler::doWork()
610
/* Open a connection: */
616
/* Do the work for the connection: */
619
/* Close the connection: */
625
void *MSConnectionHandler::completeWork()
628
/* Close the stream, if it was openned. */
634
bool MSConnectionHandler::handleException()
637
/* Start another handler if required: */
638
if (!MSNetwork::gWaitingToListen)
639
MSNetwork::startConnectionHandler();
643
CSDaemon::handleException();