2
* Copyright (C) 2011 Tuomo Penttinen, all rights reserved.
4
* Author: Tuomo Penttinen <tp@herqq.org>
6
* This file is part of Herqq UPnP Av (HUPnPAv) library.
8
* Herqq UPnP Av is free software: you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation, either version 3 of the License, or
11
* (at your option) any later version.
13
* Herqq UPnP Av is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with Herqq UPnP Av. If not, see <http://www.gnu.org/licenses/>.
22
#include "hconnectionmanager_sourceservice_p.h"
24
#include "../connectionmanager/hconnectioninfo.h"
25
#include "../connectionmanager/hconnectionmanager_service_p.h"
27
#include "../hav_global_p.h"
28
#include "../common/hresource.h"
29
#include "../common/hprotocolinfo.h"
30
#include "../cds_model/cds_objects/hitem.h"
31
#include "../cds_model/cds_objects/hcontainer.h"
32
#include "../cds_model/datasource/hcds_datasource.h"
34
#include <HUpnpCore/private/hlogger_p.h>
35
#include <HUpnpCore/private/hhttp_server_p.h>
36
#include <HUpnpCore/private/hhttp_header_p.h>
37
#include <HUpnpCore/private/hhttp_messagecreator_p.h>
39
#include <QtNetwork/QTcpSocket>
50
HHttpStreamer::HHttpStreamer(
51
HMessagingInfo* mi, const QByteArray& header, QIODevice* data, QObject* parent) :
53
m_bufSize(1024*64), m_buf(new char[m_bufSize]), m_dataToSend(data),
54
m_mi(mi), m_header(header), m_read(0), m_written(0)
57
&m_mi->socket(), SIGNAL(bytesWritten(qint64)),
58
this, SLOT(bytesWritten(qint64)));
59
Q_ASSERT(ok); Q_UNUSED(ok)
62
HHttpStreamer::~HHttpStreamer()
69
void HHttpStreamer::bytesWritten(qint64 written)
73
if (m_dataToSend->atEnd())
78
else if (m_dataToSend->pos() > 0)
80
// We've past the HTTP header.
84
if (m_written >= m_read)
86
m_read = m_dataToSend->read(m_buf, m_bufSize);
89
HLOG_WARN(QString("Failed to read data from the data source: [%1]").arg(
90
m_dataToSend->errorString()));
99
written = m_mi->socket().write(m_buf + m_written, m_read - m_written);
102
HLOG_WARN(QString("Failed to send data: %1").arg(m_mi->socket().errorString()));
107
void HHttpStreamer::send()
110
Q_ASSERT(m_dataToSend->pos() == 0);
112
qint64 wrote = m_mi->socket().write(m_header);
113
if (wrote < m_header.size())
116
"Failed to send HTTP header to the destination: [%1]. "
117
"Aborting data transfer.").arg(m_mi->socket().errorString()));
123
/*******************************************************************************
124
* HConnectionManagerHttpServer
125
******************************************************************************/
126
HConnectionManagerHttpServer::HConnectionManagerHttpServer(
127
const QByteArray& loggingId, HConnectionManagerSourceService* owner) :
128
HHttpServer(loggingId, owner), m_owner(owner)
133
HConnectionManagerHttpServer::~HConnectionManagerHttpServer()
137
void HConnectionManagerHttpServer::incomingUnknownGetRequest(
138
HMessagingInfo* mi, const HHttpRequestHeader& hdr)
140
HLOG2(H_AT, H_FUN, m_loggingIdentifier);
142
QScopedPointer<QIODevice> dev(
143
m_owner->m_dataSource->loadItemData(hdr.path().remove('/')));
147
if (dev->isSequential())
149
// TODO send in chunks
150
Q_ASSERT_X(false, "", "Currently sequential data sources are not supported");
152
else if (dev->size() < maxBytesToLoad())
154
QByteArray data = dev->readAll();
155
mi->setKeepAlive(true);
156
m_httpHandler->send(mi, HHttpMessageCreator::createResponse(Ok, *mi, data));
160
HHttpStreamer* streamer =
161
new HHttpStreamer(mi, HHttpMessageCreator::createHeaderData(
162
Ok, *mi, dev->size()), dev.take(), this);
169
mi->setKeepAlive(true);
170
m_httpHandler->send(mi, HHttpMessageCreator::createResponse(BadRequest, *mi));
174
/*******************************************************************************
175
* HConnectionManagerSourceService
176
******************************************************************************/
177
HConnectionManagerSourceService::HConnectionManagerSourceService(
178
HAbstractCdsDataSource* dataSource) :
179
HConnectionManagerService(),
181
m_httpServer(new HConnectionManagerHttpServer(
182
h_ptr->m_loggingIdentifier, this))
184
Q_ASSERT_X(dataSource, "", "Valid HCdsDataSource has to be provided");
185
m_dataSource = dataSource;
188
HConnectionManagerSourceService::~HConnectionManagerSourceService()
190
HLOG2(H_AT, H_FUN, h_ptr->m_loggingIdentifier);
194
bool HConnectionManagerSourceService::finalizeInit(QString* /*errDescription*/)
196
setSourceProtocolInfo(HProtocolInfo("http-get:*:*:*"));
197
createDefaultConnection(sourceProtocolInfo().at(0));
200
m_dataSource, SIGNAL(objectModified(Herqq::Upnp::Av::HObject*, Herqq::Upnp::Av::HObjectEventInfo)),
201
this, SLOT(objectModified(Herqq::Upnp::Av::HObject*, Herqq::Upnp::Av::HObjectEventInfo)));
202
Q_ASSERT(ok); Q_UNUSED(ok)
205
m_dataSource, SIGNAL(containerModified(Herqq::Upnp::Av::HContainer*, Herqq::Upnp::Av::HContainerEventInfo)),
206
this, SLOT(containerModified(Herqq::Upnp::Av::HContainer*, Herqq::Upnp::Av::HContainerEventInfo)));
212
void HConnectionManagerSourceService::objectModified(
213
HObject* source, const HObjectEventInfo& eventInfo)
219
void HConnectionManagerSourceService::containerModified(
220
HContainer* source, const HContainerEventInfo& eventInfo)
223
if (eventInfo.type() == HContainerEventInfo::ChildAdded)
225
HItem* item = m_dataSource->findItem(eventInfo.childId());
233
void HConnectionManagerSourceService::addLocation(HItem* item)
235
QList<QUrl> rootUrls = m_httpServer->rootUrls();
236
Q_ASSERT(!rootUrls.isEmpty());
238
HResources resources = item->resources();
240
if (resources.size())
244
for(int i = 0; i < resources.size(); ++i)
246
if (resources[i].location().isEmpty())
249
rootUrls[urlsIndex++ % rootUrls.size()].
250
toString().append('/').append(item->id());
252
resources[i].setLocation(rootUrl);
254
HProtocolInfo pi = resources[i].protocolInfo();
255
pi.setProtocol("http-get");
257
resources[i].setProtocolInfo(pi);
264
HProtocolInfo pi = resources[0].protocolInfo();
265
pi.setProtocol("http-get");
268
rootUrls[0].toString().append('/').append(item->id()), pi);
270
resources.append(res);
275
foreach(const QUrl& rootUrl, rootUrls)
277
QUrl location(rootUrl.toString().append('/').append(item->id()));
278
HResource resource(location, sourceProtocolInfo()[0]);
279
resources.append(resource);
283
item->setResources(resources);
286
bool HConnectionManagerSourceService::init()
288
HLOG2(H_AT, H_FUN, h_ptr->m_loggingIdentifier);
290
if (!m_httpServer->init())
292
HLOG_WARN("Failed to initialize HTTP server");
295
else if (m_httpServer->rootUrls().isEmpty())
300
HItems items = m_dataSource->items();
301
foreach(HItem* item, items)
309
bool HConnectionManagerSourceService::isInitialized() const
311
return m_httpServer->isInitialized();