Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fopen.c added, a fopen() style emulation for URL reading
- Loading branch information
Showing
2 changed files
with
228 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
/***************************************************************************** | ||
* _ _ ____ _ | ||
* Project ___| | | | _ \| | | ||
* / __| | | | |_) | | | ||
* | (__| |_| | _ <| |___ | ||
* \___|\___/|_| \_\_____| | ||
* | ||
* $Id$ | ||
* | ||
* This example source code introduces an fopen()/fread()/fclose() emulation | ||
* for URL reads. Using an approach similar to this, you could replace your | ||
* program's fopen() with this url_fopen() and fread() with url_fread() and | ||
* it should be possible to read remote streams instead of (only) local files. | ||
* | ||
* See the main() function at the bottom that shows a tiny app in action. | ||
* | ||
* This source code is a proof of concept. It will need further attention to | ||
* become production-use useful and solid. | ||
* | ||
* This example requires libcurl 7.9.7 or later. | ||
*/ | ||
#include <stdio.h> | ||
#include <string.h> | ||
#include <sys/time.h> | ||
|
||
#include <curl/curl.h> | ||
#include <curl/types.h> | ||
#include <curl/easy.h> | ||
|
||
struct data { | ||
int type; | ||
union { | ||
CURL *curl; | ||
FILE *file; | ||
} handle; | ||
|
||
/* TODO: We should perhaps document the biggest possible buffer chunk we can | ||
get from libcurl in one single callback... */ | ||
char buffer[CURL_MAX_WRITE_SIZE]; | ||
|
||
char *readptr; /* read from here */ | ||
int bytes; /* bytes available from read pointer */ | ||
|
||
CURLMcode m; /* stored from a previous url_fread() */ | ||
}; | ||
|
||
typedef struct data URL_FILE; | ||
|
||
/* we use a global one for convenience */ | ||
CURLM *multi_handle; | ||
|
||
static | ||
size_t write_callback(char *buffer, | ||
size_t size, | ||
size_t nitems, | ||
void *userp) | ||
{ | ||
URL_FILE *url = (URL_FILE *)userp; | ||
size *= nitems; | ||
|
||
memcpy(url->readptr, buffer, size); | ||
url->readptr += size; | ||
url->bytes += size; | ||
|
||
return size; | ||
} | ||
|
||
URL_FILE *url_fopen(char *url, char *operation) | ||
{ | ||
/* this code could check for URLs or types in the 'url' and | ||
basicly use the real fopen() for standard files */ | ||
|
||
URL_FILE *file; | ||
int still_running; | ||
|
||
file = (URL_FILE *)malloc(sizeof(URL_FILE)); | ||
if(!file) | ||
return NULL; | ||
|
||
memset(file, 0, sizeof(URL_FILE)); | ||
|
||
file->type = 1; /* marked as URL, use 0 for plain file */ | ||
file->handle.curl = curl_easy_init(); | ||
|
||
curl_easy_setopt(file->handle.curl, CURLOPT_URL, url); | ||
curl_easy_setopt(file->handle.curl, CURLOPT_FILE, file); | ||
curl_easy_setopt(file->handle.curl, CURLOPT_VERBOSE, FALSE); | ||
curl_easy_setopt(file->handle.curl, CURLOPT_WRITEFUNCTION, write_callback); | ||
|
||
if(!multi_handle) | ||
multi_handle = curl_multi_init(); | ||
|
||
curl_multi_add_handle(multi_handle, file->handle.curl); | ||
|
||
while(CURLM_CALL_MULTI_PERFORM == | ||
curl_multi_perform(multi_handle, &still_running)); | ||
|
||
/* if still_running would be 0 now, we should return NULL */ | ||
|
||
return file; | ||
} | ||
|
||
void url_fclose(URL_FILE *file) | ||
{ | ||
/* make sure the easy handle is not in the multi handle anymore */ | ||
curl_multi_remove_handle(multi_handle, file->handle.curl); | ||
|
||
/* cleanup */ | ||
curl_easy_cleanup(file->handle.curl); | ||
} | ||
|
||
|
||
|
||
size_t url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file) | ||
{ | ||
fd_set fdread; | ||
fd_set fdwrite; | ||
fd_set fdexcep; | ||
int maxfd; | ||
struct timeval timeout; | ||
int rc; | ||
int still_running = 0; | ||
|
||
if(!file->bytes) { /* no data available at this point */ | ||
|
||
file->readptr = file->buffer; /* reset read pointer */ | ||
|
||
if(CURLM_CALL_MULTI_PERFORM == file->m) { | ||
while(CURLM_CALL_MULTI_PERFORM == | ||
curl_multi_perform(multi_handle, &still_running)) { | ||
if(file->bytes) { | ||
printf("(fread) WOAH! THis happened!\n"); | ||
break; | ||
} | ||
} | ||
if(!still_running) { | ||
printf("NO MORE RUNNING AROUND!\n"); | ||
return 0; | ||
} | ||
} | ||
|
||
FD_ZERO(&fdread); | ||
FD_ZERO(&fdwrite); | ||
FD_ZERO(&fdexcep); | ||
|
||
/* set a suitable timeout to fail on */ | ||
timeout.tv_sec = 500; /* 5 minutes */ | ||
timeout.tv_usec = 0; | ||
|
||
/* get file descriptors from the transfers */ | ||
curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd); | ||
|
||
rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout); | ||
|
||
switch(rc) { | ||
case -1: | ||
/* select error */ | ||
break; | ||
case 0: | ||
break; | ||
default: | ||
/* timeout or readable/writable sockets */ | ||
do { | ||
file->m = curl_multi_perform(multi_handle, &still_running); | ||
|
||
if(file->bytes) | ||
/* we have received data, return that now */ | ||
break; | ||
|
||
} while(CURLM_CALL_MULTI_PERFORM == file->m); | ||
|
||
|
||
if(!still_running) | ||
printf("NO MORE RUNNING AROUND!\n"); | ||
|
||
break; | ||
} | ||
} | ||
else | ||
printf("(fread) Skip network read\n"); | ||
|
||
if(file->bytes) { | ||
/* data already available, return that */ | ||
int want = size * nmemb; | ||
|
||
if(file->bytes < want) | ||
want = file->bytes; | ||
|
||
memcpy(ptr, file->readptr, want); | ||
file->readptr += want; | ||
file->bytes -= want; | ||
|
||
printf("(fread) return %d bytes\n", want); | ||
|
||
return want; | ||
} | ||
return 0; /* no data available to return */ | ||
} | ||
|
||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
URL_FILE *handle; | ||
int nread; | ||
char buffer[256]; | ||
|
||
handle = url_fopen("http://www.haxx.se", "r"); | ||
|
||
if(!handle) { | ||
printf("couldn't url_fopen()\n"); | ||
} | ||
|
||
do { | ||
nread = url_fread(buffer, sizeof(buffer), 1, handle); | ||
|
||
printf("We got: %d bytes\n", nread); | ||
} while(nread); | ||
|
||
url_fclose(handle); | ||
|
||
return 0; | ||
} |