/** * \file * * \author Georg Hopp * * \copyright * Copyright © 2014 Georg Hopp * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "trbase.h" #include "trio.h" #include "trdata.h" #include "tr/connection.h" #include "tr/interface/protocol.h" #include "tr/interface/comm_end_point.h" static int connectionCtor(void * _this, va_list * params) { TR_Connection this = _this; TR_PARENTCALL(TR_Connection, _this, TR_Class, ctor, params); this->current_message = NULL; return 0; } static void connectionDtor(void * _this) { TR_Connection this = _this; TR_delete(this->current_message); TR_PARENTCALL(TR_Connection, _this, TR_Class, dtor); } static TR_ProtoMessage connectionNextMessage(void * _this) { TR_Connection this = _this; TR_CommEndPoint comm = _this; TR_RemoteData data = TR_queueGet(comm->read_buffer); TR_ProtoMessage ret_message = NULL; size_t end; if (NULL == data) return ret_message; if (! this->current_message || this->current_message->ready) { this->current_message = TR_protoCreateMessage(comm->protocol, data->remote); } end = TR_protoParse(comm->protocol, this->current_message, data); if (end != ((TR_SizedData)data)->size) { /** * TODO * This means that the parser has not consumed all of the data. * We do not know the reason, but with HTTP this should only occur * when the message is complete... anyway, to prevent us from * looping forever because a protocol implementation is buggy * we should close the connection after end was 0 the second time. * This can be done by firing a close event. */ switch(end) { default: { TR_RemoteData new_data = TR_new( TR_RemoteData, ((TR_SizedData)data)->data + end, ((TR_SizedData)data)->size - end, data->remote); TR_delete(data); data = new_data; } // intended drop through case 0: TR_queuePutFirst(comm->read_buffer, data); } } if (this->current_message->ready) { ret_message = this->current_message; this->current_message = NULL; } return ret_message; } static void connectionCompose(void * _this, TR_ProtoMessage message) { TR_queuePut( ((TR_CommEndPoint)_this)->write_buffer, TR_protoCompose(((TR_CommEndPoint)_this)->protocol, message)); } static void connectionCvInit(TR_class_ptr cls) { TR_EVENT_CREATE(cls, TR_CON_EVENT_NEW_CON); } intptr_t connection_events[TR_CON_EVENT_MAX + 1]; TR_INIT_IFACE(TR_Class, connectionCtor, connectionDtor, NULL); TR_INIT_IFACE( TR_CommEndPoint, connectionNextMessage, connectionCompose); TR_CREATE_CLASS( TR_Connection, TR_CommEndPoint, connectionCvInit, TR_IF(TR_Class), TR_IF(TR_CommEndPoint)) = { {{ TR_CON_EVENT_MAX + 1, connection_events }} }; // vim: set ts=4 sw=4: