DTerr DeviceNetworkSockets::tcp_recv (NetworkPacket &packet, const NetworkSocket &socket) { if (socket.is_empty()) return DT3_ERR_NONE; DTint socket_raw = *reinterpret_cast<DTint*>(socket.network_socket_data()); ssize_t size = ::recv( socket_raw, packet.data(), (DTuint) packet.data_size(), 0 ); if (size < 0) { packet.set_data_size(0); switch (errno) { //case EAGAIN: // Same as EWOULDBLOCK case EWOULDBLOCK: return DT3_ERR_NET_WOULD_BLOCK; default: LOG_MESSAGE << "TCPRecv: recv: " << strerror(errno) << " (" << (DTuint) errno << ")"; return DT3_ERR_NET_UNKNOWN; }; } else { packet.set_data_size(size); } return DT3_ERR_NONE; }
DTerr DeviceNetworkSockets::udp_recv (NetworkPacket &packet, const NetworkSocket &socket) { if (socket.is_empty()) return DT3_ERR_NONE; DTint socket_raw = *reinterpret_cast<DTint*>(socket.network_socket_data()); struct sockaddr from; socklen_t from_size = sizeof(from); DTsize size = ::recvfrom( socket_raw, packet.data(), (DTuint) packet.data_size(), 0, &from, &from_size ); if (size > 0) { packet.set_network_address( address_to_string(&from) ); packet.set_data_size(size); } else { switch (errno) { case EWOULDBLOCK: return DT3_ERR_NET_WOULD_BLOCK; default: return DT3_ERR_NET_UNKNOWN; }; } return DT3_ERR_NONE; }
void AssetDownloader::downloader_task ( const URL url, const FilePath save_path, DTfloat timeout) { std::shared_ptr<DeviceNetwork> network = System::network_manager(); // // Connecting Phase // // Update status update_status (STATUS_CONNECTING, 0,0); // Resolve the host NetworkSocket socket; NetworkAddress ip; DTerr err = network->resolve_host( ip, url.hostname(), url.port() ); if (err != DT3_ERR_NONE) { LOG_MESSAGE << "HTTPRequest: Unable to resolve host."; update_status (STATUS_ERROR, 0,0); return; } err = network->tcp_open(socket,ip); if (err != DT3_ERR_NONE) { LOG_MESSAGE << "HTTPRequest: Unable to resolve host."; update_status (STATUS_ERROR, 0,0); return; } // Send http request std::string server = url.hostname(); std::string path = url.path(); // Note: // HTTP/1.1 defines the "close" connection option for the sender to signal that the connection // will be closed after completion of the response. For example, // Connection: close // in either the request or the response header fields indicates that the connection SHOULD NOT // be considered `persistent' (section 8.1) after the current request/response is complete. // HTTP/1.1 applications that do not support persistent connections MUST include the "close" // connection option in every message. std::string request = "GET " + path + " HTTP/1.1\r\n" + "Host: " + server + "\r\n" + "User-Agent: DT3\r\n" + "Accept: */*\r\n" + "Cache-Control: max-age=0\r\n" + "Connection: close\r\n" + "\r\n"; LOG_MESSAGE << "Sending..."; LOG_MESSAGE << request; NetworkPacket packet_request; packet_request.set_data(request); err = network->tcp_send(packet_request, socket); if (err != DT3_ERR_NONE) { network->tcp_close(socket); LOG_MESSAGE << "HTTPRequest: Unable to send packet."; update_status (STATUS_ERROR, 0,0); return; } // // Downloading Phase // update_status (STATUS_DOWNLOADING, 0,0); // Hash the URL FilePath temp_file_path = FilePath(HAL::save_dir().full_path() + "/" + MoreStrings::cast_to_string(MoreStrings::hash(url.full_url()))); // Create a temporary file BinaryFileStream temp_file; err = FileManager::open(temp_file, temp_file_path, false); if (err != DT3_ERR_NONE) { network->tcp_close(socket); LOG_MESSAGE << "HTTPRequest: Unable to open file for writing."; update_status (STATUS_ERROR, 0,0); return; } LOG_MESSAGE << "HTTPRequest: Opened temp file at " << temp_file_path.full_path(); // Temporary storage for buffer std::string data; // Timer for timeout TimerHires timeout_timer; do { // Get the results NetworkPacket packet; packet.set_data_size(1024*16); err = network->tcp_recv(packet,socket); // If we recieved some data, append it to the buffer if (packet.data_size() > 0) { data += packet.data_as_string(); append_data(data, temp_file); timeout_timer.reset_abs_time(); } // Check complete if ( (_current_size == _total_size) && (_total_size > 0) ) { break; } // If we hit our timeout, then we abort if (timeout_timer.abs_time() > timeout) { network->tcp_close(socket); LOG_MESSAGE << "Http request timed out!"; update_status (STATUS_ERROR, 0,0); // Remove the temporary file temp_file_path.del(); return; } // Check for cancel if (_cancelled) { network->tcp_close(socket); LOG_MESSAGE << "Http request cancelled!"; update_status (STATUS_CANCELLED, 0,0); // Remove the temporary file temp_file_path.del(); return; } } while (err == DT3_ERR_NET_WOULD_BLOCK || err == DT3_ERR_NONE); // Close the connection, we're done network->tcp_close(socket); // Close off the stream finalize_data(data, temp_file); // // Finalizing phase // // Move the file save_path.del(); temp_file_path.move(save_path); LOG_MESSAGE << "Saving file to " << save_path.full_path(); // Final update of status update_status (STATUS_COMPLETE, _current_size, _total_size); }