urnHandleReply(void *data, StoreIOBuffer result)
{
    UrnState *urnState = static_cast<UrnState *>(data);
    StoreEntry *e = urnState->entry;
    StoreEntry *urlres_e = urnState->urlres_e;
    char *s = NULL;
    size_t k;
    HttpReply *rep;
    url_entry *urls;
    url_entry *u;
    url_entry *min_u;
    MemBuf *mb = NULL;
    ErrorState *err;
    int i;
    int urlcnt = 0;
    char *buf = urnState->reqbuf;
    StoreIOBuffer tempBuffer;

    debugs(52, 3, "urnHandleReply: Called with size=" << result.length << ".");

    if (EBIT_TEST(urlres_e->flags, ENTRY_ABORTED) || result.flags.error) {
        delete urnState;
        return;
    }

    /* Update reqofs to point to where in the buffer we'd be */
    urnState->reqofs += result.length;

    /* Handle reqofs being bigger than normal */
    if (urnState->reqofs >= URN_REQBUF_SZ) {
        delete urnState;
        return;
    }

    /* If we haven't received the entire object (urn), copy more */
    if (urlres_e->store_status == STORE_PENDING) {
        Must(result.length > 0); // zero length ought to imply STORE_OK
        tempBuffer.offset = urnState->reqofs;
        tempBuffer.length = URN_REQBUF_SZ - urnState->reqofs;
        tempBuffer.data = urnState->reqbuf + urnState->reqofs;
        storeClientCopy(urnState->sc, urlres_e,
                        tempBuffer,
                        urnHandleReply,
                        urnState);
        return;
    }

    /* we know its STORE_OK */
    k = headersEnd(buf, urnState->reqofs);

    if (0 == k) {
        debugs(52, DBG_IMPORTANT, "urnHandleReply: didn't find end-of-headers for " << e->url()  );
        delete urnState;
        return;
    }

    s = buf + k;
    assert(urlres_e->getReply());
    rep = new HttpReply;
    rep->parseCharBuf(buf, k);
    debugs(52, 3, "reply exists, code=" << rep->sline.status() << ".");

    if (rep->sline.status() != Http::scOkay) {
        debugs(52, 3, "urnHandleReply: failed.");
        err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw());
        err->url = xstrdup(e->url());
        errorAppendEntry(e, err);
        delete rep;
        delete urnState;
        return;
    }

    delete rep;

    while (xisspace(*s))
        ++s;

    urls = urnParseReply(s, urnState->request->method);

    if (!urls) {     /* unknown URN error */
        debugs(52, 3, "urnTranslateDone: unknown URN " << e->url());
        err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw());
        err->url = xstrdup(e->url());
        errorAppendEntry(e, err);
        delete urnState;
        return;
    }

    for (i = 0; urls[i].url; ++i)
        ++urlcnt;

    debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");

    min_u = urnFindMinRtt(urls, urnState->request->method, NULL);
    qsort(urls, urlcnt, sizeof(*urls), url_entry_sort);
    e->buffer();
    mb = new MemBuf;
    mb->init();
    mb->appendf( "<TITLE>Select URL for %s</TITLE>\n"
                 "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"
                 "<H2>Select URL for %s</H2>\n"
                 "<TABLE BORDER=\"0\" WIDTH=\"100%%\">\n", e->url(), e->url());

    for (i = 0; i < urlcnt; ++i) {
        u = &urls[i];
        debugs(52, 3, "URL {" << u->url << "}");
        mb->appendf(
            "<TR><TD><A HREF=\"%s\">%s</A></TD>", u->url, u->url);

        if (urls[i].rtt > 0)
            mb->appendf(
                "<TD align=\"right\">%4d <it>ms</it></TD>", u->rtt);
        else
            mb->appendf("<TD align=\"right\">Unknown</TD>");

        mb->appendf("<TD>%s</TD></TR>\n", u->flags.cached ? "    [cached]" : " ");
    }

    mb->appendf(
        "</TABLE>"
        "<HR noshade size=\"1px\">\n"
        "<ADDRESS>\n"
        "Generated by %s@%s\n"
        "</ADDRESS>\n",
        APP_FULLNAME, getMyHostname());
    rep = new HttpReply;
    rep->setHeaders(Http::scFound, NULL, "text/html", mb->contentSize(), 0, squid_curtime);

    if (min_u) {
        rep->header.putStr(Http::HdrType::LOCATION, min_u->url);
    }

    rep->body.setMb(mb);
    /* don't clean or delete mb; rep->body owns it now */
    e->replaceHttpReply(rep);
    e->complete();

    for (i = 0; i < urlcnt; ++i) {
        safe_free(urls[i].url);
        safe_free(urls[i].host);
    }

    safe_free(urls);
    delete urnState;
}