/* Find the cost of a filter stack ** ------------------------------- ** ** Must return the cost of the same stack which StreamStack would set up. ** ** On entry, ** length The size of the data to be converted */ PUBLIC double HTStackValue (HTList * theseConversions, HTFormat rep_in, HTFormat rep_out, double initial_value, long int length) { int which_list; HTList* conversion[2]; HTTRACE(CORE_TRACE, "StackValue.. Evaluating stream stack for %s worth %.3f to %s\n" _ HTAtom_name(rep_in) _ initial_value _ HTAtom_name(rep_out)); if (rep_out == WWW_SOURCE || rep_out == rep_in) return 0.0; conversion[0] = theseConversions; conversion[1] = HTConversions; for(which_list = 0; which_list<2; which_list++) if (conversion[which_list]) { HTList * cur = conversion[which_list]; HTPresentation * pres; while ((pres = (HTPresentation*)HTList_nextObject(cur))) { if (pres->rep == rep_in && (pres->rep_out == rep_out || HTMIMEMatch(pres->rep_out, rep_out))) { double value = initial_value * pres->quality; if (HTMaxSecs != 0.0) value = value - (length*pres->secs_per_byte + pres->secs) /HTMaxSecs; return value; } } } return NO_VALUE_FOUND; /* Really bad */ }
/* Create a new coder and insert it into stream chain ** -------------------------------------------------- ** Creating the content decoding stack is not based on quality factors as ** we don't have the freedom as with content types. Specify whether you ** you want encoding or decoding using the BOOL "encode" flag. */ PUBLIC HTStream * HTContentCodingStack (HTEncoding encoding, HTStream * target, HTRequest * request, void * param, BOOL encode) { HTList * coders[2]; HTStream * top = target; HTCoding * pres = NULL; HTCoding * best_match = NULL; double best_quality = -1e30; /* Pretty bad! */ int cnt; if (!encoding || !request) { HTTRACE(CORE_TRACE, "Codings... Nothing applied...\n"); return target ? target : HTErrorStream(); } coders[0] = HTRequest_encoding(request); coders[1] = HTContentCoders; HTTRACE(CORE_TRACE, "C-E......... Looking for `%s\'\n" _ HTAtom_name(encoding)); for (cnt=0; cnt < 2; cnt++) { HTList * cur = coders[cnt]; while ((pres = (HTCoding *) HTList_nextObject(cur))) { if ((pres->encoding == encoding || HTMIMEMatch(pres->encoding, encoding)) && pres->quality > best_quality) { best_match = pres; best_quality = pres->quality; } } } if (best_match) { HTTRACE(CORE_TRACE, "C-E......... Found `%s\'\n" _ HTAtom_name(best_match->encoding)); if (encode) { if (best_match->encoder) top = (*best_match->encoder)(request, param, encoding, top); } else { if (best_match->decoder) top = (*best_match->decoder)(request, param, encoding, top); } } else if (!HTFormat_isUnityContent(encoding)) { /* ** If this is not a unity coding and we didn't find any coders ** that could handle it then put in a local file save stream ** instead of the stream that we got. */ if (encode) { HTTRACE(CORE_TRACE, "C-E......... NOT FOUND - can't encode stream!\n"); } else { HTTRACE(CORE_TRACE, "C-E......... NOT FOUND - error!\n"); top = HTBlackHole(); } } return top; }
PRIVATE double type_value (HTAtom * content_type, HTList * accepted) { if (!content_type) return (1.0); if (accepted) { HTList * cur = accepted; HTPresentation * pres; HTPresentation * wild = NULL; while ((pres = (HTPresentation *) HTList_nextObject(cur))) { if (pres->rep == content_type) return pres->quality; else if (HTMIMEMatch(pres->rep, content_type)) wild = pres; } if (wild) return wild->quality; else return (0.0); /* Nothing matched */ } return (1.0); /* We accept all types */ }
PRIVATE double encoding_value (HTAtom * encoding, HTList * accepted) { if (!encoding) return (1.0); if (accepted) { HTList * cur = accepted; HTAcceptNode * node; HTAcceptNode * wild = NULL; const char * e = HTAtom_name(encoding); if (!strcmp(e, "7bit") || !strcmp(e, "8bit") || !strcmp(e, "binary")) return (1.0); while ((node = (HTAcceptNode*)HTList_nextObject(cur))) { if (node->atom == encoding) return node->quality; else if (HTMIMEMatch(node->atom, encoding)) wild = node; } if (wild) return wild->quality; else return (0.0); /* Nothing matched */ } return (1.0); /* We accept all encodings */ }
/* HTTPMakeRequest ** --------------- ** Makes a HTTP/1.0-1.1 request header. */ PRIVATE int HTTPMakeRequest (HTStream * me, HTRequest * request) { HTMethod method = HTRequest_method(request); HTRqHd request_mask = HTRequest_rqHd(request); HTParentAnchor * anchor = HTRequest_anchor(request); char * etag = HTAnchor_etag(anchor); char crlf[3]; char qstr[10]; *crlf = CR; *(crlf+1) = LF; *(crlf+2) = '\0'; /* Generate the HTTP/1.x RequestLine */ if (me->state == 0) { if (method != METHOD_INVALID) { PUTS(HTMethod_name(method)); PUTC(' '); } else PUTS("GET "); me->state++; } /* ** Generate the Request URI. If we are using full request URI then it's ** easy. Otherwise we must filter out the path part of the URI. ** In case it's a OPTIONS request then if there is no pathinfo then use ** a * instead. If we use a method different from GET or HEAD then use ** the content-location if available. */ if (me->state == 1) { char * abs_location = NULL; char * addr = HTAnchor_physical(anchor); char * location; /* JK: If the application specified a content-location (which is stored in the request in default put-name!), we use it instead of the URL that's being saved to. This is like having a user defined Content-Location */ location = HTRequest_defaultPutName (request); if (location) { if (HTURL_isAbsolute (location)) { char * relative; relative = HTRelative (location, location); abs_location = HTParse (relative + 2, addr, PARSE_ALL); HT_FREE (relative); } else abs_location = HTParse (location, addr, PARSE_ALL); addr = abs_location; } #if 0 /* ** We don't use the content-location any more as it is superseeded ** by etags and the combination of the two might do more harm than ** good (The etag is not guaranteed to be unique over multiple URIs) */ /* ** If we are using a method different from HEAD and GET then use ** the Content-Location if available, else the Request-URI. */ if (!HTMethod_isSafe(method)) { char * location = HTAnchor_location(anchor); if (location) { if (HTURL_isAbsolute(location)) addr = location; else { /* ** We have a content-location but it is relative and ** must expand it either to the content-base or to ** the Request-URI itself. */ char * base = HTAnchor_base(anchor); abs_location = HTParse(location, base, PARSE_ALL); addr = abs_location; } } } #endif /* ** If we are using a proxy or newer versions of HTTP then we can ** send the full URL. Otherwise we only send the path. */ if (HTRequest_fullURI(request)) StrAllocCopy(me->url, addr); else { me->url = HTParse(addr, "", PARSE_PATH | PARSE_PUNCTUATION); if (method == METHOD_OPTIONS) { /* ** We don't preserve the final slash or lack of same through ** out the code. This is mainly for optimization reasons ** but it gives a problem OPTIONS. We can either send a "*" ** or a "/" but not both. For now we send a "*". */ if (!strcmp(me->url, "/")) *me->url = '*'; } } HT_FREE(abs_location); me->state++; } /* ** Now send the URL that we have put together */ if (me->state == 2) { int status = HT_OK; if ((status = PUTS(me->url)) != HT_OK) return status; me->state++; #if 0 fprintf(stderr, "Requesting '%s'\n", me->url); #endif } PUTC(' '); /* ** Send out the version number. If we know it is a HTTP/1.0 server we ** are talking to then use HTTP/1.0, else use HTTP/1.1 as default version ** number */ if (me->version == HTTP_10) PUTS(HTTP_VERSION_10); else PUTS(HTTP_VERSION); PUTBLOCK(crlf, 2); /* Request Headers */ if (request_mask & HT_C_ACCEPT_TYPE) { HTFormat format = HTRequest_outputFormat(request); /* ** If caller has specified a specific output format then use this. ** Otherwise use all the registered converters to generate the ** accept header */ if (format == WWW_PRESENT) { int list; HTList *cur; BOOL first=YES; for (list=0; list<2; list++) { if ((!list && ((cur = HTFormat_conversion()) != NULL)) || (list && ((cur = HTRequest_conversion(request))!=NULL))) { HTPresentation * pres; while ((pres=(HTPresentation *) HTList_nextObject(cur))) { if (pres->rep_out==WWW_PRESENT && pres->quality<=1.0) { if (first) { PUTS("Accept: "); first=NO; } else PUTC(','); PUTS(HTAtom_name(pres->rep)); if (pres->quality < 1.0 && pres->quality >= 0.0) { sprintf(qstr, ";q=%1.1f", pres->quality); PUTS(qstr); } } } } } if (!first) PUTBLOCK(crlf, 2); } else { /* ** If we have an explicit output format then only send ** this one if not this is an internal libwww format ** of type www/<star> */ if (!HTMIMEMatch(WWW_INTERNAL, format)) { PUTS("Accept: "); PUTS(HTAtom_name(format)); PUTBLOCK(crlf, 2); } } } if (request_mask & HT_C_ACCEPT_CHAR) { int list; HTList *cur; BOOL first=YES; for (list=0; list<2; list++) { if ((!list && ((cur = HTFormat_charset()) != NULL)) || (list && ((cur = HTRequest_charset(request)) != NULL))) { HTAcceptNode *pres; while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) { if (first) { PUTS("Accept-Charset: "); first=NO; } else PUTC(','); PUTS(HTAtom_name(pres->atom)); if (pres->quality < 1.0 && pres->quality >= 0.0) { sprintf(qstr, ";q=%1.1f", pres->quality); PUTS(qstr); } } } } if (!first) PUTBLOCK(crlf, 2); } if (request_mask & HT_C_ACCEPT_ENC) { int list; HTList *cur; BOOL first=YES; for (list=0; list<2; list++) { if ((!list && ((cur = HTFormat_contentCoding()) != NULL)) || (list && ((cur = HTRequest_encoding(request)) != NULL))) { HTCoding * pres; while ((pres = (HTCoding *) HTList_nextObject(cur))) { double quality = HTCoding_quality(pres); if (first) { PUTS("Accept-Encoding: "); first = NO; } else PUTC(','); PUTS(HTCoding_name(pres)); if (quality < 1.0 && quality >= 0.0) { sprintf(qstr, ";q=%1.1f", quality); PUTS(qstr); } } } } if (!first) PUTBLOCK(crlf, 2); } if (request_mask & HT_C_ACCEPT_TE) { int list; HTList *cur; BOOL first=YES; for (list=0; list<2; list++) { if ((!list && ((cur = HTFormat_transferCoding()) != NULL)) || (list && ((cur = HTRequest_transfer(request)) != NULL))) { HTCoding * pres; while ((pres = (HTCoding *) HTList_nextObject(cur))) { double quality = HTCoding_quality(pres); const char * coding = HTCoding_name(pres); if (first) { PUTS("TE: "); first = NO; } else PUTC(','); /* Special check for "chunked" which is translated to "trailers" */ if (!strcasecomp(coding, "chunked")) PUTS("trailers"); else PUTS(coding); if (quality < 1.0 && quality >= 0.0) { sprintf(qstr, ";q=%1.1f", quality); PUTS(qstr); } } } } if (!first) PUTBLOCK(crlf, 2); } if (request_mask & HT_C_ACCEPT_LAN) { int list; HTList *cur; BOOL first=YES; for (list=0; list<2; list++) { if ((!list && ((cur = HTFormat_language()) != NULL)) || (list && ((cur = HTRequest_language(request)) != NULL))) { HTAcceptNode *pres; while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) { if (first) { PUTS("Accept-Language: "); first=NO; } else PUTC(','); PUTS(HTAtom_name(pres->atom)); if (pres->quality < 1.0 && pres->quality >= 0.0) { sprintf(qstr, ";q=%1.1f", pres->quality); PUTS(qstr); } } } } if (!first) PUTBLOCK(crlf, 2); } if (request_mask & HT_C_AUTH) { HTAssocList * cur = HTRequest_credentials(request); if (cur) { /* Access authentication */ HTAssoc * pres; while ((pres = (HTAssoc *) HTAssocList_nextObject(cur))) { PUTS(HTAssoc_name(pres)); PUTS(": "); PUTS(HTAssoc_value(pres)); PUTBLOCK(crlf, 2); } } } if (request_mask & HT_C_EXPECT) { HTAssocList * cur = HTRequest_expect(request); if (cur) { BOOL first=YES; HTAssoc * pres; while ((pres = (HTAssoc *) HTAssocList_nextObject(cur))) { char * value = HTAssoc_value(pres); if (first) { PUTS("Expect: "); first = NO; } else PUTC(','); /* Output the name */ PUTS(HTAssoc_name(pres)); /* Only output the value if not empty string */ if (*value) { PUTS("="); PUTS(value); } } PUTBLOCK(crlf, 2); } } if (request_mask & HT_C_FROM) { HTUserProfile * up = HTRequest_userProfile(request); const char * mailaddress = HTUserProfile_email(up); if (mailaddress) { PUTS("From: "); PUTS(mailaddress); PUTBLOCK(crlf, 2); } } if (request_mask & HT_C_HOST) { char *orig = HTAnchor_address((HTAnchor *) anchor); char *host = HTParse(orig, "", PARSE_HOST); char hostace[256]; #if 0 /* Keep the port number for HTTP/1.1 compliance */ char *ptr = strchr(host, ':'); /* Chop off port number */ if (ptr) *ptr = '\0'; #endif PUTS("Host: "); /****** still have to check UTF8toACE with port number */ if (!HTACEfromUTF8 (host, hostace, 255)) { PUTS(hostace); } else { PUTS(host); /* this may be dangerous, but helps server side debugging */ HTTRACE(PROT_TRACE, "HTTP........ Error: Cannot convert to ACE: `%s\'\n" _ host); } PUTBLOCK(crlf, 2); HT_FREE(orig); HT_FREE(host); } /* ** In the "If-*" series of headers, the ones related to etags have higher ** priority than the date relates ones. That is, if we have a etag then ** use that, otherwise use the date. First we check for range, match, and ** unmodified-since. */ if (request_mask & HT_C_IF_RANGE && etag) { PUTS("If-Range: \""); PUTS(etag); PUTC('"'); PUTBLOCK(crlf, 2); HTTRACE(PROT_TRACE, "HTTP........ If-Range using etag `%s\'\n" _ etag); } else if (request_mask & HT_C_IF_MATCH_ANY) { PUTS("If-Match: *"); PUTBLOCK(crlf, 2); HTTRACE(PROT_TRACE, "HTTP........ If-Match using `*\'\n"); } else if (request_mask & HT_C_IF_MATCH && etag) { PUTS("If-Match: \""); PUTS(etag); PUTC('"'); PUTBLOCK(crlf, 2); HTTRACE(PROT_TRACE, "HTTP........ If-Match using etag `%s\'\n" _ etag); } else if (request_mask & HT_C_IF_UNMOD_SINCE) { time_t lm = HTAnchor_lastModified(anchor); if (lm > 0) { PUTS("If-Unmodified-Since: "); PUTS(HTDateTimeStr(&lm, NO)); PUTBLOCK(crlf, 2); HTTRACE(PROT_TRACE, "HTTP........ If-Unmodified-Since `%s\'\n" _ HTDateTimeStr(&lm, NO)); } } /* ** If-None-Match and If-Modified-Since are equivalent except that the ** first uses etags and the second uses dates. Etags have precedence over ** dates. */ if (request_mask & HT_C_IF_NONE_MATCH_ANY) { PUTS("If-None-Match: *"); PUTBLOCK(crlf, 2); HTTRACE(PROT_TRACE, "HTTP........ If-None-Match using `*\'\n"); } else if (request_mask & HT_C_IF_NONE_MATCH && etag) { PUTS("If-None-Match: \""); PUTS(etag); PUTC('"'); PUTBLOCK(crlf, 2); HTTRACE(PROT_TRACE, "HTTP........ If-None-Match `%s\'\n" _ etag); } if (request_mask & HT_C_IMS) { time_t lm = HTAnchor_lastModified(anchor); if (lm > 0) { PUTS("If-Modified-Since: "); PUTS(HTDateTimeStr(&lm, NO)); PUTBLOCK(crlf, 2); HTTRACE(PROT_TRACE, "HTTP........ If-Modified-Since `%s\'\n" _ HTDateTimeStr(&lm, NO)); } } /* ** Max forwards is mainly for TRACE where we want to be able to stop the ** TRACE at a specific location un the message path. */ if (request_mask & HT_C_MAX_FORWARDS) { int hops = HTRequest_maxForwards(request); if (hops >= 0) { sprintf(qstr, "%d", hops); PUTS("Max-Forwards: "); PUTS(qstr); PUTBLOCK(crlf, 2); } } /* ** Range requests. For now, we only take the first entry registered for ** this request. This means that you can only send a single "unit" and ** then a set of range within this unit. This is in accordance with ** HTTP/1.1. Multiple units will go on multiple lines. */ if (request_mask & HT_C_RANGE) { HTAssocList * cur = HTRequest_range(request); if (cur) { /* Range requests */ HTAssoc * pres; while ((pres = (HTAssoc *) HTAssocList_nextObject(cur))) { PUTS("Range: "); PUTS(HTAssoc_name(pres)); /* Unit */ PUTS("="); PUTS(HTAssoc_value(pres)); /* Ranges within this unit */ PUTBLOCK(crlf, 2); } } } if (request_mask & HT_C_REFERER) { HTParentAnchor * parent_anchor = HTRequest_parent(request); if (parent_anchor) { char * act = HTAnchor_address((HTAnchor *) anchor); char * parent = HTAnchor_address((HTAnchor *) parent_anchor); #if 1 char * relative = HTRelative(parent, act); #else char * relative = HTParse(parent, act, PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); #endif if (relative && *relative) { PUTS("Referer: "); PUTS(relative); PUTBLOCK(crlf, 2); } HT_FREE(act); HT_FREE(parent); HT_FREE(relative); } } if (request_mask & HT_C_USER_AGENT) { PUTS("User-Agent: "); PUTS(HTLib_appName()); PUTC('/'); PUTS(HTLib_appVersion()); PUTC(' '); PUTS(HTLib_name()); PUTC('/'); PUTS(HTLib_version()); PUTBLOCK(crlf, 2); } HTTRACE(PROT_TRACE, "HTTP........ Generating HTTP/1.x Request Headers\n"); return HT_OK; }
/* Create a Content Type filter stack ** ---------------------------------- ** If a wildcard match is made, a temporary HTPresentation ** structure is made to hold the destination format while the ** new stack is generated. This is just to pass the out format to ** MIME so far. Storing the format of a stream in the stream might ** be a lot neater. ** ** The star/star format is special, in that if you can take ** that you can take anything. */ PUBLIC HTStream * HTStreamStack (HTFormat rep_in, HTFormat rep_out, HTStream * output_stream, HTRequest * request, BOOL guess) { HTList * conversion[2]; int which_list; double best_quality = -1e30; /* Pretty bad! */ HTPresentation *pres, *best_match=NULL; if (rep_out == WWW_RAW) { HTTRACE(CORE_TRACE, "StreamStack. Raw output...\n"); return output_stream ? output_stream : HTErrorStream(); } if (rep_out == rep_in) { HTTRACE(CORE_TRACE, "StreamStack. Identical input/output format (%s)\n" _ HTAtom_name(rep_out)); return output_stream ? output_stream : HTErrorStream(); } #ifdef HTDEBUG if (CORE_TRACE) { const char *p = HTAtom_name(rep_in); const char *q = HTAtom_name(rep_out); HTTRACE(CORE_TRACE, "StreamStack. Constructing stream stack for %s to %s\n" _ p ? p : "<NULL>" _ q ? q : "<NULL>"); } #endif /* HTDEBUG */ conversion[0] = HTRequest_conversion(request); conversion[1] = HTConversions; for(which_list = 0; which_list<2; which_list++) { HTList * cur = conversion[which_list]; while ((pres = (HTPresentation*)HTList_nextObject(cur))) { if ((pres->rep==rep_in || HTMIMEMatch(pres->rep, rep_in)) && (pres->rep_out==rep_out || HTMIMEMatch(pres->rep_out,rep_out))){ if (!best_match || better_match(pres->rep, best_match->rep) || (!better_match(best_match->rep, pres->rep) && pres->quality > best_quality)) { #ifdef HAVE_SYSTEM int result=0; if (pres->test_command) { result = system(pres->test_command); HTTRACE(CORE_TRACE, "StreamStack. system(%s) returns %d\n" _ pres->test_command _ result); } if (!result) { best_match = pres; best_quality = pres->quality; } #else best_match = pres; best_quality = pres->quality; #endif /* HAVE_SYSTEM */ } } } } if (best_match) { if (rep_out == WWW_SOURCE && best_match->rep_out != WWW_SOURCE) { HTTRACE(CORE_TRACE, "StreamStack. Source output\n"); return output_stream ? output_stream : HTErrorStream(); } return (*best_match->converter)(request, best_match->command, rep_in, rep_out, output_stream); } if (rep_out == WWW_SOURCE) { HTTRACE(CORE_TRACE, "StreamStack. Source output\n"); return output_stream ? output_stream : HTErrorStream(); } HTTRACE(CORE_TRACE, "StreamStack. NOT FOUND - error!\n"); return HTBlackHole(); }