/** * This callback does things a bit differently. * Instead of using a MultiResult, we use a single HttpResult object. * We won't ever have "multiple" http objects. */ static void http_complete_callback(lcb_http_request_t req, lcb_t instance, const void *cookie, lcb_error_t err, const lcb_http_resp_t *resp) { pycbc_HttpResult *htres = (pycbc_HttpResult*)cookie; htres->htreq = NULL; htres->rc = err; htres->htcode = resp->v.v0.status; htres->htflags |= PYCBC_HTRES_F_COMPLETE; if (!htres->parent) { return; } PYCBC_CONN_THR_END(htres->parent); add_data(htres, resp->v.v0.bytes, resp->v.v0.nbytes); get_headers(htres, resp); finalize_data(htres); PYCBC_CONN_THR_BEGIN(htres->parent); if (htres->htflags & PYCBC_HTRES_F_CHUNKED) { /** No data here */ if (!htres->parent->nremaining) { lcb_breakout(instance); } maybe_invoke_async_callback(htres); return; } if (htres->parent->flags & PYCBC_CONN_F_ASYNC) { maybe_invoke_async_callback(htres); return; } if (!--htres->parent->nremaining) { lcb_breakout(instance); } (void)instance; (void)req; }
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); }