Subversion Repositories Kolibri OS

Rev

Rev 3584 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3584 sourcerer 1
/*
2
 * Copyright 2006 Daniel Silverstone 
3
 * Copyright 2007 James Bursa 
4
 * Copyright 2003 Phil Mellor 
5
 *
6
 * This file is part of NetSurf.
7
 *
8
 * NetSurf is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; version 2 of the License.
11
 *
12
 * NetSurf is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program.  If not, see .
19
 */
20
 
21
/** \file
22
 * Fetching of data from a URL (implementation).
23
 *
24
 * This implementation uses libcurl's 'multi' interface.
25
 *
26
 *
27
 * The CURL handles are cached in the curl_handle_ring. There are at most
28
 * ::max_cached_fetch_handles in this ring.
29
 */
30
 
31
#include 
32
#include 
33
#include 
34
#include 
35
#include 
36
#include 
37
#include 
38
#include 
39
 
40
#include 
41
 
42
#include "utils/config.h"
43
//#include 
44
#include "content/fetch.h"
45
#include "content/fetchers/curl.h"
46
#include "content/urldb.h"
47
#include "desktop/netsurf.h"
48
#include "desktop/options.h"
49
#include "utils/log.h"
50
#include "utils/messages.h"
51
#include "utils/schedule.h"
52
#include "utils/utils.h"
53
#include "utils/ring.h"
54
#include "utils/useragent.h"
55
 
56
/* BIG FAT WARNING: This is here because curl doesn't give you an FD to
57
 * poll on, until it has processed a bit of the handle.	 So we need schedules
58
 * in order to make this work.
59
 */
60
#include 
61
 
62
/* uncomment this to use scheduler based calling
63
#define FETCHER_CURLL_SCHEDULED 1
64
*/
65
 
66
 
3616 sourcerer 67
struct fetch_curl_context {
68
	struct fetch_curl_context *r_next, *r_prev;
3584 sourcerer 69
 
3616 sourcerer 70
	struct fetch *fetchh; /**< Handle for this fetch */
3584 sourcerer 71
 
3616 sourcerer 72
	bool aborted; /**< Flag indicating fetch has been aborted */
73
	bool locked; /**< Flag indicating entry is already entered */
74
 
75
	nsurl *url; /**< The full url the fetch refers to */
76
	char *path; /**< The actual path to be used with open() */
77
 
78
	time_t file_etag; /**< Request etag for file (previous st.m_time) */
3584 sourcerer 79
};
80
 
3616 sourcerer 81
static struct fetch_curl_context *ring = NULL;
3584 sourcerer 82
 
83
 
3616 sourcerer 84
static bool fetch_curl_initialise(lwc_string *scheme); //here
85
static void fetch_curl_finalise(lwc_string *scheme); //here
86
static bool fetch_curl_can_fetch(const nsurl *url); //here
3584 sourcerer 87
static void * fetch_curl_setup(struct fetch *parent_fetch, nsurl *url,
88
		 bool only_2xx, bool downgrade_tls, const char *post_urlenc,
89
		 const struct fetch_multipart_data *post_multipart,
3616 sourcerer 90
		 const char **headers); //here
91
static bool fetch_curl_start(void *vfetch); //here
3584 sourcerer 92
 
3616 sourcerer 93
static void fetch_curl_abort(void *vf); //here
94
static void fetch_curl_free(void *f); //here
95
static void fetch_curl_poll(lwc_string *scheme_ignored); //here
3584 sourcerer 96
 
3616 sourcerer 97
 
3584 sourcerer 98
/**
99
 * Initialise the fetcher.
100
 *
101
 * Must be called once before any other function.
102
 */
103
 
104
void fetch_curl_register(void)
105
{
106
 
107
lwc_string *scheme;
108
 
109
 
110
LOG(("curl register\n"));
111
 
112
lwc_intern_string("http", SLEN("http"), &scheme);
113
 
114
	if (!fetch_add_fetcher(scheme,
3616 sourcerer 115
				fetch_curl_initialise, //here
116
				fetch_curl_can_fetch, //here
3584 sourcerer 117
				fetch_curl_setup,
118
				fetch_curl_start,
3616 sourcerer 119
				fetch_curl_abort, //here
120
				fetch_curl_free, //here
3584 sourcerer 121
#ifdef FETCHER_CURLL_SCHEDULED
122
				       NULL,
123
#else
3616 sourcerer 124
				fetch_curl_poll, //here
3584 sourcerer 125
#endif
3616 sourcerer 126
				fetch_curl_finalise)) {  //here
3584 sourcerer 127
			LOG(("Unable to register cURL fetcher for HTTP"));
128
		}
129
 
130
 
131
		lwc_intern_string("https", SLEN("https"), &scheme);
132
 
133
	if (!fetch_add_fetcher(scheme,
134
				fetch_curl_initialise,
135
				fetch_curl_can_fetch,
136
				fetch_curl_setup,
137
				fetch_curl_start,
138
				fetch_curl_abort,
139
				fetch_curl_free,
140
#ifdef FETCHER_CURLL_SCHEDULED
141
				       NULL,
142
#else
143
				fetch_curl_poll,
144
#endif
145
				fetch_curl_finalise)) {
146
			LOG(("Unable to register cURL fetcher for HTTPS"));
147
		}
148
 
149
}
150
 
151
 
152
/**
153
 * Initialise a cURL fetcher.
154
 */
155
 
156
bool fetch_curl_initialise(lwc_string *scheme)
157
{
158
 
159
LOG(("curl initi lwc\n"));
160
	return true; /* Always succeeds */
161
}
162
 
163
 
164
/**
165
 * Finalise a cURL fetcher
166
 */
167
 
168
void fetch_curl_finalise(lwc_string *scheme)
169
{
170
LOG(("curl finali\n"));
171
}
172
 
3616 sourcerer 173
static bool fetch_curl_can_fetch(const nsurl *url)
3584 sourcerer 174
{
175
	LOG(("curl can fetch\n"));
3616 sourcerer 176
	return true; //let's lie a bit
3584 sourcerer 177
}
178
 
179
/**
180
 * Start fetching data for the given URL.
181
 *
182
 * The function returns immediately. The fetch may be queued for later
183
 * processing.
184
 *
185
 * A pointer to an opaque struct curl_fetch_info is returned, which can be
186
 * passed to fetch_abort() to abort the fetch at any time. Returns 0 if memory
187
 * is exhausted (or some other fatal error occurred).
188
 *
189
 * The caller must supply a callback function which is called when anything
190
 * interesting happens. The callback function is first called with msg
191
 * FETCH_HEADER, with the header in data, then one or more times
192
 * with FETCH_DATA with some data for the url, and finally with
193
 * FETCH_FINISHED. Alternatively, FETCH_ERROR indicates an error occurred:
194
 * data contains an error message. FETCH_REDIRECT may replace the FETCH_HEADER,
195
 * FETCH_DATA, FETCH_FINISHED sequence if the server sends a replacement URL.
196
 *
197
 * Some private data can be passed as the last parameter to fetch_start, and
198
 * callbacks will contain this.
199
 */
200
 
3616 sourcerer 201
void * fetch_curl_setup (struct fetch *fetchh,
202
		 nsurl *url,
203
		 bool only_2xx,
204
		 bool downgrade_tls,
205
		 const char *post_urlenc,
3584 sourcerer 206
		 const struct fetch_multipart_data *post_multipart,
207
		 const char **headers)
208
{
209
 
3616 sourcerer 210
	LOG(("curl setup\n"));
211
 
212
	struct fetch_curl_context *ctx;
213
	int i;
3584 sourcerer 214
 
3616 sourcerer 215
	ctx = calloc(1, sizeof(*ctx));
216
	if (ctx == NULL)
217
		return NULL;
3584 sourcerer 218
 
3616 sourcerer 219
	ctx->path = url_to_path(nsurl_access(url));
220
	if (ctx->path == NULL) {
221
		free(ctx);
222
		return NULL;
223
	}
3584 sourcerer 224
 
3616 sourcerer 225
	ctx->url = nsurl_ref(url);
3584 sourcerer 226
 
227
 
3616 sourcerer 228
	ctx->fetchh = fetchh;
3584 sourcerer 229
 
3616 sourcerer 230
	RING_INSERT(ring, ctx);
3584 sourcerer 231
 
3616 sourcerer 232
	return ctx;
3584 sourcerer 233
}
234
 
235
 
236
/**
3616 sourcerer 237
 * Dispatch a single job
3584 sourcerer 238
 */
3616 sourcerer 239
bool fetch_curl_start(void *vfetch)
3584 sourcerer 240
{
3616 sourcerer 241
LOG(("curl start\n"));
242
	return true;
3584 sourcerer 243
}
244
 
245
 
246
 
247
 
248
 
249
 
250
/**
3616 sourcerer 251
 * Abort a fetch.
3584 sourcerer 252
 */
253
 
3616 sourcerer 254
void fetch_curl_abort(void *ctx)
3584 sourcerer 255
{
3616 sourcerer 256
	struct fetch_curl_context *c = ctx;
257
 
258
	/* To avoid the poll loop having to deal with the fetch context
259
	 * disappearing from under it, we simply flag the abort here.
260
	 * The poll loop itself will perform the appropriate cleanup.
261
	 */
262
	c->aborted = true;
3584 sourcerer 263
}
264
 
265
 
266
/**
3616 sourcerer 267
 * Free a fetch structure and associated resources.
3584 sourcerer 268
 */
269
 
3616 sourcerer 270
void fetch_curl_free(void *ctx)
3584 sourcerer 271
{
3616 sourcerer 272
	struct fetch_curl_context *c = ctx;
273
	nsurl_unref(c->url);
274
	free(c->path);
275
	RING_REMOVE(ring, c);
276
	free(ctx);
3584 sourcerer 277
}
278
 
3616 sourcerer 279
static inline bool fetch_curl_send_callback(const fetch_msg *msg,
280
		struct fetch_curl_context *ctx)
281
{
282
	ctx->locked = true;
283
	fetch_send_callback(msg, ctx->fetchh);
284
	ctx->locked = false;
3584 sourcerer 285
 
3616 sourcerer 286
	return ctx->aborted;
287
}
3584 sourcerer 288
 
3616 sourcerer 289
static bool fetch_curl_send_header(struct fetch_curl_context *ctx,
290
		const char *fmt, ...)
3584 sourcerer 291
{
3616 sourcerer 292
	fetch_msg msg;
293
	char header[64];
294
	va_list ap;
3584 sourcerer 295
 
3616 sourcerer 296
	va_start(ap, fmt);
3584 sourcerer 297
 
3616 sourcerer 298
	vsnprintf(header, sizeof header, fmt, ap);
3584 sourcerer 299
 
3616 sourcerer 300
	va_end(ap);
3584 sourcerer 301
 
3616 sourcerer 302
	msg.type = FETCH_HEADER;
303
	msg.data.header_or_data.buf = (const uint8_t *) header;
304
	msg.data.header_or_data.len = strlen(header);
305
	fetch_curl_send_callback(&msg, ctx);
3584 sourcerer 306
 
3616 sourcerer 307
	return ctx->aborted;
308
}
3584 sourcerer 309
 
3616 sourcerer 310
static void fetch_curl_process_error(struct fetch_curl_context *ctx, int code)
3584 sourcerer 311
{
3616 sourcerer 312
	fetch_msg msg;
313
	char buffer[1024];
314
	const char *title;
315
	char key[8];
3584 sourcerer 316
 
3616 sourcerer 317
	/* content is going to return error code */
318
	fetch_set_http_code(ctx->fetchh, code);
3584 sourcerer 319
 
3616 sourcerer 320
	/* content type */
321
	if (fetch_curl_send_header(ctx, "Content-Type: text/html"))
322
		goto fetch_file_process_error_aborted;
3584 sourcerer 323
 
3616 sourcerer 324
	snprintf(key, sizeof key, "HTTP%03d", code);
325
	title = messages_get(key);
3584 sourcerer 326
 
3616 sourcerer 327
	snprintf(buffer, sizeof buffer, "%s"
328
			"

%s

"
329
			"

Error %d while fetching file %s

",
330
			title, title, code, nsurl_access(ctx->url));
3584 sourcerer 331
 
3616 sourcerer 332
	msg.type = FETCH_DATA;
333
	msg.data.header_or_data.buf = (const uint8_t *) buffer;
334
	msg.data.header_or_data.len = strlen(buffer);
335
	if (fetch_curl_send_callback(&msg, ctx))
336
		goto fetch_file_process_error_aborted;
3584 sourcerer 337
 
3616 sourcerer 338
	msg.type = FETCH_FINISHED;
339
	fetch_curl_send_callback(&msg, ctx);
340
 
341
fetch_file_process_error_aborted:
342
	return;
3584 sourcerer 343
}
344
 
345
 
3616 sourcerer 346
static void fetch_curl_process(struct fetch_curl_context *ctx) {
347
 
348
	fetch_msg msg;
349
	const char * buf = "

Hello, file fetcher!

";
350
//	fetch_curl_process_error(ctx, 501);
351
//	return;
3584 sourcerer 352
 
3616 sourcerer 353
/* fetch is going to be successful */
354
	fetch_set_http_code(ctx->fetchh, 200);
3584 sourcerer 355
 
3616 sourcerer 356
	/* Any callback can result in the fetch being aborted.
357
	 * Therefore, we _must_ check for this after _every_ call to
358
	 * fetch_file_send_callback().
359
	 */
3584 sourcerer 360
 
3616 sourcerer 361
	/* content type */
362
	if (fetch_curl_send_header(ctx, "Content-Type: text/html"))
363
		goto fetch_file_process_aborted;
3584 sourcerer 364
 
3616 sourcerer 365
 
366
	/* main data loop */
3584 sourcerer 367
 
3616 sourcerer 368
		msg.type = FETCH_DATA;
369
		msg.data.header_or_data.buf = (const uint8_t *) buf;
370
		msg.data.header_or_data.len = strlen(buf);
371
		fetch_curl_send_callback(&msg, ctx);
372
 
373
 
3584 sourcerer 374
 
3616 sourcerer 375
	if (ctx->aborted == false) {
376
		msg.type = FETCH_FINISHED;
377
		fetch_curl_send_callback(&msg, ctx);
378
	}
3584 sourcerer 379
 
3616 sourcerer 380
fetch_file_process_aborted:
381
return;
3584 sourcerer 382
 
383
}
384
 
3616 sourcerer 385
 
3584 sourcerer 386
/**
3616 sourcerer 387
 * Do some work on current fetches.
3584 sourcerer 388
 *
3616 sourcerer 389
 * Must be called regularly to make progress on fetches.
3584 sourcerer 390
 */
391
 
3616 sourcerer 392
void fetch_curl_poll(lwc_string *scheme_ignored)
3584 sourcerer 393
{
3616 sourcerer 394
	LOG(("curl poll\n"));
395
 
396
	struct fetch_curl_context *c, *next;
3584 sourcerer 397
 
3616 sourcerer 398
	if (ring == NULL) return;
3584 sourcerer 399
 
3616 sourcerer 400
	/* Iterate over ring, processing each pending fetch */
401
	c = ring;
402
	do {
403
		/* Ignore fetches that have been flagged as locked.
404
		 * This allows safe re-entrant calls to this function.
405
		 * Re-entrancy can occur if, as a result of a callback,
406
		 * the interested party causes fetch_poll() to be called
407
		 * again.
408
		 */
409
		if (c->locked == true) {
410
			next = c->r_next;
411
			continue;
412
		}
3584 sourcerer 413
 
3616 sourcerer 414
		/* Only process non-aborted fetches */
415
		if (c->aborted == false) {
416
			/* file fetches can be processed in one go */
417
			fetch_curl_process(c);
418
		}
3584 sourcerer 419
 
3616 sourcerer 420
		/* Compute next fetch item at the last possible moment as
421
		 * processing this item may have added to the ring.
422
		 */
423
		next = c->r_next;
3584 sourcerer 424
 
3616 sourcerer 425
		fetch_remove_from_queues(c->fetchh);
426
		fetch_free(c->fetchh);
427
 
428
		/* Advance to next ring entry, exiting if we've reached
429
		 * the start of the ring or the ring has become empty
430
		 */
431
	} while ( (c = next) != ring && ring != NULL);
432
 
3584 sourcerer 433
}
434