📄 http.c
字号:
BStrCat0(command, crlf); /* Blank line means "end" of headers */ BStrCat(command, anAnchor->post_data); } else BStrCat0(command, crlf); /* Blank line means "end" of headers */ if (TRACE) { CTRACE((tfp, "Writing:\n")); trace_bstring(command);#ifdef USE_SSL CTRACE((tfp, "%s", (anAnchor->post_data && !do_connect ? crlf : "")));#else CTRACE((tfp, "%s", (anAnchor->post_data ? crlf : "")));#endif /* USE_SSL */ CTRACE((tfp, "----------------------------------\n")); } _HTProgress (gettext("Sending HTTP request."));#ifdef NOT_ASCII /* S/390 -- gil -- 0548 */ { char *p; for ( p = BStrData(command); p < BStrData(command) + BStrLen(command); p++ ) *p = TOASCII(*p); }#endif /* NOT_ASCII */ status = HTTP_NETWRITE(s, BStrData(command), BStrLen(command), handle); BStrFree(command); FREE(linebuf); if (status <= 0) { if (status == 0) { CTRACE((tfp, "HTTP: Got status 0 in initial write\n")); /* Do nothing. */ } else if ((SOCKET_ERRNO == ENOTCONN || SOCKET_ERRNO == ECONNRESET || SOCKET_ERRNO == EPIPE) && !already_retrying && /* Don't retry if we're posting. */ !do_post) { /* ** Arrrrgh, HTTP 0/1 compatibility problem, maybe. */ CTRACE((tfp, "HTTP: BONZO ON WRITE Trying again with HTTP0 request.\n")); _HTProgress (RETRYING_AS_HTTP0); HTTP_NETCLOSE(s, handle); extensions = NO; already_retrying = TRUE; goto try_again; } else { CTRACE((tfp, "HTTP: Hit unexpected network WRITE error; aborting connection.\n")); HTTP_NETCLOSE(s, handle); status = -1; HTAlert(gettext("Unexpected network write error; connection aborted.")); goto done; } } CTRACE((tfp, "HTTP: WRITE delivered OK\n")); _HTProgress (gettext("HTTP request sent; waiting for response.")); /* Read the first line of the response ** ----------------------------------- */ { /* Get numeric status etc */ BOOL end_of_file = NO; int buffer_length = INIT_LINE_SIZE; line_buffer = typecallocn(char, buffer_length); if (line_buffer == NULL) outofmem(__FILE__, "HTLoadHTTP"); HTReadProgress (bytes_already_read = 0, 0); do {/* Loop to read in the first line */ /* ** Extend line buffer if necessary for those crazy WAIS URLs ;-) */ if (buffer_length - length < LINE_EXTEND_THRESH) { buffer_length = buffer_length + buffer_length; line_buffer = (char *)realloc(line_buffer, (buffer_length * sizeof(char))); if (line_buffer == NULL) outofmem(__FILE__, "HTLoadHTTP"); } CTRACE((tfp, "HTTP: Trying to read %d\n", buffer_length - length - 1)); status = HTTP_NETREAD(s, line_buffer + length, buffer_length - length - 1, handle); CTRACE((tfp, "HTTP: Read %d\n", status)); if (status <= 0) { /* * Retry if we get nothing back too. * Bomb out if we get nothing twice. */ if (status == HT_INTERRUPTED) { CTRACE((tfp, "HTTP: Interrupted initial read.\n")); _HTProgress (CONNECTION_INTERRUPTED); HTTP_NETCLOSE(s, handle); status = HT_NO_DATA; goto clean_up; } else if (status < 0 && (SOCKET_ERRNO == ENOTCONN ||#ifdef _WINDOWS /* 1997/11/09 (Sun) 16:59:58 */ SOCKET_ERRNO == ETIMEDOUT ||#endif SOCKET_ERRNO == ECONNRESET || SOCKET_ERRNO == EPIPE) && !already_retrying && !do_post) { /* ** Arrrrgh, HTTP 0/1 compability problem, maybe. */ CTRACE((tfp, "HTTP: BONZO Trying again with HTTP0 request.\n")); HTTP_NETCLOSE(s, handle); FREE(line_buffer); FREE(line_kept_clean); extensions = NO; already_retrying = TRUE; _HTProgress (RETRYING_AS_HTTP0); goto try_again; } else { CTRACE((tfp, "HTTP: Hit unexpected network read error; aborting connection; status %d.\n", status)); HTAlert(gettext("Unexpected network read error; connection aborted.")); HTTP_NETCLOSE(s, handle); status = -1; goto clean_up; } }#ifdef NOT_ASCII /* S/390 -- gil -- 0564 */ { char *p; for ( p = line_buffer + length; p < line_buffer + length + status; p++ ) *p = FROMASCII(*p); }#endif /* NOT_ASCII */ bytes_already_read += status; HTReadProgress (bytes_already_read, 0);#ifdef UCX /* UCX returns -1 on EOF */ if (status == 0 || status == -1)#else if (status == 0)#endif { end_of_file = YES; break; } line_buffer[length+status] = 0; if (line_buffer) { FREE(line_kept_clean); line_kept_clean = (char *)malloc(buffer_length * sizeof(char)); if (line_kept_clean == NULL) outofmem(__FILE__, "HTLoadHTTP"); memcpy(line_kept_clean, line_buffer, buffer_length); real_length_of_line = length + status; } eol = strchr(line_buffer + length, LF); /* Do we *really* want to do this? */ if (eol && eol != line_buffer && *(eol-1) == CR) *(eol-1) = ' '; length = length + status; /* Do we really want to do *this*? */ if (eol) *eol = 0; /* Terminate the line */ } /* All we need is the first line of the response. If it's a HTTP/1.0 ** response, then the first line will be absurdly short and therefore ** we can safely gate the number of bytes read through this code ** (as opposed to below) to ~1000. ** ** Well, let's try 100. */ while (!eol && !end_of_file && bytes_already_read < 100); } /* Scope of loop variables */ /* save total length, in case we decide later to show it all - kw */ rawlength = length; /* We now have a terminated unfolded line. Parse it. ** -------------------------------------------------- */ CTRACE((tfp, "HTTP: Rx: %s\n", line_buffer)); /* ** Kludge to work with old buggy servers and the VMS Help gateway. ** They can't handle the third word, so we try again without it. */ if (extensions && /* Old buggy server or Help gateway? */ (0==strncmp(line_buffer,"<TITLE>Bad File Request</TITLE>",31) || 0==strncmp(line_buffer,"Address should begin with",25) || 0==strncmp(line_buffer,"<TITLE>Help ",12) || 0==strcmp(line_buffer, "Document address invalid or access not authorised"))) { FREE(line_buffer); FREE(line_kept_clean); extensions = NO; already_retrying = TRUE; CTRACE((tfp, "HTTP: close socket %d to retry with HTTP0\n", s)); HTTP_NETCLOSE(s, handle); /* print a progress message */ _HTProgress (RETRYING_AS_HTTP0); goto try_again; } { int fields; char server_version[VERSION_LENGTH+1]; server_version[0] = 0; fields = sscanf(line_buffer, "%20s %d", server_version, &server_status); CTRACE((tfp, "HTTP: Scanned %d fields from line_buffer\n", fields)); if (http_error_file) { /* Make the status code externally available */ FILE *error_file;#ifdef SERVER_STATUS_ONLY error_file = fopen(http_error_file, TXT_W); if (error_file) { /* Managed to open the file */ fprintf(error_file, "error=%d\n", server_status); fclose(error_file); }#else error_file = fopen(http_error_file, TXT_A); if (error_file) { /* Managed to open the file */ fprintf(error_file, " URL=%s (%s)\n", url, METHOD); fprintf(error_file, "STATUS=%s\n", line_buffer); fclose(error_file); }#endif /* SERVER_STATUS_ONLY */ } /* ** Rule out a non-HTTP/1.n reply as best we can. */ if (fields < 2 || !server_version[0] || server_version[0] != 'H' || server_version[1] != 'T' || server_version[2] != 'T' || server_version[3] != 'P' || server_version[4] != '/' || server_version[6] != '.') { /* * Ugh! An HTTP0 reply, */ HTAtom * encoding; CTRACE((tfp, "--- Talking HTTP0.\n")); format_in = HTFileFormat(url, &encoding, NULL); /* ** Treat all plain text as HTML. ** This sucks but its the only solution without ** without looking at content. */ if (!strncmp(HTAtom_name(format_in), "text/plain",10)) { CTRACE((tfp, "HTTP: format_in being changed to text/HTML\n")); format_in = WWW_HTML; } if (!IsUnityEnc(encoding)) { /* ** Change the format to that for "www/compressed". */ CTRACE((tfp, "HTTP: format_in is '%s',\n", HTAtom_name(format_in))); StrAllocCopy(anAnchor->content_type, HTAtom_name(format_in)); StrAllocCopy(anAnchor->content_encoding, HTAtom_name(encoding)); format_in = HTAtom_for("www/compressed"); CTRACE((tfp, " Treating as '%s' with encoding '%s'\n", "www/compressed", HTAtom_name(encoding))); } start_of_data = line_kept_clean; } else { /* ** Set up to decode full HTTP/1.n response. - FM */ format_in = HTAtom_for("www/mime"); CTRACE((tfp, "--- Talking HTTP1.\n")); /* ** We set start_of_data to "" when !eol here because there ** will be a put_block done below; we do *not* use the value ** of start_of_data (as a pointer) in the computation of ** length (or anything else) when !eol. Otherwise, set the ** value of length to what we have beyond eol (i.e., beyond ** the status line). - FM */ start_of_data = eol ? eol + 1 : ""; length = eol ? length - (start_of_data - line_buffer) : 0; /* ** Trim trailing spaces in line_buffer so that we can use ** it in messages which include the status line. - FM */ while (line_buffer[strlen(line_buffer)-1] == ' ') line_buffer[strlen(line_buffer)-1] = '\0'; /* ** Take appropriate actions based on the status. - FM */ switch (server_status/100) { case 1: /* ** HTTP/1.1 Informational statuses. ** 100 Continue. ** 101 Switching Protocols. ** > 101 is unknown. ** We should never get these, and they have only ** the status line and possibly other headers, ** so we'll deal with them by showing the full ** header to the user as text/plain. - FM */ HTAlert(gettext("Got unexpected Informational Status.")); do_head = TRUE; break; case 2: /* ** Good: Got MIME object! (Successful) - FM */ if (do_head) { /* * If HEAD was requested, show headers (and possibly * bogus body) for all 2xx status codes as text/plain - KW */ HTProgress(line_buffer); break; } switch (server_status) { case 204: /* * No Content. */ HTAlert(line_buffer); HTTP_NETCLOSE(s, handle); HTNoDataOK = 1; status = HT_NO_DATA; goto clean_up; case 205: /* * Reset Content. The server has fulfilled the * request but nothing is returned and we should * reset any form content. We'll instruct the * user to do that, and restore the current * document. - FM */ HTAlert(gettext("Request fulfilled. Reset Content.")); HTTP_NETCLOSE(s, handle); status = HT_NO_DATA; goto clean_up; case 206: /* * Partial Content. We didn't send a Range * so something went wrong somewhere. Show * the status message and restore the current * document. - FM */ HTAlert(line_buffer); HTTP_NETCLOSE(s, handle); status = HT_NO_DATA; goto clean_up; default: /* * 200 OK. * 201 Created. * 202 Accepted. * 203 Non-Authoritative Information. * > 206 is unknown. * All should return something to display. */#if defined(USE_SSL) && !defined(DISABLE_NEWS) if (do_connect) { CTRACE((tfp, "HTTP: Proxy tunnel to '%s' established.\n", connect_host)); do_connect = FALSE; url = connect_url; FREE(line_buffer); FREE(line_kept_clean); if (!strncmp(connect_url, "snews", 5)) { CTRACE((tfp, " Will attempt handshake and snews connection.\n")); status = HTNewsProxyConnect(s, url, anAnchor, format_out, sink); goto done; } did_connect = TRUE; already_retrying = TRUE; eol = 0; bytes_already_read = 0; had_header = NO; length = 0; doing_redirect = FALSE; permanent_redirection = FALSE; target = NULL; CTRACE((tfp, " Will attempt handshake and resubmit headers.\n")); goto use_tunnel; }#endif /* USE_SSL */ HTProgress(line_buffer); } /* case 2 switch */ break; case 3: /* ** Various forms of Redirection. - FM ** 300 Multiple Choices. ** 301 Moved Permanently. ** 302 Found (temporary; we can, and do, use GET). ** 303 See Other (temporary; always use GET). ** 304 Not Modified. ** 305 Use Proxy. ** 306 Set Proxy. ** 307 Temporary Redirect with method retained. ** > 308 is unknown. */ if (no_url_redirection || do_head || keep_mime_headers) { /* * If any of these flags are set, we do not redirect, * but instead show what was returned to the user as * text/plain. - FM */ HTProgress(line_buffer); break; } if (server_status == 300) { /* Multiple Choices */ /* * For client driven content negotiation. The server * should be sending some way for the user-agent to * make a selection, so we'll show the user whatever * the server returns. There might be a Location: * header with the server's preference present, but * the choice should be up to the user, someday based * on an Alternates: header, and a body always should * be present with descriptions and links for the * choices (i.e., we use the latter, for now). - FM */ HTAlert(line_buffer); if (traversal) { HTTP_NETCLOSE(s, handle); status = -1; goto clean_up; } if (!dump_output_immediately && format_out == HTAtom_for("www/download")) { /* * Convert a download request to * a presentation request for * interactive users. - FM */ format_out = WWW_PRESENT; } break; } if (server_status == 304) { /* Not Modified */ /* * We didn't send an "If-Modified-Since" header, * so this status is inappropriate. We'll deal * with it by showing the full header to the user * as text/plain. - FM */ HTAlert(gettext("Got unexpected 304 Not Modified status.")); do_head = TRUE; break; } if (server_status == 305 || server_status == 306 || server_status > 307) { /* * Show user the content, if any, for 305, 306, * or unknown status. - FM */ HTAlert(line_buffer); if (traversal) { HTTP_NETCLOSE(s, handle); status = -1; goto clean_up; } if (!dump_output_immediately && format_out == HTAtom_for("www/download")) { /* * Convert a download request to * a presentation request for * interactive users. - FM */ format_out = WWW_PRESENT; } break; } /* * We do not load the file, but read the headers for * the "Location:", check out that redirecting_url * and if it's acceptible (e.g., not a telnet URL * when we have that disabled), initiate a new fetch. * If that's another redirecting_url, we'll repeat the * checks, and fetch initiations if acceptible, until * we reach the actual URL, or the redirection limit * set in HTAccess.c is exceeded. If the status was 301 * indicating that the relocation is permanent, we set * the permanent_redirection flag to make it permanent * for the current anchor tree (i.e., will persist until * the tree is freed or the client exits). If the * redirection would include POST content, we seek * confirmation from an interactive user, with option to * use 303 for 301 (but not for 307), and otherwise refuse * the redirection. We also don't allow permanent * redirection if we keep POST content. If we don't find * the Location header or it's value is zero-length, we * display whatever the server returned, and the user * should RELOAD that to try again, or make a selection * from it if it contains links, or Left-Arrow to the * previous document. - FM */ { if ((dump_output_immediately || traversal) && do_post && server_status != 303 && server_status != 302 && server_status != 301) { /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -