threads / discuss / 3692

Re: Cloning from sites with 404 overridden

Subject: Re: Cloning from sites with 404 overridden

## tl;dr

18 messages between Mar 22, 2006 and Mar 24, 2006.

replies: 17people: 10as markdown or json

linux@horizon.com· Mar 22, 2006, 02:59 UTC · lore

If someone feels ambitious, you can detect this condition automatically by searching for a file that you know won't be there and seeing if you get a 404 response to that.

To avoid punishing good servers, it would be nice to defer the test until reciving the first corrupted object.

I'm not sure what the best "object that's not supposed to be there" is. It could just be a random hash, or would a malformed object file name be better? Any fixed name has a finite chance of being created by someone somewhere, but generating 160-bit random numbers is a PITA on non-freenix platforms.

(As an aside, I suspect this is all caused by Microsoft's "friendly HTML error messages" invention.)

Shawn Pearce· Mar 22, 2006, 03:12 UTC · re: linux@horizon.com · lore

'0' x 40. :-) There's some places already in the GIT source which would have ``issues'' if they got an object with this hash. Not sure if it is actually an entirely impossible hash or just one that is highly improbable.

My own website has this problem and its because I'm using WordPress to handle all URLs on the site; I haven't yet found a way to configure WordPress to return a proper 404 when the URL can't be mapped to something on the server. Note that 404 status codes can in fact return pretty HTML content for the user, and many websites do this and many browsers display that pretty HTML. But a bot can then also recognize the status code and DTRT.

The webservers are just plain broken, mine included. I think the best option is to delay corrupt object reporting to the end of the download process if you get only one corrupt object and that corrupt object was actually attainable from a pack. And in this case its just a minor warning:

	Warning: The server appears to not return proper HTTP status
	codes on missing files.  The files were found in one or
	more packs so the download is OK, but the server administrator
	should really fix their server.  If you know the server
	administrator you might want to prod them to do so.

But that's already been suggested and I thought someone worked up a patch based on that idea? If not I could try to do so since my own damn server has the problem. :-)

linux@horizon.com wrote:
Show 16 quoted lines
> If someone feels ambitious, you can detect this condition automatically
> by searching for a file that you know won't be there and seeing if you
> get a 404 response to that.
> 
> To avoid punishing good servers, it would be nice to defer the test
> until reciving the first corrupted object.
> 
> I'm not sure what the best "object that's not supposed to be there" is.
> It could just be a random hash, or would a malformed object file name
> be better?  Any fixed name has a finite chance of being created by
> someone somewhere, but generating 160-bit random numbers is a PITA on
> non-freenix platforms.
> 
> 
> (As an aside, I suspect this is all caused by Microsoft's "friendly HTML
> error messages" invention.)
-- 
Shawn.
Linus Torvalds· Mar 22, 2006, 04:13 UTC · re: Shawn Pearce · lore
On Tue, 21 Mar 2006, Shawn Pearce wrote:
Show 5 quoted lines
>
> '0' x 40.  :-) There's some places already in the GIT source
> which would have ``issues'' if they got an object with this hash.
> Not sure if it is actually an entirely impossible hash or just one
> that is highly improbable.

The all-zeroes hash is as improbable as any other one, and finding a "collision" (ie a "real object") with that hash is as improbable as any other collision, ie we can (and do) depend on it beign a unique identifier for "does not exist".

			Linus
Marco Costalba· Mar 22, 2006, 06:06 UTC · re: linux@horizon.com · lore
On 21 Mar 2006 21:59:21 -0500, linux@horizon.com <linux@horizon.com> wrote:
> If someone feels ambitious, you can detect this condition automatically
> by searching for a file that you know won't be there and seeing if you
> get a 404 response to that.
>

Perhaps I am proposing a total idiocy, I don't know git-fetch internals, but wouldn't be better to avoid trying to download a non existing object? So to fix the problem at the origin?

I don't know if it is possible to list contents before try to download so to avoid asking for a non existing object.

Marco
Junio C Hamano· Mar 22, 2006, 06:47 UTC · re: Marco Costalba · lore
"Marco Costalba" <mcostalba@gmail.com> writes:
Show 6 quoted lines
> Perhaps I am proposing a total idiocy, I don't know git-fetch
> internals, but wouldn't be better to avoid trying to download a non
> existing object? So to fix the problem at the origin?
>
> I don't know if it is possible to list contents before try to download
> so to avoid asking for a non existing object.

There is no way for the downloader to know if the upstream repository has packed which object. What is happening is that the commit walker asks for loose object first because it does not know. Upon getting a "no such file" (or in the case of misconfigured HTTP server that does not say 404, "corrupt object"), it then checks if the object appears in the pack by downloading the pack index. It can tell what objects are in the packs by looking at the pack index and downloads the pack that contains needed object.

Andreas Ericsson· Mar 22, 2006, 13:36 UTC · re: linux@horizon.com · lore
linux@horizon.com wrote:
Show 8 quoted lines
> If someone feels ambitious, you can detect this condition automatically
> by searching for a file that you know won't be there and seeing if you
> get a 404 response to that.
> 
> To avoid punishing good servers, it would be nice to defer the test
> until reciving the first corrupted object.
> 
> I'm not sure what the best "object that's not supposed to be there" is.
.git/objects/00/hoping-for-a-404-or-webadmin-should-fix

It has the right number of chars so it should fit in wherever a real object name does but is obviously bogus anyways.

> It could just be a random hash, or would a malformed object file name
> be better?

A malformed object name is infinitely better. Otherwise we'd end up with a wild guess that hits home some day, to much surprise and a bug-report I wouldn't want to track. Not to mention the embarrassment when explaining why that object-name was chosen.

> 
> (As an aside, I suspect this is all caused by Microsoft's "friendly HTML
> error messages" invention.)
The body of the 404-page has absolutely nothing to do with it.
-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231
Mark Wooding· Mar 24, 2006, 17:29 UTC · re: Andreas Ericsson · lore
Andreas Ericsson <ae@op5.se> wrote:
>> I'm not sure what the best "object that's not supposed to be there" is.
>
> .git/objects/00/hoping-for-a-404-or-webadmin-should-fix

If .git/objects/00/00000000000000000000000000000000000000 exists, the repository has big problems already.

(Aside: `C-u 38 0' doesn't work because Emacs hears `C-u 380' and waits for a key. `M-: (insert-char ?0 38) RET' does the right thing, but is ugly. Any better suggestions?)

-- [mdw]
Junio C Hamano· Mar 24, 2006, 17:52 UTC · re: Mark Wooding · lore
Mark Wooding <mdw@distorted.org.uk> writes:
> (Aside: `C-u 38 0' doesn't work because Emacs hears `C-u 380' and waits
> for a key.  `M-: (insert-char ?0 38) RET' does the right thing, but is
> ugly.  Any better suggestions?)
C-u 38 C-u 0
Linus Torvalds· Mar 24, 2006, 17:53 UTC · re: Mark Wooding · lore
On Fri, 24 Mar 2006, Mark Wooding wrote:
> 
> (Aside: `C-u 38 0' doesn't work because Emacs hears `C-u 380' and waits
> for a key.  `M-: (insert-char ?0 38) RET' does the right thing, but is
> ugly.  Any better suggestions?)

I don't do GNU emacs, but the way to do it in some other editors that do repeats somewhat similarly is to do the action that starts with a number as a macro, and do that macro 37 more times.

On uemacs: ^X '(' '0' ^X ')' ESC '3' '7' ^X 'E'

(Of course, the easier way is to just do '0' LEFT ^K to put the 0 in the buffer, and than ESC '3' '8' ^Y to yank it 38 times, but the macro trick is generic, even if it's a few more keystrokes).

		Linus "teaching people the one true editor" Torvalds
Morten Welinder· Mar 24, 2006, 18:16 UTC · re: Mark Wooding · lore
> (Aside: `C-u 38 0' doesn't work because Emacs hears `C-u 380' and waits
> for a key.  `M-: (insert-char ?0 38) RET' does the right thing, but is
> ugly.  Any better suggestions?)
There's a million ways to skin that cat.
ESC 38 C-q 60 RET
[Octal 060 == '0']
M.
Andreas Ericsson· Mar 24, 2006, 18:40 UTC · re: Mark Wooding · lore
Mark Wooding wrote:
Show 11 quoted lines
> Andreas Ericsson <ae@op5.se> wrote:
> 
> 
>>>I'm not sure what the best "object that's not supposed to be there" is.
>>
>>.git/objects/00/hoping-for-a-404-or-webadmin-should-fix
> 
> 
> If .git/objects/00/00000000000000000000000000000000000000 exists, the
> repository has big problems already.
> 

Indeed. I'm off sobriety again, it being friday and all, but I'm assuming there are more than 18 zeroes there, yes? The "feature" of the above line is that it will fit in any buffer that already exists, and will match any third argument to send(2) that already exists.

> (Aside: `C-u 38 0' doesn't work because Emacs hears `C-u 380' and waits
> for a key.  `M-: (insert-char ?0 38) RET' does the right thing, but is
> ugly.  Any better suggestions?)
> 

This I happily don't understand at all. I'm also happy ignorant of what it has to do with the issue at hand.

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231
Nick Hengeveld· Mar 22, 2006, 17:22 UTC · re: linux@horizon.com · lore
On Tue, Mar 21, 2006 at 09:59:21PM -0500, linux@horizon.com wrote:
> If someone feels ambitious, you can detect this condition automatically
> by searching for a file that you know won't be there and seeing if you
> get a 404 response to that.

It might be feasible to detect this condition using the Content-Type: header in the server response. So far, all the GIT repositories I've tried return text/plain for loose objects and a special 404 page will likely be text/html.

-- 
For a successful technology, reality must take precedence over public
relations, for nature cannot be fooled.
Nick Hengeveld· Mar 22, 2006, 18:36 UTC · re: Nick Hengeveld · lore
On Wed, Mar 22, 2006 at 09:22:27AM -0800, Nick Hengeveld wrote:
> It might be feasible to detect this condition using the Content-Type:
> header in the server response.  So far, all the GIT repositories I've
> tried return text/plain for loose objects and a special 404 page will
> likely be text/html.
Something like this:
http_fetch: report text/html responses for loose objects

Some HTTP server environments return a 200 status and text/html error document or a redirect to one rather than a 404 status if a loose object does not exist. This patch detects and reports this condition to differentiate between a misconfigured server and an actual corrupt object on the server.

Signed-off-by: Nick Hengeveld <nickh@reactrix.com>
---
 http-fetch.c |   19 ++++++++++++++++++-
 1 files changed, 18 insertions(+), 1 deletions(-)
61069cc348640fef2b8c503b8b8f00f689872cab
diff --git a/http-fetch.c b/http-fetch.c
index dc67218..ee5b585 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -41,6 +41,7 @@ struct object_request
 	CURLcode curl_result;
 	char errorstr[CURL_ERROR_SIZE];
 	long http_code;
+	char *content_type;
 	unsigned char real_sha1[20];
 	SHA_CTX c;
 	z_stream stream;
@@ -258,9 +259,15 @@ static void finish_object_request(struct
 
 static void process_object_response(void *callback_data)
 {
+	char *content_type;
 	struct object_request *obj_req =
 		(struct object_request *)callback_data;
 
+	curl_easy_getinfo(obj_req->slot->curl, CURLINFO_CONTENT_TYPE,
+			  &content_type);
+	if (content_type)
+		obj_req->content_type = strdup(content_type);
+
 	obj_req->curl_result = obj_req->slot->curl_result;
 	obj_req->http_code = obj_req->slot->http_code;
 	obj_req->slot = NULL;
@@ -298,6 +305,8 @@ static void release_object_request(struc
 			entry->next = entry->next->next;
 	}
 
+	if (obj_req->content_type)
+		free(obj_req->content_type);
 	free(obj_req->url);
 	free(obj_req);
 }
@@ -340,6 +349,7 @@ void prefetch(unsigned char *sha1)
 	memcpy(newreq->sha1, sha1, 20);
 	newreq->repo = alt;
 	newreq->url = NULL;
+	newreq->content_type = NULL;
 	newreq->local = -1;
 	newreq->state = WAITING;
 	snprintf(newreq->filename, sizeof(newreq->filename), "%s", filename);
@@ -836,7 +846,14 @@ static int fetch_object(struct alt_base 
 				    obj_req->http_code, hex);
 	} else if (obj_req->zret != Z_STREAM_END) {
 		corrupt_object_found++;
-		ret = error("File %s (%s) corrupt", hex, obj_req->url);
+		if (obj_req->content_type &&
+		    !strcmp(obj_req->content_type, "text/html")) {
+			ret = error("text/html response for file %s (%s)",
+				    sha1_to_hex(obj_req->sha1), obj_req->url);
+		} else {
+			ret = error("File %s (%s) corrupt",
+				    sha1_to_hex(obj_req->sha1), obj_req->url);
+		}
 	} else if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) {
 		ret = error("File %s has bad hash", hex);
 	} else if (obj_req->rename < 0) {
-- 
1.2.4.gb1bc1d-dirty
Junio C Hamano· Mar 22, 2006, 19:05 UTC · re: Nick Hengeveld · lore
Nick Hengeveld <nickh@reactrix.com> writes:
Show 5 quoted lines
> Some HTTP server environments return a 200 status and text/html error
> document or a redirect to one rather than a 404 status if a loose
> object does not exist.  This patch detects and reports this condition
> to differentiate between a misconfigured server and an actual corrupt
> object on the server.
Show 11 quoted lines
> 61069cc348640fef2b8c503b8b8f00f689872cab
> diff --git a/http-fetch.c b/http-fetch.c
> index dc67218..ee5b585 100644
> --- a/http-fetch.c
> +++ b/http-fetch.c
> @@ -41,6 +41,7 @@ struct object_request
>  	CURLcode curl_result;
>...
> +	char *content_type;
>  	unsigned char real_sha1[20];
>...
You probably need only one bit here,...
Show 9 quoted lines
> @@ -258,9 +259,15 @@ static void finish_object_request(struct
>  
>  static void process_object_response(void *callback_data)
>...  
> +	curl_easy_getinfo(obj_req->slot->curl, CURLINFO_CONTENT_TYPE,
> +			  &content_type);
> +	if (content_type)
> +		obj_req->content_type = strdup(content_type);
> +
... and note if that is an HTML document or not.

We do bend backwards to support ISP HTTP servers, but this might be going a bit too far. Also I wonder if ISP runs a really dumb-friendly configured server that defaults to text/html unless the mimemap says otherwise. Loose object files do not have suffixes and I am expecting these servers would give whatever the server default is.

Junio C Hamano· Mar 22, 2006, 19:22 UTC · re: Junio C Hamano · lore
Junio C Hamano <junkio@cox.net> writes:
Show 6 quoted lines
> We do bend backwards to support ISP HTTP servers, but this might
> be going a bit too far.  Also I wonder if ISP runs a really
> dumb-friendly configured server that defaults to text/html
> unless the mimemap says otherwise.  Loose object files do not
> have suffixes and I am expecting these servers would give
> whatever the server default is.

Clarification. Even if a server configured as such existed and sent an otherwise valid loose object with text/html, your code does the right thing.

However the patch would not help when such a server also did a "Sorry, did you mistype the URL?" HTML response, and I was wondering how typical that would be.

Nick Hengeveld· Mar 23, 2006, 18:43 UTC · re: Junio C Hamano · lore
On Wed, Mar 22, 2006 at 11:22:14AM -0800, Junio C Hamano wrote:
> You probably need only one bit here,...
> ... and note if that is an HTML document or not.
/me smacks self...
> However the patch would not help when such a server also did a
> "Sorry, did you mistype the URL?" HTML response, and I was
> wondering how typical that would be.
Seems like there are three cases to worry about:
1) the server returns a 200 status and a text/html response instead of a
   404, and the server's default content type is not text/html
2) the server returns a 200 status and a text/html response instead of a
   404, and the server's default content type is text/html
3) the server returns a corrupt object from the repository

I don't think there's a way to distinguish between #2 and #3, so all we can really do is display as helpful an error message as possible.

We can detect #1 if there has been a previous successful loose object transfer by tracking whether the repo's default content type is text/html. In such a case should http-fetch behave as if the server returned 404? If there have been no successful loose object transfers, we'd have to respond as with #2. This approach could potentially break if requests are load-balanced to servers with different misconfigurations - but I think trying to detect that is bending backwards a little too far.

On a related note, I noticed that http-fetch will continue to try inflating/sha1_updating the response after an inflate error has been detected. It's probably not a huge deal, but we could just error out immediately at that point or at least stop the unnecessary processing.

Something like this? Tested by cloning http://digilander.libero.it/mcostalba/scm/qgit.git

[PATCH] http-fetch: try to detect 404s from misconfigured servers

Some HTTP server environments return a 200 status and text/html error document or a redirect to one rather than a 404 status if a loose object does not exist. This patch tries to detect such a response and treat it as a 404.

Signed-off-by: Nick Hengeveld <nickh@reactrix.com>
---
 http-fetch.c |   24 ++++++++++++++++++++++--
 1 files changed, 22 insertions(+), 2 deletions(-)
ab97429c5b0a4b4466ee0072f75706399e42b675
diff --git a/http-fetch.c b/http-fetch.c
index dc67218..bb75050 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -16,6 +16,7 @@ struct alt_base
 {
 	char *base;
 	int got_indices;
+	int default_html_content_type;
 	struct packed_git *packs;
 	struct alt_base *next;
 };
@@ -41,6 +42,7 @@ struct object_request
 	CURLcode curl_result;
 	char errorstr[CURL_ERROR_SIZE];
 	long http_code;
+	char html_content_type;
 	unsigned char real_sha1[20];
 	SHA_CTX c;
 	z_stream stream;
@@ -249,6 +251,9 @@ static void finish_object_request(struct
 		unlink(obj_req->tmpfile);
 		return;
 	}
+	if (obj_req->repo->default_html_content_type == -1)
+		obj_req->repo->default_html_content_type =
+			obj_req->html_content_type;
 	obj_req->rename =
 		move_temp_to_file(obj_req->tmpfile, obj_req->filename);
 
@@ -258,9 +263,15 @@ static void finish_object_request(struct
 
 static void process_object_response(void *callback_data)
 {
+	char *content_type;
 	struct object_request *obj_req =
 		(struct object_request *)callback_data;
 
+	curl_easy_getinfo(obj_req->slot->curl, CURLINFO_CONTENT_TYPE,
+			  &content_type);
+	if (content_type && !strcmp(content_type, "text/html"))
+		obj_req->html_content_type = 1;
+
 	obj_req->curl_result = obj_req->slot->curl_result;
 	obj_req->http_code = obj_req->slot->http_code;
 	obj_req->slot = NULL;
@@ -340,6 +351,7 @@ void prefetch(unsigned char *sha1)
 	memcpy(newreq->sha1, sha1, 20);
 	newreq->repo = alt;
 	newreq->url = NULL;
+	newreq->html_content_type = 0;
 	newreq->local = -1;
 	newreq->state = WAITING;
 	snprintf(newreq->filename, sizeof(newreq->filename), "%s", filename);
@@ -539,6 +551,7 @@ static void process_alternates_response(
 				newalt->next = NULL;
 				newalt->base = target;
 				newalt->got_indices = 0;
+				newalt->default_html_content_type = -1;
 				newalt->packs = NULL;
 				while (tail->next != NULL)
 					tail = tail->next;
@@ -835,8 +848,14 @@ static int fetch_object(struct alt_base 
 				    obj_req->errorstr, obj_req->curl_result,
 				    obj_req->http_code, hex);
 	} else if (obj_req->zret != Z_STREAM_END) {
-		corrupt_object_found++;
-		ret = error("File %s (%s) corrupt", hex, obj_req->url);
+		if (obj_req->html_content_type &&
+		    !obj_req->repo->default_html_content_type)
+			ret = -1; /* Be silent, looks like a 404 */
+		else {
+			corrupt_object_found++;
+			ret = error("File %s (%s) corrupt",
+				    sha1_to_hex(obj_req->sha1), obj_req->url);
+		}
 	} else if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) {
 		ret = error("File %s has bad hash", hex);
 	} else if (obj_req->rename < 0) {
@@ -985,6 +1004,7 @@ int main(int argc, char **argv)
 	alt = xmalloc(sizeof(*alt));
 	alt->base = url;
 	alt->got_indices = 0;
+	alt->default_html_content_type = -1;
 	alt->packs = NULL;
 	alt->next = NULL;
 
-- 
1.2.4.gb1bc1d-dirty
Junio C Hamano· Mar 23, 2006, 20:45 UTC · re: Nick Hengeveld · lore
Nick Hengeveld <nickh@reactrix.com> writes:
Show 7 quoted lines
> Seems like there are three cases to worry about:
>
> 1) the server returns a 200 status and a text/html response instead of a
>    404, and the server's default content type is not text/html
> 2) the server returns a 200 status and a text/html response instead of a
>    404, and the server's default content type is text/html
> 3) the server returns a corrupt object from the repository
> I don't think there's a way to distinguish between #2 and #3, so all we
> can really do is display as helpful an error message as possible.

The code behaves correctly the same way whether the server says 404 or 200 with human readable "No such object", and this is just for formatting error messages, and to be honest I do not really care at this point. I think the existing error message at the end of transfer we added recently should be sufficient.

> On a related note, I noticed that http-fetch will continue to try
> inflating/sha1_updating the response after an inflate error has been
> detected.  It's probably not a huge deal, but we could just error out
> immediately at that point or at least stop the unnecessary processing.
That would probably be more helpful.
Radoslaw Szkodzinski· Mar 22, 2006, 21:24 UTC · re: Junio C Hamano · lore
On Wednesday 22 March 2006 20:05, Junio C Hamano wrote yet:
>
> .. and note if that is an HTML document or not.
>

Better yet, see first if the object is corrupt. If it is and its Content-Type is text/html, error out.

Show 6 quoted lines
> We do bend backwards to support ISP HTTP servers, but this might
> be going a bit too far.  Also I wonder if ISP runs a really
> dumb-friendly configured server that defaults to text/html
> unless the mimemap says otherwise.  Loose object files do not
> have suffixes and I am expecting these servers would give
> whatever the server default is.

That server would break a *lot* of file types. That admin should be hanged, shot, then burned.

I think of only one reason for doing that: to restrict file types posted on the server to, say, zip and html.

-- 
GPG Key id:  0xD1F10BA2
Fingerprint: 96E2 304A B9C4 949A 10A0  9105 9543 0453 D1F1 0BA2

AstralStorm

← back to recent threads