📄 http.c
字号:
/* HyperText Tranfer Protocol - Client implementation HTTP.c** ==========================** Modified:** 27 Jan 1994 PDM Added Ari Luotonen's Fix for Reload when using proxy** servers.** 28 Apr 1997 AJL,FM Do Proxy Authorisation.*/#include <HTUtils.h>#include <HTTP.h>#include <LYUtils.h>#ifdef USE_SSL#include <HTNews.h>#endif#define HTTP_VERSION "HTTP/1.0"#define HTTP_PORT 80#define HTTPS_PORT 443#define SNEWS_PORT 563#define INIT_LINE_SIZE 1536 /* Start with line buffer this big */#define LINE_EXTEND_THRESH 256 /* Minimum read size */#define VERSION_LENGTH 20 /* for returned protocol version */#include <HTParse.h>#include <HTTCP.h>#include <HTFormat.h>#include <HTFile.h>#include <HTAlert.h>#include <HTMIME.h>#include <HTML.h>#include <HTInit.h>#include <HTAABrow.h>#include <HTAccess.h> /* Are we using an HTTP gateway? */#include <LYCookie.h>#include <LYGlobalDefs.h>#include <GridText.h>#include <LYStrings.h>#include <LYLeaks.h>struct _HTStream{ HTStreamClass * isa;};extern char * HTAppName; /* Application name: please supply */extern char * HTAppVersion; /* Application version: please supply */PUBLIC BOOL reloading = FALSE; /* Reloading => send no-cache pragma to proxy */PUBLIC char * redirecting_url = NULL; /* Location: value. */PUBLIC BOOL permanent_redirection = FALSE; /* Got 301 status? */PUBLIC BOOL redirect_post_content = FALSE; /* Don't convert to GET? */#ifdef USE_SSLPUBLIC SSL_CTX * ssl_ctx = NULL; /* SSL ctx */PUBLIC SSL * SSL_handle = NULL;PUBLIC int ssl_okay;PRIVATE void free_ssl_ctx NOARGS{ if (ssl_ctx != NULL) SSL_CTX_free(ssl_ctx);}PRIVATE int HTSSLCallback(int preverify_ok, X509_STORE_CTX *x509_ctx){ char *msg = NULL; int result = 1; if (!(preverify_ok || ssl_okay || ssl_noprompt)) {#ifdef USE_X509_SUPPORT HTSprintf0(&msg, "SSL error:%s-Continue?", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx))); if (HTForcedPrompt(ssl_noprompt, msg, YES)) ssl_okay = 1; else result = 0;#endif FREE(msg); } return result;}PUBLIC SSL * HTGetSSLHandle NOARGS{ if (ssl_ctx == NULL) { /* * First time only. */#if SSLEAY_VERSION_NUMBER < 0x0800 ssl_ctx = SSL_CTX_new(); X509_set_default_verify_paths(ssl_ctx->cert);#else SSLeay_add_ssl_algorithms(); ssl_ctx = SSL_CTX_new(SSLv23_client_method()); SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL); SSL_CTX_set_default_verify_paths(ssl_ctx); SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, HTSSLCallback);#endif /* SSLEAY_VERSION_NUMBER < 0x0800 */ atexit(free_ssl_ctx); } ssl_okay = 0; return(SSL_new(ssl_ctx));}PUBLIC void HTSSLInitPRNG NOARGS{#if SSLEAY_VERSION_NUMBER >= 0x00905100 if (RAND_status() == 0) { char rand_file[256]; time_t t; int pid; long l,seed; t = time(NULL); pid = getpid(); RAND_file_name(rand_file, 256); CTRACE((tfp,"HTTP: Seeding PRNG\n")); if(rand_file != NULL) { /* Seed as much as 1024 bytes from RAND_file_name */ RAND_load_file(rand_file, 1024); } /* Seed in time (mod_ssl does this) */ RAND_seed((unsigned char *)&t, sizeof(time_t)); /* Seed in pid (mod_ssl does this) */ RAND_seed((unsigned char *)&pid, sizeof(pid)); /* Initialize system's random number generator */ RAND_bytes((unsigned char *)&seed, sizeof(long)); lynx_srand(seed); while (RAND_status() == 0) { /* Repeatedly seed the PRNG using the system's random number generator until it has been seeded with enough data */ l = lynx_rand(); RAND_seed((unsigned char *)&l, sizeof(long)); } if (rand_file != NULL) { /* Write a rand_file */ RAND_write_file(rand_file); } }#endif /* SSLEAY_VERSION_NUMBER >= 0x00905100 */ return;}#define HTTP_NETREAD(sock, buff, size, handle) \ (handle ? SSL_read(handle, buff, size) : NETREAD(sock, buff, size))#define HTTP_NETWRITE(sock, buff, size, handle) \ (handle ? SSL_write(handle, buff, size) : NETWRITE(sock, buff, size))#define HTTP_NETCLOSE(sock, handle) \ { (void)NETCLOSE(sock); if (handle) SSL_free(handle); SSL_handle = handle = NULL; }#else#define HTTP_NETREAD(a, b, c, d) NETREAD(a, b, c)#define HTTP_NETWRITE(a, b, c, d) NETWRITE(a, b, c)#define HTTP_NETCLOSE(a, b) (void)NETCLOSE(a)#endif /* USE_SSL */#ifdef _WINDOWS /* 1997/11/06 (Thu) 13:00:08 */#define BOX_TITLE "Lynx " __FILE__#define BOX_FLAG (MB_ICONINFORMATION | MB_SETFOREGROUND)typedef struct { int fd; char *buf; int len;} recv_data_t;PUBLIC int ws_read_per_sec = 0;PRIVATE int ws_errno = 0;PRIVATE DWORD g_total_times = 0;PRIVATE DWORD g_total_bytes = 0;PUBLIC char * str_speed(void){ static char buff[32]; if (ws_read_per_sec > 1000) sprintf(buff, "%d.%03dkB", ws_read_per_sec / 1000, (ws_read_per_sec % 1000) ); else sprintf(buff, "%3d", ws_read_per_sec); return buff;}/* The same like read, but takes care of EINTR and uses select to timeout the stale connections. */PRIVATE int ws_read(int fd, char *buf, int len){ int res; int retry = 3; do { res = recv(fd, buf, len, 0); if (WSAEWOULDBLOCK == WSAGetLastError()) { Sleep(100); if (retry-- > 0) continue; } } while (res == SOCKET_ERROR && SOCKET_ERRNO == EINTR); return res;}PRIVATE void _thread_func (void *p){ int i, val, ret; recv_data_t *q = (recv_data_t *)p; i = 0; i++; val = ws_read(q->fd, q->buf, q->len); if (val == SOCKET_ERROR) { ws_errno = WSAGetLastError();#if 0 char buff[256]; sprintf(buff, "Thread read: %d, error (%ld), fd = %d, len = %d", i, ws_errno, q->fd, q->len); MessageBox(NULL, buff, BOX_TITLE, BOX_FLAG);#endif ret = -1; } else { ret = val; } ExitThread((DWORD)ret);}/* The same like read, but takes care of EINTR and uses select to timeout the stale connections. */PUBLIC int ws_netread(int fd, char *buf, int len){ int i; char buff[256]; /* 1998/03/30 (Mon) 09:01:21 */ HANDLE hThread; DWORD dwThreadID; DWORD exitcode = 0; DWORD ret_val = -1, val, process_time, now_TickCount, save_TickCount; static recv_data_t para; extern int win32_check_interrupt(void); /* LYUtil.c */ extern int lynx_timeout; /* LYMain.c */ extern CRITICAL_SECTION critSec_READ; /* LYMain.c */#define TICK 5#define STACK_SIZE 0x2000uL InitializeCriticalSection(&critSec_READ); para.fd = fd; para.buf = buf; para.len = len; ws_read_per_sec = 0; save_TickCount = GetTickCount(); hThread = CreateThread((void *)NULL, STACK_SIZE, (LPTHREAD_START_ROUTINE)_thread_func, (void *)¶, 0UL, &dwThreadID); if (hThread == 0) { HTInfoMsg("CreateThread Failed (read)"); goto read_exit; } i = 0; while (1) { val = WaitForSingleObject(hThread, 1000/TICK); i++; if (val == WAIT_FAILED) { HTInfoMsg("Wait Failed"); ret_val = -1; break; } else if (val == WAIT_TIMEOUT) { i++; if (i/TICK > (AlertSecs + 2)) { sprintf(buff, "Read Waiting (%2d.%01d) for %d Bytes", i/TICK, (i%TICK) * 10 / TICK, len); SetConsoleTitle(buff); } if (win32_check_interrupt() || ((i/TICK) > lynx_timeout)) { if (CloseHandle(hThread) == FALSE) { HTInfoMsg("Thread terminate Failed"); } WSASetLastError(ETIMEDOUT); ret_val = HT_INTERRUPTED; break; } } else if (val == WAIT_OBJECT_0) { if (GetExitCodeThread(hThread, &exitcode) == FALSE) { exitcode = -1; } if (CloseHandle(hThread) == FALSE) { HTInfoMsg("Thread terminate Failed"); } now_TickCount = GetTickCount(); if (now_TickCount > save_TickCount) process_time = now_TickCount - save_TickCount; else process_time = now_TickCount + (0xffffffff - save_TickCount); g_total_times += process_time; g_total_bytes += exitcode; if (g_total_bytes > 2000000) { ws_read_per_sec = g_total_bytes / (g_total_times/1000); } else { ws_read_per_sec = g_total_bytes * 1000 / g_total_times; } ret_val = exitcode; break; } } /* end while(1) */ read_exit: LeaveCriticalSection(&critSec_READ); return ret_val;}#endif/* * Strip any username from the given string so we retain only the host. * If the */PRIVATE void strip_userid ARGS1( char *, host){ char *p1 = host; char *p2 = strchr(host, '@'); char *fake; if (p2 != 0) { *p2++ = '\0'; if ((fake = HTParse(host, "", PARSE_HOST)) != NULL) { char *msg = NULL; CTRACE((tfp, "FIXME:%s\n", fake)); HTSprintf0(&msg, gettext("Address contains a username: %s"), host); HTAlert(msg); FREE(msg); } while ((*p1++ = *p2++) != '\0') { ; } }}/* Load Document from HTTP Server HTLoadHTTP()** ==============================**** Given a hypertext address, this routine loads a document.****** On entry,** arg is the hypertext reference of the article to be loaded.**** On exit,** returns >=0 If no error, a good socket number** <0 Error.**** The socket must be closed by the caller after the document has been** read.***/PRIVATE int HTLoadHTTP ARGS4 ( CONST char *, arg, HTParentAnchor *, anAnchor, HTFormat, format_out, HTStream*, sink){ int s; /* Socket number for returned data */ CONST char *url = arg; /* The URL which get_physical() returned */ bstring *command = NULL; /* The whole command */ char *eol; /* End of line if found */ char *start_of_data; /* Start of body of reply */ int status; /* tcp return */ int bytes_already_read; char crlf[3]; /* A CR LF equivalent string */ HTStream *target; /* Unconverted data */ HTFormat format_in; /* Format arriving in the message */ BOOL do_head = FALSE; /* Whether or not we should do a head */ BOOL do_post = FALSE; /* ARE WE posting ? */ char *METHOD; BOOL had_header; /* Have we had at least one header? */ char *line_buffer; char *line_kept_clean; int real_length_of_line; BOOL extensions; /* Assume good HTTP server */ char *linebuf = NULL; char temp[80]; BOOL first_Accept = TRUE; BOOL show_401 = FALSE; BOOL show_407 = FALSE; BOOL auth_proxy = NO; /* Generate a proxy authorization. - AJL */ int length, rawlength, rv; int server_status; BOOL doing_redirect, already_retrying = FALSE; int len = 0;#ifdef USE_SSL BOOL do_connect = FALSE; /* ARE WE going to use a proxy tunnel ? */ BOOL did_connect = FALSE; /* ARE WE actually using a proxy tunnel ? */ CONST char *connect_url = NULL; /* The URL being proxied */ char *connect_host = NULL; /* The host being proxied */ SSL * handle = NULL; /* The SSL handle */ char ssl_dn[256]; char *cert_host; char *ssl_host; char *p; char *msg = NULL;#if SSLEAY_VERSION_NUMBER >= 0x0900 BOOL try_tls = TRUE;#endif /* SSLEAY_VERSION_NUMBER >= 0x0900 */ SSL_handle = NULL;#else void * handle = NULL;#endif /* USE_SSL */ if (anAnchor->isHEAD) do_head = TRUE; else if (anAnchor->post_data) do_post = TRUE; if (!url) { status = -3; _HTProgress (BAD_REQUEST); goto done; } if (!*url) { status = -2; _HTProgress (BAD_REQUEST); goto done; }#ifdef USE_SSL if (using_proxy && !strncmp(url, "http://", 7)) { if ((connect_url = strstr((url+7), "https://"))) { do_connect = TRUE; connect_host = HTParse(connect_url, "https", PARSE_HOST); if (!strchr(connect_host, ':')) { sprintf(temp, ":%d", HTTPS_PORT); StrAllocCat(connect_host, temp); } CTRACE((tfp, "HTTP: connect_url = '%s'\n", connect_url)); CTRACE((tfp, "HTTP: connect_host = '%s'\n", connect_host)); } else if ((connect_url = strstr((url+7), "snews://"))) { do_connect = TRUE; connect_host = HTParse(connect_url, "snews", PARSE_HOST); if (!strchr(connect_host, ':')) { sprintf(temp, ":%d", SNEWS_PORT); StrAllocCat(connect_host, temp); } CTRACE((tfp, "HTTP: connect_url = '%s'\n", connect_url)); CTRACE((tfp, "HTTP: connect_host = '%s'\n", connect_host)); } }#endif /* USE_SSL */ sprintf(crlf, "%c%c", CR, LF); /* ** At this point, we're talking HTTP/1.0. */ extensions = YES;try_again: /* ** All initializations are moved down here from up above, ** so we can start over here... */ eol = 0; had_header = NO; length = 0; doing_redirect = FALSE; permanent_redirection = FALSE; redirect_post_content = FALSE; target = NULL; line_buffer = NULL; line_kept_clean = NULL;#ifdef USE_SSL if (!strncmp(url, "https", 5)) status = HTDoConnect (url, "HTTPS", HTTPS_PORT, &s); else status = HTDoConnect (url, "HTTP", HTTP_PORT, &s);#else if (!strncmp(url, "https", 5)) { HTAlert(gettext("This client does not contain support for HTTPS URLs.")); status = HT_NOT_LOADED; goto done; } status = HTDoConnect (arg, "HTTP", HTTP_PORT, &s);#endif /* USE_SSL */ if (status == HT_INTERRUPTED) { /* ** Interrupt cleanly. */ CTRACE((tfp, "HTTP: Interrupted on connect; recovering cleanly.\n")); _HTProgress (CONNECTION_INTERRUPTED); status = HT_NOT_LOADED; goto done; } if (status < 0) {#ifdef _WINDOWS CTRACE((tfp, "HTTP: Unable to connect to remote host for `%s'\n" " (status = %d, sock_errno = %d).\n", url, status, SOCKET_ERRNO));#else CTRACE((tfp, "HTTP: Unable to connect to remote host for `%s' (errno = %d).\n", url, SOCKET_ERRNO));#endif HTAlert(gettext("Unable to connect to remote host.")); status = HT_NOT_LOADED; goto done; }/* *sob* All this needs to be converted to handle binary strings * if we're going to be able to handle binary form uploads... * This is a nice long function as well. *sigh* -RJP
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -