I want to play mp3 files on the internet without downloading them. So, I use libcurl to get it as a stream in memory, like this:
static size_t use_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
/* stream is NULL */
/* What to do with the stream of data ? */
}
CURLcode download_file(const char *url, const char *path, curl_progress_callback progress) {
CURL *curl;
CURLcode res = 0;
FILE *fp;
if ((curl = curl_easy_init())) {
if (progress) {
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress);
}
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, use_data);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
fclose(fp);
}
return res;
}
How can I parse the stream in memory to play sounds ?
The easiest way for you IMHO would be using lightweight MP3 decoding library. For example, minimp3 does it's job and consists of only 2 files.
http://keyj.emphy.de/minimp3
The API is very simple and a usage example can be found here: https://github.com/corporateshark/PortAMP/tree/master/src/Decoders/MP3
Related
I am downloading file quite commonly with curl. However, the server does a tricky thing: it return non-200 code and still sends some data. The problem is that I have the HTTP code after the data are written, but I do not want to write anything if it is non-200. Does anyone know a way to do that without storing data on disk or memory?
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteHandler);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr);
res = curl_easy_perform(curl);
if (res == CURLE_OK) {
return 0;
}
long response_code;
curl_easy_getinfo(curl_.get(), CURLINFO_RESPONSE_CODE, &response_code);
if (response_code != 200) {
return 0;
}
size_t curlWriteHandler(char* chars, size_t size, size_t nmemb, void* userp) {
// write to file
return size * nmemb;
}
Setting CURLOPT_FAILONERROR should do it for 4xx and 5xx errors.
When this option is used and an error is detected, it will cause the connection to get closed and CURLE_HTTP_RETURNED_ERROR is returned.
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
Closing connection is not good for me, it is important to reuse one. Can you think about anything else?
Unfortunately I can't find a way to make CURLOPT_FAILONERROR not close the connection.
The other option is to make the write function aware of the response. Unfortunately the curl handle is not passed into the callback.
We could make the curl variable global. Or we can take advantage of the void *userdata option to the write callback and pass in a struct containing both the curl handle and the buffer.
Here's a rough sketch demonstrating how the write callback can get access to the response code and also save the response body.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <curl/curl.h>
typedef struct {
CURL *curl;
char *buf;
} curl_write_data;
size_t curlWriteHandler(char* chars, size_t size, size_t nmemb, void* userp) {
curl_write_data *curl_data = (curl_write_data*)userp;
long response_code;
curl_easy_getinfo(curl_data->curl, CURLINFO_RESPONSE_CODE, &response_code);
printf("Response: %ld\n", response_code);
// Now we can save if we like.
if( response_code < 300 ) {
curl_data->buf = malloc(size*(nmemb+1));
strcpy(curl_data->buf, chars);
strcat(curl_data->buf, "\0");
return size * nmemb;
}
else {
return 0;
}
}
int main() {
CURL *curl = curl_easy_init();
if(!curl) {
perror("Cant' init curl");
}
curl_write_data curl_data = { .curl = curl, .buf = NULL };
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/alsdfjalj");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteHandler);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &curl_data);
curl_easy_perform(curl);
if( curl_data.buf ) {
puts(curl_data.buf);
}
}
I'm not sure if this is the best idea, its what I came up with.
I am trying to write curl c code to using the http webdav put method to upload a file.
Using wireshark I have tried to capture the packets, there is 301 response from the server.
When I try to put the file from the PC to webserver it works fine
Below is the code:
static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream)
{
size_t retcode;
curl_off_t nread;
/* in real-world cases, this would probably get this data differently
as this fread() stuff is exactly what the library already would do
by default internally */
retcode = fread(ptr, size, nmemb, stream);
nread = (curl_off_t)retcode;
fprintf(stderr, "*** We read %" CURL_FORMAT_CURL_OFF_T
" bytes from file\n", nread);
return retcode;
}
int curlApache ()
{
CURL *curl;
CURLcode res;
FILE * hd_src;
struct stat file_info;
char *file;
char *url;
char error;
file = "/bd0/filecreate.txt";
url = "http://10.1.21.14/webdav/test.txt";
curl_slist *slist = NULL;
slist = curl_slist_append(slist, "Accept: text/xml");
slist = curl_slist_append(slist, "Depth: infinity");
slist = curl_slist_append(slist, "Connection: Keep-Alive");
slist = curl_slist_append(slist, "Content-Type: text/xml");
slist = curl_slist_append(slist, "Expect:");
/* get the file size of the local file */
stat(file, &file_info);
hd_src = fopen(file, "a+");
if (hd_src == NULL)
printf("Disc full or no permission\n");
const char *str = "This is the file content";
const char read[24];
if (hd_src != NULL)
if (fputs (str, hd_src) != EOF);
if( fgets (read, 24, hd_src)!=NULL )
{
/* writing content to stdout */
puts(read);
}
/* In windows, this will init the winsock stuff */
curl_global_init(CURL_GLOBAL_ALL);
/* get a curl handle */
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_VERBOSE, 3L);
/* we want to use our own read function */
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
/* enable uploading */
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
/* HTTP PUT please */
curl_easy_setopt(curl, CURLOPT_PUT, 1L);
/* tell libcurl we can use "any" auth, which lets the lib pick one, but it also costs one extra round-trip and possibly sending of all the PUT data twice!!! */
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_DIGEST);
curl_easy_setopt(curl, CURLOPT_USERPWD, "admin:nimo0630");
fseek(hd_src, 0L, SEEK_END);
int file_size;
file_size = ftell(hd_src);
Curl_setopt(curl, CURLOPT_INFILE, hd_src);
Curl_setopt(curl, CURLOPT_INFILESIZE, file_size);
/* specify target URL, and note that this URL should include a file
name, not only a directory */
curl_easy_setopt(curl, CURLOPT_URL, url);
/* now specify which file to upload */
curl_easy_setopt(curl, CURLOPT_READDATA, hd_src);
/* provide the size of the upload, we specicially typecast the value
to curl_off_t since we must be sure to use the correct data size */
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, curl_off_t)file_info.st_size);
/* Now run off and do what you've been told! */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
if(!res) {
/* extract the available authentication types */
long auth;
res = curl_easy_getinfo(curl, CURLINFO_HTTPAUTH_AVAIL, &auth);
if(!res)
{
if(!auth)
printf("No auth available, perhaps no 401?\n");
else
{
printf("%s%s%s%s\n", \
auth & CURLAUTH_BASIC ? "Basic ":"", \
auth & CURLAUTH_DIGEST ? "Digest ":"", \
auth & CURLAUTH_NEGOTIATE ? "Negotiate ":"", \
auth % CURLAUTH_NTLM ? "NTLM ":"");
}
}
}
/* always cleanup */
curl_easy_cleanup(curl);
}
fclose(hd_src); /* close the local file */
curl_global_cleanup();
return 0;
}
There is status code 301 returned from the server
I believe you want to understand how to debug this issue. If you paste your code, a more specific answer can be provided.
First check if you are able to upload file using command line curl. This will tell you if server is working fine
Check if the options you use are provided in C API
Check the API's are not returning any error
You could use tcpdump / wireshark to capture packets on the client or server to see if packets went out and what was the http content. You may not see any packet if API failed
I am trying to send a SOAP request from a xml file and send to a SOAP service and then read the response while saving it into a file, using libcurl.
An example request in an xml file is as followed:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:olm="http://127.0.0.1:1090/services/olmDSMN">
<soapenv:Header/>
<soapenv:Body>
<olm:getAllCommercialProducts soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</soapenv:Body>
</soapenv:Envelope>
The libcurl code I'm trying to use to make the read and write is as follows:
#define URL "http://192.168.56.101:8088/olmDSMN"
size_t write_data(void *ptr, size_t size, size_t nmeb, void *stream)
{
return fwrite(ptr,size,nmeb,stream);
}
size_t read_data(void *ptr, size_t size, size_t nmeb, void *stream)
{
return fread(ptr,size,nmeb,stream);
}
int sendMessage (char * inFile, char * outFile) {
//writing to file initially
FILE * rfp = fopen(inFile, "r");
if(!rfp) {
perror("Read File Open:");
// exit(0);
}
FILE * wfp = fopen(outFile, "w+"); //File pointer to write the soap response
if(!wfp) {
perror("Write File Open:");
// exit(0);
}
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, URL);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_data); //Reads xml file to be sent via POST operationt
curl_easy_setopt(curl, CURLOPT_READDATA, rfp);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); //Gets data to be written to file
curl_easy_setopt(curl, CURLOPT_WRITEDATA, wfp); //Writes result to file
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
return 1;
}
}
At the moment the above does not work. When the function is invoked is just waits(as if waiting on a response that never comes). I'm just learning libcurl, so I'm sure I've done many things incorrectly. I've really been piecing together a couple different tutorials to get it do what I want it to do. Any assistance would be greatly appreciated.
Ok, so after a whole load of fiddling and piecing together code from different examples I've figured out how to perform the above task. See code below:
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
#define URL "http://192.168.56.101:8088/olmDSMN"
size_t write_data(void *ptr, size_t size, size_t nmeb, void *stream)
{
return fwrite(ptr,size,nmeb,stream);
}
size_t read_data(void *ptr, size_t size, size_t nmeb, void *stream)
{
return fread(ptr,size,nmeb,stream);
}
int sendMessage (char * inFile, char * outFile) {
//writing to file initially
FILE * rfp = fopen(inFile, "r");
if(!rfp) {
perror("Read File Open:");
// exit(0);
}
FILE * wfp = fopen(outFile, "w+"); //File pointer to write the soap response
if(!wfp) {
perror("Write File Open:");
// exit(0);
}
struct curl_slist *header = NULL;
header = curl_slist_append (header, "Content-Type:text/xml");
header = curl_slist_append (header, "SOAPAction: myur1");
header = curl_slist_append (header, "Transfer-Encoding: chunked");
header = curl_slist_append (header, "Expect:");
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, URL);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_data); //Reads xml file to be sent via POST operationt
curl_easy_setopt(curl, CURLOPT_READDATA, rfp);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); //Gets data to be written to file
curl_easy_setopt(curl, CURLOPT_WRITEDATA, wfp); //Writes result to file
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)-1);
// curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_VERBOSE,1L);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
return 1;
}
}
Presuming that your SOAP server expects a HTTP POST, you need to:
Set the CURLOPT_POST option;
Set the Content-Type header Content-Type: application/soap+xml using CURLOPT_HTTPHEADER;
Set the size of your XML request using the CURLOPT_POSTFIELDSIZE option.
I am using the following code to download files from the internet:
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
size_t written;
written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main(int argc, char** argv)
{
FILE *downloaded_file;
if ( (downloaded_file = fopen (download_path , "w" ) ) != NULL )
{
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_URL, "www.asd.com/files/file_to_download.rar");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, downloaded_file);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (res == CURLE_OK)
{
printf("Download complete!\n");
}
}
fclose(downloaded_file);
}
}
How can I measure the current download speed (e.g. every second) and the remaining time to complete the download?
You can use CURLOPT_PROGRESSFUNCTION. curl will pass 5 arguments to your callback function, clientp, dltotal, dlnow, ultotal, and ulnow. clientp is a pointer you provide with CURLOPT_PROGRESSDATA. The total parameters are the total amounts that need to be downloaded; the now ones are the amounts so far. Unknown values are 0.
To use this, you must set CURLOPT_NOPROGRESS to 0.
CURLOPT_PROGRESSFUNCTION has been deprecated since v7.32.0, instead you can use CURLOPT_XFERINFOFUNCTION, the usage and callback structure (all the arguments dltotal, dlnow ...etc) are almost the same as CURLOPT_PROGRESSFUNCTION
I am building an application (on windows using Dev-C++) and I want it to download a file. I am doing this using libcurl (I have already installed the source code using packman). I found a working example (http://siddhantahuja.wordpress.com/2009/04/12/how-to-download-a-file-from-a-url-and-save-onto-local-directory-in-c-using-libcurl/) but it doesn't close the file after download is complete. I would like an example of how to download a file in C.
The example you are using is wrong. See the man page for easy_setopt. In the example write_data uses its own FILE, *outfile, and not the fp that was specified in CURLOPT_WRITEDATA. That's why closing fp causes problems - it's not even opened.
This is more or less what it should look like (no libcurl available here to test)
#include <stdio.h>
#include <curl/curl.h>
/* For older cURL versions you will also need
#include <curl/types.h>
#include <curl/easy.h>
*/
#include <string>
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main(void) {
CURL *curl;
FILE *fp;
CURLcode res;
char *url = "http://localhost/aaa.txt";
char outfilename[FILENAME_MAX] = "C:\\bbb.txt";
curl = curl_easy_init();
if (curl) {
fp = fopen(outfilename,"wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
fclose(fp);
}
return 0;
}
Updated: as suggested by #rsethc types.h and easy.h aren't present in current cURL versions anymore.
Just for those interested you can avoid writing custom function by passing NULL as last parameter (if you do not intend to do extra processing of returned data).
In this case default internal function is used.
Details
http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTWRITEDATA
Example
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl;
FILE *fp;
CURLcode res;
char *url = "http://stackoverflow.com";
char outfilename[FILENAME_MAX] = "page.html";
curl = curl_easy_init();
if (curl)
{
fp = fopen(outfilename,"wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
}
return 0;
}