Hi,

this is my second parq patch. It should support both active queueing and
PARQ 1.0 now at the client side. Server side support is not implemented
yet.
I still don't know how to include a new file into a patch (I tried cvs
diff -uRN) so parq.[ch] are attached as a seperate file.

Please review (And test if you like).

With regards,
Jeroen Asselman


Index: src/downloads.c
===================================================================
RCS file: /cvsroot/gtk-gnutella/gtk-gnutella-current/src/downloads.c,v
retrieving revision 1.194
diff -u -r1.194 downloads.c
--- src/downloads.c	1 Feb 2003 23:16:54 -0000	1.194
+++ src/downloads.c	2 Feb 2003 20:53:01 -0000
@@ -30,7 +30,6 @@
 #include "sockets.h"
 #include "downloads.h"
 #include "hosts.h"
-#include "header.h"
 #include "routing.h"
 #include "routing.h"
 #include "gmsg.h"
@@ -47,6 +46,8 @@
 #include "settings.h"
 #include "nodes.h"
 
+#include "parq.h"
+
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -313,6 +314,7 @@
 		l = l->next;
 
 		switch (d->status) {
+		case GTA_DL_ACTIVE_QUEUED:
 		case GTA_DL_RECEIVING:
 		case GTA_DL_HEADERS:
 		case GTA_DL_PUSH_SENT:
@@ -327,6 +329,9 @@
 			}
 
 			switch (d->status) {
+			case GTA_DL_ACTIVE_QUEUED:
+ 				t = d->queue_status.retry_delay;
+ 				break;
 			case GTA_DL_PUSH_SENT:
 			case GTA_DL_FALLBACK:
 				t = download_push_sent_timeout;
@@ -342,7 +347,15 @@
 			}
 
 			if (now - d->last_update > t) {
-				if (d->status == GTA_DL_CONNECTING)
+				/*
+				 * When the 'timeout' has expired, first check wether the
+				 * download was activly queued. If so, tell parq to retry the
+				 * download in which case the HTTP connection wasn't closed
+				 *   JA 31 jan 2003
+				 */
+				if (d->status == GTA_DL_ACTIVE_QUEUED)
+					parq_download_retry_active_queued(d);
+				else if (d->status == GTA_DL_CONNECTING)
 					download_fallback_to_push(d, TRUE, FALSE);
 				else if (d->status == GTA_DL_HEADERS)
 					download_incomplete_header(d);
@@ -1902,7 +1915,7 @@
  * Returns TRUE if we may continue with the download, FALSE if it has been
  * stopped due to a problem.
  */
-static gboolean download_start_prepare_running(struct download *d)
+gboolean download_start_prepare_running(struct download *d)
 {
 	struct dl_file_info *fi = d->file_info;
 
@@ -1986,7 +1999,7 @@
  * Returns TRUE if we may continue with the download, FALSE if it has been
  * stopped due to a problem.
  */
-static gboolean download_start_prepare(struct download *d)
+gboolean download_start_prepare(struct download *d)
 {
     g_assert(d != NULL);
 	g_assert(d->list_idx != DL_LIST_RUNNING);	/* Not already running */
@@ -3784,7 +3797,7 @@
  *
  * Extract Retry-After delay from header, returning 0 if none.
  */
-static guint extract_retry_after(header_t *header)
+guint extract_retry_after(header_t *header)
 {
 	gchar *buf;
 	guint delay = 0;
@@ -4042,7 +4055,7 @@
 {
 	gchar *buf;
 	gchar *available = "X-Available-Ranges";
-
+	
 	if (d->ranges != NULL) {
 		http_range_free(d->ranges);
 		d->ranges = NULL;
@@ -4051,6 +4064,9 @@
 	if (!d->file_info->use_swarming)
 		return;
 
+	g_assert(header != NULL);
+	g_assert(header->headers != NULL);
+	
 	buf = header_get(header, available);
 
 	if (buf == NULL)
@@ -4173,7 +4189,7 @@
 	struct dl_file_info *fi;
 	gchar short_read[80];
 	guint delay;
-
+		
 	/*
 	 * If `ok' is FALSE, we might not even have fully read the status line,
 	 * in which case `s->getline' will be null.
@@ -4236,7 +4252,7 @@
 
 	ack_code = http_status_parse(status, "HTTP",
 		&ack_message, &http_major, &http_minor);
-
+	
 	if (!download_check_status(d, s->getline, ack_code))
 		return;
 
@@ -4284,20 +4300,58 @@
 			"[short %d line%s header] ", count, count == 1 ? "" : "s");
 	}
 
-	/*
-	 * If we made a /uri-res/N2R? request, yet if the download still
-	 * has the old index/name indication, convert it to a /uri-res/.
-	 */
-
+	
 	if (ack_code == 503 || (ack_code >= 200 && ack_code <= 299)) {
+
+		/*
+		 * If we made a /uri-res/N2R? request, yet if the download still
+		 * has the old index/name indication, convert it to a /uri-res/.
+	 	 */
 		if (d->record_index != URN_INDEX && (d->flags & DL_F_URIRES))
 			if (!download_convert_to_urires(d))
 				return;
+		
+		/*
+		 * The download could be remotely queued. Check this now before
+		 * continueing at all.
+		 *   JA, 31 jan 2003
+		 */			
+		if (ack_code == 503) {
+			
+			/* Check for queued status */
+			if (parq_download_supports_parq(header)) {		
+
+				parq_download_parse_queue_status(d, header);
+			
+				/* If we are queued there is nothing anymore we can do for now*/
+				if (parq_download_is_active_queued(d)) {
+					
+					/* Make sure we're waiting for the right file, 
+					   collect alt-locs */
+					if (check_content_urn(d, header)) {
+
+						/* Update mesh */
+						if (!d->always_push && d->sha1)
+							dmesh_add(d->sha1, ip, port, d->record_index,
+								d->file_name, 0);
+			
+						return;
+						
+					} /* Check content urn failed */
+
+					return;
+					
+				} /* Download not active queued, continue as normal */
+				d->status = GTA_DL_HEADERS;
+			}
+		} /* ack_code was not 503 */
 	}
 
 	update_available_ranges(d, header);		/* Updates `d->ranges' */
 	delay = extract_retry_after(header);
 
+
+		
 	/*
 	 * Partial File Sharing Protocol (PFSP) -- client-side
 	 *
@@ -4411,11 +4465,13 @@
 					gui_update_download(d, TRUE);
 				}
 			} else {
+				/* Host might support queueing. If so, retreive queue status */
 				/* Server has nothing for us yet, give it time */
 				download_queue_delay(d,
 					MAX(delay, download_retry_refused_delay),
 					"Partial file on server, waiting");
 			}
+			
 			return;
 		default:
 			break;
@@ -4459,11 +4515,10 @@
 			if (download_retry_no_urires(d, delay, ack_code))
 				return;
 			break;
-		case 503:				/* Busy */
+		case 503:				/* Busy */			
 			/* Make sure we're waiting for the right file, collect alt-locs */
 			if (!check_content_urn(d, header))
 				return;
-
 			/*
 			 * If we made a follow-up request, mark host as not reliable.
 			 *
@@ -4509,10 +4564,11 @@
 
 	/*
 	 * If an URN is present, validate that we can continue this download.
-	 */
+ 	 */
+ 
+ 	if (!check_content_urn(d, header))
+ 		return;
 
-	if (!check_content_urn(d, header))
-		return;
 
 	/*
 	 * If they configured us to require a server name, and we have none
@@ -4920,7 +4976,11 @@
 		"Host: %s\r\n"
 		"User-Agent: %s\r\n",
 		ip_port_to_gchar(download_ip(d), download_port(d)), version_string);
-
+	
+	/*
+	 * Add X-Queue / X-Queued information into the header
+	 */
+	parq_download_add_header(dl_tmp, &rw, d);
 	/*
 	 * If server is known to NOT support keepalives, then request only
 	 * a range starting from d->skip.  Likewise if we don't know whether
Index: src/downloads.h
===================================================================
RCS file: /cvsroot/gtk-gnutella/gtk-gnutella-current/src/downloads.h,v
retrieving revision 1.59
diff -u -r1.59 downloads.h
--- src/downloads.h	27 Jan 2003 22:20:37 -0000	1.59
+++ src/downloads.h	2 Feb 2003 20:53:01 -0000
@@ -28,6 +28,9 @@
 
 #include "bsched.h"
 #include "fileinfo.h"
+#include "header.h"
+
+#define PARQ_MAX_ID_LENGTH 40
 
 /*
  * We keep a list of all the downloads queued per GUID+IP:port (host).  Indeed
@@ -66,6 +69,19 @@
 	guint32 attrs;
 };
 
+/* 
+ * Download dependent queuing status.
+ */
+struct dl_queued {	
+	guint16 position;			/* Current position in the queue */
+	guint16 length;				/* Current queue length */
+	guint16 ETA;				/* Estimated time till upload slot retreived */
+	guint16 lifetime;			/* Maximum interval before loosing queue 
+	                          			position */
+	guint16 retry_delay;			/* Interval between new attempt */
+	gchar ID[PARQ_MAX_ID_LENGTH];	/* PARQ Queue ID	*/
+};
+
 struct download {
 	gchar error_str[256];	/* Used to sprintf() error strings with vars */
 	guint32 status;			/* Current status of the download */
@@ -113,6 +129,8 @@
 	gboolean visible;		/* The download is visible in the GUI */
 	gboolean push;			/* Currently in push mode */
 	gboolean always_push;	/* Always use the push method for this download */
+	
+	struct dl_queued queue_status;	/* Queuing status */
 };
 
 /*
@@ -139,6 +157,10 @@
 #define GTA_DL_DONE				18	/* All done! */
 #define GTA_DL_SINKING			19	/* Sinking HTML reply */
 
+// JA, 31 jan 2003 GTA_DL_ACTIVE_QUEUED
+#define GTA_DL_ACTIVE_QUEUED	20	/* Actively queued */
+#define GTA_DL_PASSIVE_QUEUED	21	/* Passively queued */
+
 /*
  * Download flags.
  */
@@ -209,11 +231,13 @@
 #define DOWNLOAD_IS_WAITING(d)			\
 	(  (d)->status == GTA_DL_TIMEOUT_WAIT)
 
+// JA, 31 jan 2003 GTA_DL_ACTIVE_QUEUED
 #define DOWNLOAD_IS_ESTABLISHING(d)		\
 	(  (d)->status == GTA_DL_CONNECTING \
 	|| (d)->status == GTA_DL_PUSH_SENT	\
 	|| (d)->status == GTA_DL_FALLBACK	\
 	|| (d)->status == GTA_DL_REQ_SENT	\
+	|| (d)->status == GTA_DL_ACTIVE_QUEUED \
 	|| (d)->status == GTA_DL_HEADERS	)
 
 #define DOWNLOAD_IS_EXPECTING_GIV(d)	\
@@ -256,6 +280,7 @@
 void download_abort(struct download *);
 void download_resume(struct download *);
 void download_start(struct download *, gboolean);
+gboolean download_start_prepare_running(struct download *d);
 void download_requeue(struct download *);
 void download_send_request(struct download *);
 void download_retry(struct download *);
@@ -277,6 +302,10 @@
 void download_move_progress(struct download *d, guint32 copied);
 void download_move_done(struct download *d, time_t elapsed);
 void download_move_error(struct download *d);
+
+gboolean download_start_prepare(struct download *d);
+
+guint extract_retry_after(header_t *header);
 
 #endif /* _downloads_h_ */
 
Index: src/downloads_gui.c
===================================================================
RCS file: /cvsroot/gtk-gnutella/gtk-gnutella-current/src/downloads_gui.c,v
retrieving revision 1.17
diff -u -r1.17 downloads_gui.c
--- src/downloads_gui.c	1 Feb 2003 23:16:54 -0000	1.17
+++ src/downloads_gui.c	2 Feb 2003 20:53:04 -0000
@@ -28,6 +28,7 @@
 #include "downloads.h" // FIXME: remove this dependency
 #include "statusbar_gui.h"
 #include "downloads_cb.h"
+#include "parq.h"
 
 RCSID("$Id: downloads_gui.c,v 1.17 2003/02/01 23:16:54 rmanfredi Exp $");
 
@@ -83,6 +84,30 @@
 	fi = d->file_info;
 
 	switch (d->status) {
+	case GTA_DL_ACTIVE_QUEUED:	// JA, 31 jan 2003 Active queueing
+		{
+			gint when = d->timeout_delay - (now - d->last_update);
+			rw = gm_snprintf(tmpstr, sizeof(tmpstr),
+				"Retry in %ds ", MAX(0, when));
+
+			if (d->queue_status.position > 0) {
+				rw += gm_snprintf(&tmpstr[rw], sizeof(tmpstr)-rw,
+					"Remotely queued at position %d ",	d->queue_status.position);
+				
+				if (d->queue_status.length > 0) {
+					rw += gm_snprintf(&tmpstr[rw], sizeof(tmpstr)-rw,
+						"out of %d ",	d->queue_status.length);
+				}
+
+				if (d->queue_status.ETA > 0) {
+					rw += gm_snprintf(&tmpstr[rw], sizeof(tmpstr)-rw,
+						"ETA: %ds ",	d->queue_status.ETA  - (now - d->last_update));
+				}
+
+				}
+		a = tmpstr;
+		}
+		break;
 	case GTA_DL_QUEUED:
 		a = (gchar *) ((d->remove_msg) ? d->remove_msg : "");
 		break;
/*
 * Copyright (c) 2003 Jeroen Asselman
 *
 *----------------------------------------------------------------------
 * This file is part of gtk-gnutella.
 *
 *  gtk-gnutella is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  gtk-gnutella is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with gtk-gnutella; if not, write to the Free Software
 *  Foundation, Inc.:
 *      59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *----------------------------------------------------------------------
 */
 
#include "parq.h"
#include "ioheader.h"
#include "sockets.h"
#include "gnutella.h"

RCSID("$Id$");

/*
 * get_header_version
 * 
 * Extract the version from a given header. EG:
 * X-Queue: 1.0
 * major=1 minor=0
 */
gboolean get_header_version(gchar const * const header, 
								gint *major, gint *minor)
{
	return sscanf(header, ": %d.%d", major, minor) != 0;
}
/* 
 * get_header_value
 *
 * retreives a value from a header line. If possible the length (in gchars)
 * is returned of that value.
 */
gchar*	get_header_value(gchar const * const s, gchar const * const attribute, 
							gint *length)
{
	gchar* lowercase_header;
	gchar* end;
	
	g_assert(s != NULL);
	g_assert(attribute != NULL);
	
	lowercase_header = strcasestr(s, attribute);

	if (lowercase_header == NULL)
		return NULL;

	lowercase_header = strchr(lowercase_header, '=');
	if (lowercase_header == NULL)
		return NULL;

	lowercase_header += sizeof(gchar);
	
	if (length != NULL) {
		*length = 0;
		end = strchr(lowercase_header, ';');
		
		if (end == NULL)
			strchr(lowercase_header, ';');
		
		if (end == NULL) {
			/* 
			 * We still couldn't find a delimiter. If there are no other
			 * values after the current one, 'the end' is just the end off
			 * this value
			 */
			if (strchr(lowercase_header, '=') == NULL) {
				*length = strlen(lowercase_header);
			}
		} else { 
			*length = lowercase_header - end;
		}
	}

	/* Could be NULL */
	return lowercase_header;
}


/*
 * parq_download_retry_active_queued
 *
 * Active queued means we didn't close the http connection on a HTTP 503 busy
 * when the server supports queueing. So prepare the download structure
 * for a 'valid' segment. And re-request the segment.
 */
void parq_download_retry_active_queued(struct download *d)
{
	g_assert(d != NULL);
	g_assert(d->socket != NULL);
	g_assert(d->status == GTA_DL_ACTIVE_QUEUED);
	g_assert(parq_download_is_active_queued(d));
	
	if (download_start_prepare_running(d)) {
		struct gnutella_socket *s = d->socket;
		d->keep_alive = TRUE;			/* was reset in start_prepare_running */
		
 		/* Will be re initialised in download_send_request */
		io_free(d->io_opaque);
		d->io_opaque = NULL;
		getline_free(s->getline);		/* No longer need this */
		s->getline = NULL;

		/* Resend request for download */
		download_send_request(d);
	}
}
	
/*
 * parq_download_supports_parq 
 *
 * Wether the server supports queueing or not.
 */
gboolean parq_download_supports_parq(header_t *header)
{
	g_assert(header != NULL);	
	
	return (NULL != header_get(header, "X-Queue") || 
			NULL != header_get(header, "X-Queued"));
}

/*
 * parq_download_parse_queue_status
 *
 * Retreive and parse queueing information.
 */
void parq_download_parse_queue_status(struct download *d, header_t *header)
{	
	gchar *buf;
	gchar *value;
	gint major, minor;
	gint header_value_length;

	g_assert(d != NULL);
	g_assert(header != NULL);

	buf = header_get(header, "X-Queue");
	
	g_assert(buf != NULL);

	if (!get_header_version(buf, &major, &minor)) {
		/*
		 * Could not retreive queueing version. It could be 0.1 but there is no
		 * way to tell for certain
		 */
		major = 0;
		minor = 1;
	}
	
	if (major == 1) {
		/* PARQ */
		buf = header_get(header, "X-Queued");
	}
	
	switch (major) {
		case 1:
			/* PARQ */
			value = get_header_value(buf, "lifetime", NULL);
			d->queue_status.lifetime = value == NULL ? 0 : strtoul(value, NULL, 0);
	
			d->queue_status.retry_delay = extract_retry_after(header);
		case 0:
			/* Active queueing */		
			value = get_header_value(buf, "pollMin", NULL);
			d->queue_status.retry_delay  = value == NULL ? 0 : strtoul(value, NULL, 0);
		
			value = get_header_value(buf, "pollmax", NULL);
			d->queue_status.lifetime  = value == NULL ? 0 : strtoul(value, NULL, 0);
	}
	

	value = get_header_value(buf, "position", NULL);
	d->queue_status.position = value == NULL ? 0 : strtoul(value, NULL, 0);
	
	value = get_header_value(buf, "length", NULL);
	d->queue_status.length   = value == NULL ? 0 : strtoul(value, NULL, 0);
				
	value = get_header_value(buf, "ETA", NULL);
	d->queue_status.ETA  = value == NULL ? 0 : strtoul(value, NULL, 0);

	value = get_header_value(buf, "ID", &header_value_length);
	
	if (header_value_length < PARQ_MAX_ID_LENGTH) {
		strcpy(d->queue_status.ID, value);
	}

	if (dbg) {
		printf("Queue version: %d.%d, position %d out of %d, retry in %ds\n\r",
			major, minor, d->queue_status.position, d->queue_status.length,
			d->queue_status.retry_delay);
	}
	
	
	/* FIXME: This isn't 100% correct yet for PARQ, it is for active queueing */
	if (parq_download_is_active_queued(d)) {
		
		/*
		 * Don't keep a chunk busy if we are queued, perhaps another servent
		 * can complete it for us.
		 */
		file_info_clear_download(d, TRUE);
		
		
		d->status = GTA_DL_ACTIVE_QUEUED;
	}
	
	d->timeout_delay = d->queue_status.retry_delay;
}

/*
 * parq_download_is_active_queued
 *
 * Wether the download is queued remotely or not.
 */
gboolean parq_download_is_active_queued(struct download *d)
{
	g_assert(d != NULL);

	return d->queue_status.position > 0 && d->keep_alive;
}

/*
 * parq_download_add_header
 *
 * Adds an:
 * X-Queue: 1.0
 * X-Queued: position=x; ID=xxxxx
 * to the HTTP GET request
 */
void parq_download_add_header(gchar *dl_tmp, gint *rw, struct download *d)
{
	*rw += gm_snprintf(&(dl_tmp[*rw]), sizeof(dl_tmp)-*rw,
		"X-Queue: %d.%d\r\n"
		"X-Queued: position=%d; ID=%s\r\n",
		PARQ_VERSION_MAJOR, PARQ_VERSION_MINOR,
		d->queue_status.position, d->queue_status.ID);

}
/*
 * Copyright (c) 2003 Jeroen Asselman
 *
 *----------------------------------------------------------------------
 * This file is part of gtk-gnutella.
 *
 *  gtk-gnutella is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  gtk-gnutella is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with gtk-gnutella; if not, write to the Free Software
 *  Foundation, Inc.:
 *      59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *----------------------------------------------------------------------
 */
 
#ifndef _PARQ_H_
#define _PARQ_H_

#include "header.h"
#include "downloads.h"

/*
 * PARQ Version information
 */
 
#define PARQ_VERSION_MAJOR	1
#define	PARQ_VERSION_MINOR	0

void parq_download_retry_active_queued(struct download *d);
gboolean parq_download_supports_parq(header_t *header);
void parq_download_parse_queue_status(struct download *d, header_t *header);
gboolean parq_download_is_active_queued(struct download *d);
void parq_download_add_header(gchar *dl_tmp, gint *rw, struct download *d);
	
#endif /* _PARQ_H_ */

Reply via email to