Esempio n. 1
0
int SLNFilterWriteURIs(SLNFilterRef const filter, SLNSessionRef const session, SLNFilterPosition *const pos, bool const meta, uint64_t const max, bool const wait, SLNFilterWriteCB const writecb, void *ctx) {
	uint64_t remaining = max;
	for(;;) {
		ssize_t const count = SLNFilterWriteURIBatch(filter, session, pos, meta, remaining, writecb, ctx);
		if(count < 0) return count;
		remaining -= count;
		if(!remaining) return 0;
		if(!count) break;
	}

	if(!wait || pos->dir < 0) return 0;

	SLNRepoRef const repo = SLNSessionGetRepo(session);
	for(;;) {
		uint64_t const timeout = uv_now(async_loop)+(1000 * 30);
		int rc = SLNRepoSubmissionWait(repo, pos->sortID, timeout);
		if(UV_ETIMEDOUT == rc) {
			uv_buf_t const parts[] = { uv_buf_init((char *)STR_LEN("\r\n")) };
			rc = writecb(ctx, parts, numberof(parts));
			if(rc < 0) break;
			continue;
		}
		assert(rc >= 0); // TODO: Handle cancellation?

		for(;;) {
			ssize_t const count = SLNFilterWriteURIBatch(filter, session, pos, meta, remaining, writecb, ctx);
			if(count < 0) return count;
			remaining -= count;
			if(!remaining) return 0;
		}
	}

	return 0;
}
Esempio n. 2
0
int SLNFilterWriteURIs(SLNFilterRef const filter, SLNSessionRef const session, SLNFilterPosition *const pos, bool const meta, uint64_t const max, bool const wait, SLNFilterWriteCB const writecb, SLNFilterFlushCB const flushcb, void *ctx) {
	uint64_t remaining = max;
	for(;;) {
		ssize_t const count = SLNFilterWriteURIBatch(filter, session, pos, meta, remaining, writecb, ctx);
		if(count < 0) return count;
		remaining -= count;
		if(!remaining) return 0;
		if(!count) break;
	}

	if(!wait || pos->dir < 0) return 0;

	SLNRepoRef const repo = SLNSessionGetRepo(session);
	for(;;) {
		int rc = flushcb ? flushcb(ctx) : 0;
		if(rc < 0) return rc;

		uint64_t latest = pos->sortID;
		uint64_t const timeout = uv_now(async_loop)+(1000 * 30);
		rc = SLNRepoSubmissionWait(repo, &latest, timeout);
		if(UV_ETIMEDOUT == rc) {
			uv_buf_t const parts[] = { uv_buf_init((char *)STR_LEN("\r\n")) };
			rc = writecb(ctx, parts, numberof(parts));
			if(rc < 0) break;
			continue;
		}
		assert(rc >= 0); // TODO: Handle cancellation?

		for(;;) {
			ssize_t const count = SLNFilterWriteURIBatch(filter, session, pos, meta, remaining, writecb, ctx);
			if(count < 0) return count;
			remaining -= count;
			if(!remaining) return 0;
			if(count < BATCH_SIZE) break;
		}

		// This is how far we scanned, even if we didn't find anything.
		if(pos->sortID < latest) {
			pos->sortID = latest;
			pos->fileID = 0;
		}
	}

	return 0;
}
static void SLNFilterResultsWrite(SLNSessionRef const session, SLNFilterRef const filter, SLNFilterOpts *const opts, HTTPConnectionRef const conn) {
	// TODO: Accept count and use it for the total number of results.
	opts->count = 0;

	// We're sending a series of batches, so reversing one batch
	// doesn't make sense.
	opts->outdir = opts->dir;

	static strarg_t const fields[] = { "wait" };
	str_t *values[numberof(fields)] = {};
	QSValuesParse(qs, values, fields, numberof(fields));
	bool const wait = parse_wait(values[0]);
	QSValuesCleanup(values, numberof(values));

	// I'm aware that we're abusing HTTP for sending real-time push data.
	// I'd also like to support WebSocket at some point, but this is simpler
	// and frankly probably more widely supported.
	// Note that the protocol doesn't really break even if this data is
	// cached. It DOES break if a proxy tries to buffer the whole response
	// before passing it back to the client. I'd be curious to know whether
	// such proxies still exist in 2015.
	HTTPConnectionWriteResponse(conn, 200, "OK");
	HTTPConnectionWriteHeader(conn, "Transfer-Encoding", "chunked");
	HTTPConnectionWriteHeader(conn,
		"Content-Type", "text/uri-list; charset=utf-8");
	HTTPConnectionWriteHeader(conn, "Cache-Control", "no-store");
	HTTPConnectionWriteHeader(conn, "Vary", "*");
	HTTPConnectionBeginBody(conn);
	int rc;

	for(;;) {
		rc = sendURIBatch(session, filter, opts, conn);
		if(DB_NOTFOUND == rc) break;
		if(DB_SUCCESS == rc) continue;
		fprintf(stderr, "Query error: %s\n", db_strerror(rc));
		goto cleanup;
	}

	if(!wait || opts->dir < 0) goto cleanup;

	SLNRepoRef const repo = SLNSessionGetRepo(session);
	for(;;) {
		uint64_t const timeout = uv_now(async_loop)+(1000 * 30);
		rc = SLNRepoSubmissionWait(repo, opts->sortID, timeout);
		if(UV_ETIMEDOUT == rc) {
			uv_buf_t const parts[] = { uv_buf_init((char *)STR_LEN("\r\n")) };
			rc = HTTPConnectionWriteChunkv(conn, parts, numberof(parts));
			if(rc < 0) break;
			continue;
		}
		assert(rc >= 0); // TODO: Handle cancellation?

		for(;;) {
			rc = sendURIBatch(session, filter, opts, conn);
			if(DB_NOTFOUND == rc) break;
			if(DB_SUCCESS == rc) continue;
			fprintf(stderr, "Query error: %s\n", db_strerror(rc));
			goto cleanup;
		}
	}

cleanup:
	HTTPConnectionWriteChunkEnd(conn);
	HTTPConnectionEnd(conn);
	SLNFilterOptsCleanup(opts);
}