PRIVATE void free_channel (HTChannel * ch) { if (ch) { /* Close the input and output stream */ if (ch->input) { (*ch->input->isa->close)(ch->input); ch->input = NULL; } if (ch->output) { (*ch->output->isa->close)(ch->output); ch->output = NULL; } /* Close the socket */ if (ch->sockfd != INVSOC) { NETCLOSE(ch->sockfd); HTNet_decreaseSocket(); HTTRACE(PROT_TRACE, "Channel..... Deleted %p, socket %d\n" _ ch _ ch->sockfd); ch->sockfd = INVSOC; } /* Close the file */ if (ch->fp) { fclose(ch->fp); HTTRACE(PROT_TRACE, "Channel..... Deleted %p, file %p\n" _ ch _ ch->fp); ch->fp = NULL; } HT_FREE(ch); } }
PRIVATE int response ARGS1(CONST char *,command) { int result; char * p = response_text; if (command) { int status; int length = strlen(command); if (TRACE) fprintf(stderr, "NNTP command to be sent: %s", command); #ifdef NOT_ASCII { CONST char * p; char * q; char ascii[LINE_LENGTH+1]; for(p = command, q=ascii; *p; p++, q++) { *q = TOASCII(*p); } status = NETWRITE(s, ascii, length); } #else status = NETWRITE(s, command, length); #endif if (status<0){ if (TRACE) fprintf(stderr, "HTNews: Unable to send command. Disconnecting.\n"); NETCLOSE(s); s = -1; return status; } /* if bad status */ } /* if command to be sent */ for(;;) { if (((*p++=NEXT_CHAR) == '\n') || (p == &response_text[LINE_LENGTH])) { *p++=0; /* Terminate the string */ if (TRACE) fprintf(stderr, "NNTP Response: %s\n", response_text); sscanf(response_text, "%d", &result); return result; } /* if end of line */ if (*(p-1) < 0) { if (TRACE) fprintf(stderr, "HTNews: EOF on read, closing socket %d\n", s); NETCLOSE(s); /* End of file, close socket */ return s = -1; /* End of file on response */ } } /* Loop over characters */ }
/* Parse a socket given format and file number ** ** This routine is responsible for creating and PRESENTING any ** graphic (or other) objects described by the file. ** ** The file number given is assumed to be a TELNET stream ie containing ** CRLF at the end of lines which need to be stripped to LF for unix ** when the format is textual. ** */ PUBLIC int HTParseSocket ARGS6( HTFormat, format_in, HTFormat, format_out, HTParentAnchor *, anchor, int, file_number, HTStream*, sink, int, compressed) { HTStream * stream; HTStreamClass targetClass; int rv; stream = HTStreamStack(format_in, format_out, compressed, sink, anchor); if (!stream) { char buffer[1024]; /* @@@@@@@@ */ sprintf(buffer, "Sorry, can't convert from %s to %s.", HTAtom_name(format_in), HTAtom_name(format_out)); #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "HTFormat: %s\n", buffer); #endif return HTLoadError(sink, 501, buffer); } targetClass = *(stream->isa); /* Copy pointers to procedures */ rv = HTCopy(file_number, stream, 0); if (rv == -1) { /* handle_interrupt should have been done in HTCopy */ /* (*targetClass.handle_interrupt)(stream); */ return HT_INTERRUPTED; } (*targetClass.end_document)(stream); /* New thing: we force close the data socket here, so that if an external viewer gets forked off in the free method below, the connection doesn't remain upon until the child exits -- which it does if we don't do this. */ NETCLOSE (file_number); (*targetClass.free)(stream); return HT_LOADED; }
static int echo_client(char *hostname, const int fd) { /* * WRITE TEXT FROM FILE DESCRIPTOR INTO STDOUT */ char buf[BUFSIZ]; int cc; D_PRINTF( "echo_client running\n" ); while (getppid() != 1) { cc = NETREAD(fd, buf, sizeof(buf)); if (cc > 0) { write(STDOUT_FILENO, buf, cc); } } D_PRINTF( "Exiting echo_client\n" ); NETCLOSE(fd); }
/* ** Closes a file descriptor whatever means are available on the current ** platform. If we have unix file descriptors then use this otherwise use ** the ANSI C file descriptors ** ** returns HT_ERROR Error has occured or interrupted ** HT_OK if connected ** HT_WOULD_BLOCK if operation would have blocked */ PUBLIC int HTFileClose (HTNet * net) { HTHost * host = HTNet_host(net); HTChannel * ch = HTHost_channel(host); int status = -1; if (net && ch) { #ifdef NO_UNIX_IO FILE * fp = HTChannel_file(ch); if (fp) { HTTRACE(PROT_TRACE, "Closing..... ANSI file %p\n" _ fp); status = fclose(fp); HTChannel_setFile(ch, NULL); } #else SOCKET sockfd = HTChannel_socket(ch); if (sockfd != INVSOC) { HTTRACE(PROT_TRACE, "Closing..... fd %d\n" _ sockfd); status = NETCLOSE(sockfd); HTChannel_setSocket(ch, INVSOC); } #endif /* NO_UNIX_IO */ } return status < 0 ? HT_ERROR : HT_OK; }
/* Load by name HTLoadFinger * ============ */ int HTLoadFinger(const char *arg, HTParentAnchor *anAnchor, HTFormat format_out, HTStream *stream) { static char empty[1]; char *username, *sitename; /* Fields extracted from URL */ char *slash, *at_sign; /* Fields extracted from URL */ char *command, *str, *param; /* Buffers */ int port; /* Port number from URL */ int status; /* tcp return */ int result = HT_LOADED; BOOL IsGopherURL = FALSE; const char *p1 = arg; CTRACE((tfp, "HTFinger: Looking for %s\n", (arg ? arg : "NULL"))); if (!(arg && *arg)) { HTAlert(COULD_NOT_LOAD_DATA); return HT_NOT_LOADED; /* Ignore if no name */ } if (!initialized) initialized = initialize(); if (!initialized) { HTAlert(gettext("Could not set up finger connection.")); return HT_NOT_LOADED; /* FAIL */ } /* Set up the host and command fields. */ if (!strncasecomp(arg, "finger://", 9)) { p1 = arg + 9; /* Skip "finger://" prefix */ } else if (!strncasecomp(arg, "gopher://", 9)) { p1 = arg + 9; /* Skip "gopher://" prefix */ IsGopherURL = TRUE; } param = 0; sitename = StrAllocCopy(param, p1); if (param == 0) { HTAlert(COULD_NOT_LOAD_DATA); return HT_NOT_LOADED; } else if ((slash = StrChr(sitename, '/')) != NULL) { *slash++ = '\0'; HTUnEscape(slash); if (IsGopherURL) { if (*slash != '0') { HTAlert(COULD_NOT_LOAD_DATA); return HT_NOT_LOADED; /* FAIL */ } *slash++ = '\0'; } } if ((at_sign = StrChr(sitename, '@')) != NULL) { if (IsGopherURL) { HTAlert(COULD_NOT_LOAD_DATA); return HT_NOT_LOADED; /* FAIL */ } else { *at_sign++ = '\0'; username = sitename; sitename = at_sign; HTUnEscape(username); } } else if (slash) { username = slash; } else { username = empty; } if (*sitename == '\0') { HTAlert(gettext("Could not load data (no sitename in finger URL)")); result = HT_NOT_LOADED; /* Ignore if no name */ } else if (HTParsePort(sitename, &port) != NULL) { if (port != 79) { HTAlert(gettext("Invalid port number - will only use port 79!")); result = HT_NOT_LOADED; /* Ignore if wrong port */ } } if (result == HT_LOADED) { /* Load the string for making a connection/ */ str = 0; HTSprintf0(&str, "lose://%s/", sitename); /* Load the command for the finger server. */ command = 0; if (at_sign && slash) { if (*slash == 'w' || *slash == 'W') { HTSprintf0(&command, "/w %s%c%c", username, CR, LF); } else { HTSprintf0(&command, "%s%c%c", username, CR, LF); } } else if (at_sign) { HTSprintf0(&command, "%s%c%c", username, CR, LF); } else if (*username == '/') { if ((slash = StrChr((username + 1), '/')) != NULL) { *slash = ' '; } HTSprintf0(&command, "%s%c%c", username, CR, LF); } else if ((*username == 'w' || *username == 'W') && *(username + 1) == '/') { if (*username + 2 != '\0') { *(username + 1) = ' '; } else { *(username + 1) = '\0'; } HTSprintf0(&command, "/%s%c%c", username, CR, LF); } else if ((*username == 'w' || *username == 'W') && *(username + 1) == '\0') { HTSprintf0(&command, "/%s%c%c", username, CR, LF); } else if ((slash = StrChr(username, '/')) != NULL) { *slash++ = '\0'; if (*slash == 'w' || *slash == 'W') { HTSprintf0(&command, "/w %s%c%c", username, CR, LF); } else { HTSprintf0(&command, "%s%c%c", username, CR, LF); } } else { HTSprintf0(&command, "%s%c%c", username, CR, LF); } /* Now, let's get a stream setup up from the FingerHost: * CONNECTING to finger host */ CTRACE((tfp, "HTFinger: doing HTDoConnect on '%s'\n", str)); status = HTDoConnect(str, "finger", FINGER_PORT, &finger_fd); CTRACE((tfp, "HTFinger: Done DoConnect; status %d\n", status)); if (status == HT_INTERRUPTED) { /* Interrupt cleanly */ CTRACE((tfp, "HTFinger: Interrupted on connect; recovering cleanly.\n")); HTProgress(CONNECTION_INTERRUPTED); result = HT_NOT_LOADED; } else if (status < 0) { NETCLOSE(finger_fd); finger_fd = -1; CTRACE((tfp, "HTFinger: Unable to connect to finger host.\n")); HTAlert(gettext("Could not access finger host.")); result = HT_NOT_LOADED; /* FAIL */ } else { CTRACE((tfp, "HTFinger: Connected to finger host '%s'.\n", str)); /* Send the command, and process response if successful. */ if (response(command, sitename, anAnchor, format_out, stream) != 0) { HTAlert(gettext("No response from finger server.")); result = HT_NOT_LOADED; } } FREE(str); FREE(command); } FREE(param); return result; }
static int response(char *command, char *sitename, HTParentAnchor *anAnchor, HTFormat format_out, HTStream *sink) { int status; int length = (int) strlen(command); int ch, i; char line[BIG], *l, *cmd = NULL; char *p = line, *href = NULL; if (length == 0) return (-1); /* Set up buffering. */ HTInitInput(finger_fd); /* Send the command. */ CTRACE((tfp, "HTFinger command to be sent: %s", command)); status = (int) NETWRITE(finger_fd, (char *) command, (unsigned) length); if (status < 0) { CTRACE((tfp, "HTFinger: Unable to send command. Disconnecting.\n")); NETCLOSE(finger_fd); finger_fd = -1; return status; } /* if bad status */ /* Make a hypertext object with an anchor list. */ target = HTML_new(anAnchor, format_out, sink); targetClass = *target->isa; /* Copy routine entry points */ /* Create the results report. */ CTRACE((tfp, "HTFinger: Reading finger information\n")); START(HTML_HTML); PUTC('\n'); START(HTML_HEAD); PUTC('\n'); START(HTML_TITLE); PUTS("Finger server on "); PUTS(sitename); END(HTML_TITLE); PUTC('\n'); END(HTML_HEAD); PUTC('\n'); START(HTML_BODY); PUTC('\n'); START(HTML_H1); PUTS("Finger server on "); START(HTML_EM); PUTS(sitename); END(HTML_EM); PUTS(": "); StrAllocCopy(cmd, command); for (i = ((int) strlen(cmd) - 1); i >= 0; i--) { if (cmd[i] == LF || cmd[i] == CR) { cmd[i] = '\0'; } else { break; } } PUTS(cmd); FREE(cmd); END(HTML_H1); PUTC('\n'); START(HTML_PRE); while ((ch = NEXT_CHAR) != EOF) { if (interrupted_in_htgetcharacter) { CTRACE((tfp, "HTFinger: Interrupted in HTGetCharacter, apparently.\n")); _HTProgress(CONNECTION_INTERRUPTED); goto end_html; } if (ch != LF) { *p = (char) ch; /* Put character in line */ if (p < &line[BIG - 1]) { p++; } } else { *p = '\0'; /* Terminate line */ /* * OK we now have a line. * Load it as 'l' and parse it. */ p = l = line; while (*l) { if (StrNCmp(l, STR_NEWS_URL, LEN_NEWS_URL) && StrNCmp(l, "snews://", 8) && StrNCmp(l, "nntp://", 7) && StrNCmp(l, "snewspost:", 10) && StrNCmp(l, "snewsreply:", 11) && StrNCmp(l, "newspost:", 9) && StrNCmp(l, "newsreply:", 10) && StrNCmp(l, "ftp://", 6) && StrNCmp(l, "file:/", 6) && StrNCmp(l, "finger://", 9) && StrNCmp(l, "http://", 7) && StrNCmp(l, "https://", 8) && StrNCmp(l, "wais://", 7) && StrNCmp(l, STR_MAILTO_URL, LEN_MAILTO_URL) && StrNCmp(l, "cso://", 6) && StrNCmp(l, "gopher://", 9)) PUTC(*l++); else { StrAllocCopy(href, l); start_anchor(strtok(href, " \r\n\t,>)\"")); while (*l && !StrChr(" \r\n\t,>)\"", *l)) PUTC(*l++); END(HTML_A); FREE(href); } } PUTC('\n'); } } NETCLOSE(finger_fd); finger_fd = -1; end_html: END(HTML_PRE); PUTC('\n'); END(HTML_BODY); PUTC('\n'); END(HTML_HTML); PUTC('\n'); FREE_TARGET; return (0); }
/* Push data from a socket down a stream ** ------------------------------------- ** ** This routine is responsible for creating and PRESENTING any ** graphic (or other) objects described by the file. ** ** The file number given is assumed to be a TELNET stream, i.e., containing ** CRLF at the end of lines which need to be stripped to LF for unix ** when the format is textual. ** ** State of socket and target stream on entry: ** socket (file_number) assumed open, ** target (sink) assumed valid. ** ** Return values: ** HT_INTERRUPTED Interruption or error after some data received. ** -2 Unexpected disconnect before any data received. ** -1 Interruption or error before any data received, or ** (UNIX) other read error before any data received, or ** download cancelled. ** HT_LOADED Normal close of socket (end of file indication ** received), or ** unexpected disconnect after some data received, or ** other read error after some data received, or ** (not UNIX) other read error before any data received. ** ** State of socket and target stream on return depends on return value: ** HT_INTERRUPTED socket still open, target aborted. ** -2 socket still open, target stream still valid. ** -1 socket still open, target aborted. ** otherwise socket closed, target stream still valid. */ PUBLIC int HTCopy ARGS4( HTParentAnchor *, anchor, int, file_number, void*, handle GCC_UNUSED, HTStream*, sink) { HTStreamClass targetClass; BOOL suppress_readprogress = NO; int bytes; int rv = 0; /* Push the data down the stream */ targetClass = *(sink->isa); /* Copy pointers to procedures */ /* Push binary from socket down sink ** ** This operation could be put into a main event loop */ HTReadProgress(bytes = 0, 0); for (;;) { int status; if (LYCancelDownload) { LYCancelDownload = FALSE; (*targetClass._abort)(sink, NULL); rv = -1; goto finished; } if (HTCheckForInterrupt()) { _HTProgress (TRANSFER_INTERRUPTED); (*targetClass._abort)(sink, NULL); if (bytes) rv = HT_INTERRUPTED; else rv = -1; goto finished; } #ifdef USE_SSL if (handle) status = SSL_read((SSL *)handle, input_buffer, INPUT_BUFFER_SIZE); else status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE); #else status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE); #endif /* USE_SSL */ if (status <= 0) { if (status == 0) { break; } else if (status == HT_INTERRUPTED) { _HTProgress (TRANSFER_INTERRUPTED); (*targetClass._abort)(sink, NULL); if (bytes) rv = HT_INTERRUPTED; else rv = -1; goto finished; } else if (SOCKET_ERRNO == ENOTCONN || #ifdef _WINDOWS /* 1997/11/10 (Mon) 16:57:18 */ SOCKET_ERRNO == ETIMEDOUT || #endif SOCKET_ERRNO == ECONNRESET || SOCKET_ERRNO == EPIPE) { /* * Arrrrgh, HTTP 0/1 compatibility problem, maybe. */ if (bytes <= 0) { /* * Don't have any data, so let the calling * function decide what to do about it. - FM */ rv = -2; goto finished; } else { #ifdef UNIX /* * Treat what we've received already as the complete * transmission, but not without giving the user * an alert. I don't know about all the different * TCP stacks for VMS etc., so this is currently * only for UNIX. - kw */ HTInetStatus("NETREAD"); HTAlert("Unexpected server disconnect."); CTRACE((tfp, "HTCopy: Unexpected server disconnect. Treating as completed.\n")); status = 0; break; #else /* !UNIX */ /* * Treat what we've gotten already * as the complete transmission. - FM */ CTRACE((tfp, "HTCopy: Unexpected server disconnect. Treating as completed.\n")); status = 0; break; #endif /* UNIX */ } #ifdef UNIX } else { /* status < 0 and other errno */ /* * Treat what we've received already as the complete * transmission, but not without giving the user * an alert. I don't know about all the different * TCP stacks for VMS etc., so this is currently * only for UNIX. - kw */ HTInetStatus("NETREAD"); HTAlert("Unexpected read error."); if (bytes) { (void)NETCLOSE(file_number); rv = HT_LOADED; } else { (*targetClass._abort)(sink, NULL); rv = -1; } goto finished; #endif } break; } /* * Suppress ReadProgress messages when collecting a redirection * message, at least initially (unless/until anchor->content_type * gets changed, probably by the MIME message parser). That way * messages put up by the HTTP module or elsewhere can linger in * the statusline for a while. - kw */ suppress_readprogress = (anchor && anchor->content_type && !strcmp(anchor->content_type, "message/x-http-redirection")); #ifdef NOT_ASCII { char * p; for (p = input_buffer; p < input_buffer+status; p++) { *p = FROMASCII(*p); } } #endif /* NOT_ASCII */ (*targetClass.put_block)(sink, input_buffer, status); bytes += status; if (!suppress_readprogress) HTReadProgress(bytes, anchor ? anchor->content_length : 0); HTDisplayPartial(); } /* next bufferload */ _HTProgress(TRANSFER_COMPLETE); (void)NETCLOSE(file_number); rv = HT_LOADED; finished: HTFinishDisplayPartial(); return(rv); }
/* 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. ** gate is nill if no gateway, else the gateway address. ** ** 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. ** */ PUBLIC int HTLoadHTTP ARGS4 (CONST char *, arg, CONST char *, gate, HTAnchor *, anAnchor, int, diag) { int s; /* Socket number for returned data */ char *command; /* The whole command */ int status; /* tcp return */ char host[256]; /* Hold on to the host */ SockA soc_address; /* Binary network address */ SockA * sin = &soc_address; if (!arg) return -3; /* Bad if no name sepcified */ if (!*arg) return -2; /* Bad if name had zero length */ /* Set up defaults: */ #ifdef DECNET sin->sdn_family = AF_DECnet; /* Family = DECnet, host order */ sin->sdn_objnum = DNP_OBJ; /* Default: http object number */ #else /* Internet */ sin->sin_family = AF_INET; /* Family = internet, host order */ sin->sin_port = htons(TCP_PORT); /* Default: http port */ #endif if (TRACE) { if (gate) fprintf(stderr, "HTTPAccess: Using gateway %s for %s\n", gate, arg); else fprintf(stderr, "HTTPAccess: Direct access for %s\n", arg); } /* Get node name and optional port number: */ { char *p1 = HTParse(gate ? gate : arg, "", PARSE_HOST); strcpy(host, p1); int status = HTParseInet(sin, p1); /* TBL 920622 */ free(p1); if (status) return status; /* No such host for example */ } /* Now, let's get a socket set up from the server for the sgml data: */ #ifdef DECNET s = socket(AF_DECnet, SOCK_STREAM, 0); #else s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); #endif status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address)); if (status < 0) { #ifndef DECNET /* This code is temporary backward-compatibility. It should go away when no server runs on port 2784 alone */ if (sin->sin_port == htons(TCP_PORT)) { /* Try the old one */ if (TRACE) printf ( "HTTP: Port %d doesn't answer (errno = %d). Trying good old port %d...\n", TCP_PORT, errno, OLD_TCP_PORT); sin->sin_port = htons(OLD_TCP_PORT); /* First close current socket and open a clean one */ status = NETCLOSE (s); s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address)); } if (status < 0) #endif { if (TRACE) fprintf(stderr, "HTTP: Unable to connect to remote host for `%s' (errno = %d).\n", arg, errno); /* free(command); BUG OUT TBL 921121 */ return HTInetStatus("connect"); } } if (TRACE) fprintf(stderr, "HTTP connected, socket %d\n", s); /* Ask that node for the document, ** omitting the host name & anchor if not gatewayed. */ if (gate) { command = malloc(4 + strlen(arg)+ 2 + 1); if (command == NULL) outofmem(__FILE__, "HTLoadHTTP"); strcpy(command, "GET "); strcat(command, arg); } else if ( HTTP11 ) { /* not gatewayed */ /* Switch this to HTTP 1.1 */ char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION); command = malloc(4 + strlen(arg) + 11 + 6 + strlen(host) + 2 + 20 + 2 + 1); if (command == NULL) outofmem(__FILE__, "HTLoadHTTP"); strcpy(command, "GET "); strcat(command, arg); strcat(command, " HTTP/1.1\r\n"); strcat(command, "Host: "); strcat(command, host); strcat(command, "\r\n"); strcat(command, "Connection: close\r\n"); } else { char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION); command = malloc(4 + strlen(p1)+ 2 + 1); if (command == NULL) outofmem(__FILE__, "HTLoadHTTP"); strcpy(command, "GET "); strcat(command, p1); free(p1); } strcat(command, "\r\n"); /* Include CR for telnet compat. */ if (TRACE) fprintf(stderr, "HTTP writing command `%s' to socket %d\n", command, s); #ifdef NOT_ASCII { char * p; for(p = command; *p; p++) { *p = TOASCII(*p); } } #endif status = NETWRITE(s, command, (int)strlen(command)); free(command); if (status<0) { if (TRACE) fprintf(stderr, "HTTPAccess: Unable to send command.\n"); return HTInetStatus("send"); } /* Skip the HTTP 1.1 headers */ if ( HTTP11 ) { char buffer[2]; int endhdr = 0; while ( 1 ) { int rc = read(s, buffer, 1) ; if ( TRACE ) fprintf(stderr,"%c",buffer[0]); if ( rc < 1 ) { if (TRACE) fprintf(stderr, "HTTPAccess: EOF reading headers.\n"); return HTInetStatus("send"); } if ( buffer[0] == '\r' && endhdr == 0 ) endhdr = 1; else if ( buffer[0] == '\n' && endhdr == 1 ) endhdr = 2; else if ( buffer[0] == '\r' && endhdr == 2 ) endhdr = 3; else if ( buffer[0] == '\n' && endhdr == 3 ) break; else endhdr = 0; } } /* Now load the data */ { char *url = HTParse(arg, "", PARSE_ACCESS | PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION); int compressed; int fmt = diag ? WWW_PLAINTEXT : HTFileFormat (url, WWW_HTML, &compressed); HTParseFormat(fmt, (HTParentAnchor *) anAnchor, s, compressed); free (url); } if (TRACE) fprintf(stderr, "HTTP: close socket %d.\n", s); status = NETCLOSE(s); return HT_LOADED; /* Good return */ }
void ClientThread(void *dummy) { THREAD FILE *logfile; THREAD stats_t timestat; THREAD rqst_timer_t timerarray[MAXNUMOFFILES]; THREAD SOCKET mastersock = BADSOCKET_VALUE; /* connection to webmaster */ THREAD page_stats_t *page_stats; /* actually a dynamic array */ int loopcnt = 0; int filecnt; int loop; int ran_number; int page_index; int page_number; int file_count = 0; char file_name[50]; struct timeval runningtime; time_t junk; int i; int returnval; /* * INITIALIZE DATA */ page_stats = (page_stats_t *)mymalloc((number_of_pages)*sizeof(page_stats_t)); for (i=0; i < number_of_pages; i++) { page_stats_init(&(page_stats[i])); } if (debug) { /* * OPEN A DEBUG FILE */ fflush(stderr); sprintf(file_name, "%s.%d", DEBUG_FILE, (int)getpid()); debugfile = fopen(file_name, "w+"); if (debugfile == 0) errexit("Can't open debug file\n"); D_PRINTF( "Running in debug mode, %d\n",amclient ); } if (record_all_transactions) { /* * OPEN A LOG FILE. */ sprintf(file_name, "%s%d", LOG_FILE, (int)getpid()); returnerr("Log file is %s\n", file_name); logfile = fopen(file_name, "w+"); } /* Initialize random number generator */ junk = getpid (); rand_r(&junk); D_PRINTF( "Random seed: %d\n", junk ); for (i=0; i < MAXNUMOFFILES; i++) { rqtimer_init(&(timerarray[i])); } stats_init(×tat); D_PRINTF( "Number of files %d\n", numfiles ); timestat.total_num_of_files = numfiles; if (amclient) { /* * WE ARE A CLIENT PROCESS. (i.e. WE ARE NOT RUN BY A USER, BUT BY * THE MASTER WWWSTONE PROCESS. WE NEED TO CONNECT TO THE * MASTER WHO WILL SYNCHRONIZE ALL THE CLIENTS. */ D_PRINTF( "Trying to connect with %s\n",connectstr ); mastersock = connecttomaster(connectstr); D_PRINTF( "connecttomaster returns %d, %s\n", mastersock, neterrstr() ); if(BADSOCKET(mastersock)) { /* * ERROR CONNECTING TO THE MASTER. ABORT. */ errexit("Error connecting to the master: %s\n", neterrstr()); } } /* END IF CLIENT */ #ifdef WIN32 /* Tell parent we're ready */ InterlockedIncrement(&CounterSemaphore); /* Wait for main() thread to release us */ WaitForSingleObject(hSemaphore, INFINITE); ReleaseSemaphore(hSemaphore, 1, 0); #endif /* WIN32 */ if (testtime != 0) { /* * IF RUNNING A TIMED TEST, WE WILL LOOP * UNTIL THE ALARM GOES OFF. * WE'LL ALSO NEED TO SET THE SIGNAL HANDLER */ #ifndef WIN32 /*signal(SIGALRM, alarmhandler);*/ /* * SEND SIGALRM IN testtime SECONDS */ /*alarm(testtime);*/ #endif /* WIN32 */ } /* * AND THEY'RE OFF... */ if (testtime) numloops = INFINITY; GETTIMEOFDAY(&(timestat.starttime), &(timestat.starttimezone)); /* LOOP UNTIL WE HIT numloops, OR WE RUN OUT OF TIME */ for(loopcnt = 0; (loopcnt < numloops) && !timeexpired; loopcnt++) { /* * THIS IS WHERE LOAD TESTING IS DONE. * GET A RANDOM NUMBER, THEN INDEX INTO THE * PAGE, AND THEN REQUEST THAT SET OF FILES. */ if (uil_filelist_f) /* HAVE FILELIST */ { D_PRINTF( "Have filelist\n" ); /* if (testtime != 0) /* RUNNING IN TIMED MODE */ if (1) { D_PRINTF( "Running in timed mode\n" ); /* random number between 0 and totalweight-1 */ junk = getpid (); ran_number = (rand_r(&junk) % total_weight); D_PRINTF( "random %ld\n", ran_number ); /* loop through pages, find correct one * while ran_number is positive, decrement it * by the load_num of the current page * example: ran_number is 5, pages have weights of 10 and 10 * first iteration page_index = 0, ran_number = -5 * iteration halted, page_index = 0 */ page_index = -1; while (ran_number >= 0) { page_index++; D_PRINTF( "Current page index %d: %ld - %d\n", page_index, ran_number, load_file_list[page_index].load_num ); ran_number -= load_file_list[page_index].load_num; } if (page_index >= number_of_pages) { page_index--; } D_PRINTF( "Final page index %d\n", page_index ); filecnt = makeload(load_file_list[page_index].num_of_files, page_index, timerarray, ×tat, mastersock, page_stats); testtime = 1; } else /* NOT RUNNING IN TIMED MODE */ { for (page_number = 0; page_number < number_of_pages; page_number++) { filecnt = makeload(load_file_list[page_number].num_of_files, page_number, timerarray, ×tat, mastersock, page_stats); } /* END for page_number */ } /* END if/else TIMED MODE */ } else /* NO FILELIST */ { D_PRINTF( "No filelist\n" ); /* * LOOP THROUGH UNTIL numfiles TIMES OR UNTIL TIMER EXPIRES * AND ALARM SETS filecnt TO INFINITY. */ /* does this still work?? */ /* filecnt = makeload(numfiles, -1, timerarray); */ } /* END if HAVE FILELIST */ if (filecnt > 0) file_count += filecnt; } /* END while loopcnt */ GETTIMEOFDAY(&(timestat.endtime), &(timestat.endtimezone)); D_PRINTF( "Test run complete\n" ); signal(SIGALRM, 0); if (testtime == 0) { numfiles = loopcnt; if (uil_filelist_f) { numfiles = file_count; } } /* This option ( "-R" ) looks broken (e.g. l > 50) -- JEF 2/15/96 */ if (record_all_transactions) { /* * DUMP THE LOG FILE INFORMATION. */ for (loop=0; loop < (loopcnt * file_count); loop++) { fprintf(logfile, " entertime \t%d.%d\n" " beforeconnect \t%d.%d\n" " afterconnect \t%d.%d\n" " beforeheader \t%d.%d\n" " afterheader \t%d.%d\n" " afterbody \t%d.%d\n" " exittime \t%d.%d\n" " total bytes \t%d\n" " body bytes\t%d\n", timerarray[loop].entertime.tv_sec, timerarray[loop].entertime.tv_usec, timerarray[loop].beforeconnect.tv_sec, timerarray[loop].beforeconnect.tv_usec, timerarray[loop].afterconnect.tv_sec, timerarray[loop].afterconnect.tv_usec, timerarray[loop].beforeheader.tv_sec, timerarray[loop].beforeheader.tv_usec, timerarray[loop].afterheader.tv_sec, timerarray[loop].afterheader.tv_usec, timerarray[loop].afterbody.tv_sec, timerarray[loop].afterbody.tv_usec, timerarray[loop].exittime.tv_sec, timerarray[loop].exittime.tv_usec, timerarray[loop].totalbytes, timerarray[loop].bodybytes); } /* end for loop */ } /* end if recording all transactions */ D_PRINTF( "total errors: %d\n",timestat.rs.totalerrs ); /* gethostname(timestat.hostname,MAXHOSTNAMELEN); */ /* D_PRINTF( "Test for host: %s\n",timestat.hostname ); */ D_PRINTF( "Server is: %s running at port number: %d\n", webserver,portnum ); /* sprintf(timestat.hostname,"%s:%d",timestat.hostname,getpid()); */ if (amclient) /* CLIENT TO A WEBMASTER */ { char *stats_as_text; /* * SEND THE TIMING DATA TO THE MASTER */ stats_as_text = stats_to_text(×tat); D_PRINTF( "stats_to_text returned %s\n", stats_as_text ); returnval = senddata(mastersock, stats_as_text, SIZEOF_STATSTEXTBASE + number_of_pages*SIZEOF_DOUBLETEXT); D_PRINTF( "Wrote time stats to master %d\n", returnval ); if (returnval < 1) { D_PRINTF( "Error while writing time stats: %s\n", neterrstr() ); errexit("Error while writing time stats: %s\n", neterrstr()); } if (uil_filelist_f) /* write pagestats */ { char *page_stats_as_text; for (i = 0; i < number_of_pages; i++) { D_PRINTF( "On page_stats[%d]\n", i ); page_stats_as_text = page_stats_to_text(&page_stats[i]); returnval = strlen(page_stats_as_text); D_PRINTF( "page_stats_to_text[%d] returned %d\n", i, returnval ); returnval = senddata(mastersock, page_stats_as_text, SIZEOF_PAGESTATSTEXT); if (returnval < 1) { D_PRINTF( "Error while writing page_stats[%d]: %s\n", i, neterrstr() ); errexit("Error while writing page_stats[%d]: %s\n", i, neterrstr()); } /* end if */ D_PRINTF( "Wrote %d bytes of page_stats[%d] to master\n", returnval, i ); } /* end for */ } /* end if filelist */ D_PRINTF( "About to close socket\n" ); if (NETCLOSE(mastersock)) D_PRINTF( "Close socket error: %s\n", neterrstr() ); } else /* NOT A CLIENT TO A WEBMASTER */ { if (testtime) { printf("Test ran for: %d minutes\n",(testtime/60)); } else { printf("Test ran for: %d iterations.\n",numloops); } compdifftime(&(timestat.endtime), &(timestat.starttime), &(runningtime)); printf("Total time of test (sec) %d.%d\n", runningtime.tv_sec, runningtime.tv_usec); printf("Files retrieved per iteration: %d\n",numfiles); /* 'per iteration' */ printf("----------------------------------\n"); printf("Totals:\n\n"); rqstat_print(&(timestat.rs)); if (timestat.rs.totalconnects == 0) goto end; printf("Thruput = %5.2lf Kbytes/sec\n", thruputpersec(timestat.rs.totalbytes, &runningtime) / 1000); if (uil_filelist_f && numloops && verbose) { for (loop = 0; loop < number_of_pages; loop++) { if (timestat.page_numbers[loop] != 0) { printf ("===============================================================================\n"); printf ("Page # %d\n\n", loop); printf ("Total number of times page was hit %d\n", page_stats[loop].totalpages); rqstat_print(&(page_stats[loop].rs)); printf ("Page size %d \n", page_stats[loop].page_size); printf ("===============================================================================\n\n"); } /* END if timestat */ } /* END for loop */ } /* END if filelist */ } /* END if client */ end: if(record_all_transactions) fclose(logfile); if(debug) { D_PRINTF( "Client exiting.\n" ); fclose(debugfile); } #ifdef WIN32 /* tell parent we're done */ InterlockedIncrement(&CounterSemaphore); #endif /* WIN32 */ } /* END ClientThread() */
/* connect to a socket given the hostname and protocol */ SOCKET connectsock(char *host, NETPORT portnum, char *protocol) { struct sockaddr_in sin; /* an Internet endpoint address */ SOCKET s; /* socket descriptor */ int type; /* socket type */ short proto; int returnval; /* temporary return value */ D_PRINTF( "Beginning connectsock; host=%s port=%d proto=%s\n", host, portnum, protocol ); sin.sin_family = AF_INET; memset((char *)&sin, 0, sizeof(sin)); D_PRINTF( "Zeroed address structure\n" ); sin.sin_port = htons(portnum); D_PRINTF( "Set port number %d\n", portnum ); /* get the contact information */ if (strcmp(host, webserver) == 0) { sin.sin_addr.S_ADDR = webserv_addr; sin.sin_family = PF_INET; proto = webserv_ppe.p_proto; type = webserv_type; } else if (strcmp(host, webmaster) == 0) { sin.sin_addr.S_ADDR = webmast_addr; sin.sin_family = PF_INET; proto = webmast_ppe.p_proto; type = webmast_type; } else { struct hostent host_phe; struct protoent host_ppe; unsigned long host_addr; short host_type; /* socket type */ if (resolve_addrs(host, "tcp", &host_phe, &host_ppe, &host_addr, &host_type)) return returnerr("Can't resolve hostname %s in get()\n", host); sin.sin_addr.S_ADDR = host_addr; sin.sin_family = PF_INET; proto = host_ppe.p_proto; type = host_type; } /* Allocate a socket */ s = socket(PF_INET, type, proto); D_PRINTF( "Socket %d returned %d, %s\n", type, s, neterrstr() ); if (BADSOCKET(s)) { D_PRINTF( "Can't create socket: %s\n",neterrstr() ); return BADSOCKET_VALUE; } /* Connect the socket */ D_PRINTF( "Trying to connect %d with size %d, %s\n", s, sizeof(sin), neterrstr() ); D_PRINTF( "Address is family %d, port %d, addr %s\n", sin.sin_family, ntohs(sin.sin_port), inet_ntoa(sin.sin_addr) ); returnval = connect(s, (struct sockaddr *)&sin, sizeof(sin)); D_PRINTF( "Connect returned %d, %s\n", returnval, neterrstr() ); if (returnval < 0) { D_PRINTF( "Can't connect: %s\n", neterrstr() ); NETCLOSE(s); return BADSOCKET_VALUE; } /* all done, returning socket descriptor */ D_PRINTF( "Returning %d from connectsock call\n", s ); return(s); } /* END connectsock() */
PUBLIC int HTLoadHTTP ARGS4 ( char *, arg, HTParentAnchor *, anAnchor, HTFormat, format_out, HTStream*, sink) { int s; /* Socket number for returned data */ char *command; /* 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 */ char *line_buffer; char *line_kept_clean; BOOL extensions; /* Assume good HTTP server */ int compressed; char line[2048]; /* bumped up to cover Kerb huge headers */ int length, doing_redirect, rv; int already_retrying = 0; int return_nothing; int i; int keepingalive = 0; char *p; /*SWP*/ int statusError=0; if (!arg) { status = -3; HTProgress ("Bad request."); goto done; } if (!*arg) { status = -2; HTProgress ("Bad request."); goto done; } 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; bytes_already_read = 0; length = 0; doing_redirect = 0; compressed = 0; target = NULL; line_buffer = NULL; line_kept_clean = NULL; return_nothing = 0; /* okay... addr looks like http://hagbard.ncsa.uiuc.edu/blah/etc.html lets crop it at the 3rd '/' */ for(p = arg,i=0;*p && i!=3;p++) if(*p=='/') i++; if(i==3) i = p-arg; /* i = length not counting last '/' */ else i = 0; if((lsocket != -1) && i && addr && !strncmp(addr,arg,i)){ /* keepalive is active and addresses match -- try the old socket */ s = lsocket; keepingalive = 1; /* flag in case of network error due to server timeout*/ lsocket = -1; /* prevent looping on failure */ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Keep-Alive reusing '%s'\n",addr); #endif /* if (addr && *addr) { sprintf(tmpbuf,"Reusing socket from %s.",addr); HTProgress(tmpbuf); } */ } else { if(addr) free(addr); /* save the address for next time around */ addr = malloc(i+1); strncpy(addr,arg,i); *(addr+i)=0; keepingalive = 0; /* just normal opening of the socket */ if(lsocket != -1) NETCLOSE(lsocket); /* no socket leaks here */ lsocket = -1; /*dont assign until we know the server says okay */ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Keep-Alive saving '%s'\n",addr); #endif /* if (addr && *addr) { sprintf(tmpbuf,"Saving %s for possible socket reuse.",addr); HTProgress(tmpbuf); } */ } if (!keepingalive) { status = HTDoConnect (arg, "HTTP", TCP_PORT, &s); if (status == HT_INTERRUPTED){ /* Interrupt cleanly. */ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Interrupted on connect; recovering cleanly.\n"); #endif HTProgress ("Connection interrupted."); /* status already == HT_INTERRUPTED */ goto done; } if (status < 0) { #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "HTTP: Unable to connect to remote host for `%s' (errno = %d).\n", arg, errno); #endif HTProgress ("Unable to connect to remote host."); status = HT_NO_DATA; goto done; } } /* Ask that node for the document, ** omitting the host name & anchor */ { char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION); command = malloc(5 + strlen(p1)+ 2 + 31); if (do_post && !do_put) strcpy(command, "POST "); else if (do_post && do_put) strcpy(command, "PUT "); else if (do_head) strcpy(command, "HEAD "); else if (do_meta) strcpy(command, "META "); else strcpy(command, "GET "); /* * For a gateway, the beginning '/' on the request must * be stripped before appending to the gateway address. */ if ((using_gateway)||(using_proxy)) { strcat(command, p1+1); } else strcat(command, p1); free(p1); } if (extensions) { strcat(command, " "); strcat(command, HTTP_VERSION); } strcat(command, crlf); /* CR LF, as in rfc 977 */ if (extensions) { #ifdef SAM_NOT_YET /* SAM This produces an absolutely huge Accept: line. While there is probably a better way to turn this off, just compiling it out works for now. */ int n, i; if (!HTPresentations) HTFormatInit(); n = HTList_count(HTPresentations); begin_ptr=command+strlen(command); env_length=0; sprintf(line, "Accept:"); env_length+=strlen(line); StrAllocCat(command, line); /* KCMS Accept Header - swp */ if (KCMS_Return_Format==JPEG) { sprintf(line," image/x-pcd-jpeg,"); StrAllocCat(command, line); env_length+=strlen(line); } else if (KCMS_Return_Format==JYCC) { sprintf(line," image/x-pcd-jycc,"); StrAllocCat(command, line); env_length+=strlen(line); } else if (KCMS_Return_Format==GIF) { sprintf(line," image/x-pcd-gif,"); StrAllocCat(command, line); env_length+=strlen(line); } for(i=0; i<n; i++) { HTPresentation * pres = HTList_objectAt(HTPresentations, i); if (pres->rep_out == WWW_PRESENT) { sprintf(line, " %s,",HTAtom_name(pres->rep)); env_length+=strlen(line); StrAllocCat(command, line); if (env_length>200) { if ((tmp_ptr=strrchr(command,','))!=NULL) { *tmp_ptr='\0'; } sprintf(line, "%c%c",CR,LF); StrAllocCat(command, line); begin_ptr=command+strlen(command); sprintf(line, "Accept:"); env_length=strlen(line); StrAllocCat(command, line); } } } /* This gets rid of the last comma. */ if ((tmp_ptr=strrchr(command,','))!=NULL) { *tmp_ptr='\0'; sprintf(line, "%c%c",CR,LF); StrAllocCat(command, line); } else { /* No accept stuff...get rid of "Accept:" */ begin_ptr='\0'; } #endif /* if reloading, send no-cache pragma to proxy servers. --swp */ /* original patch from Ari L. <*****@*****.**> */ if (reloading) { sprintf(line, "Pragma: no-cache%c%c", CR, LF); StrAllocCat(command, line); } /*This is just used for "not" sending this header on a proxy request*/ if (useKeepAlive) { sprintf(line, "Connection: keep-alive%c%c", CR, LF); StrAllocCat(command, line); } if (sendAgent) { sprintf(line, "User-Agent: %s%c%c",agent[selectedAgent],CR,LF); /* sprintf(line, "User-Agent: %s/%s libwww/%s%c%c", HTAppName ? HTAppName : "unknown", HTAppVersion ? HTAppVersion : "0.0", HTLibraryVersion, CR, LF); */ StrAllocCat(command, line); } if (sendReferer) { /* HTTP Referer field, specifies back-link URL - amb */ if (HTReferer) { sprintf(line, "Referer: %s%c%c", HTReferer, CR, LF); StrAllocCat(command, line); HTReferer = NULL; } } { char *tmp,*startPtr,*endPtr; /* addr is always in URL form */ if (addr && !using_proxy && !using_gateway) { tmp=strdup(addr); startPtr=strchr(tmp,'/'); if (startPtr) { startPtr+=2; /*now at begining of hostname*/ if (*startPtr) { endPtr=strchr(startPtr,':'); if (!endPtr) { endPtr=strchr(startPtr,'/'); if (endPtr && *endPtr) { *endPtr='\0'; } } else { *endPtr='\0'; } sprintf(line, "Host: %s%c%c", startPtr, CR, LF); StrAllocCat(command, line); free(tmp); tmp=startPtr=endPtr=NULL; } } } else if (using_proxy || using_gateway) { sprintf(line, "Host: %s%c%c", proxy_host_fix, CR, LF); StrAllocCat(command, line); } } /* SWP -- 7/10/95 */ /* HTTP Extension headers */ /* Domain Restriction */ sprintf(line,"Extension: Notify-Domain-Restriction%c%c",CR,LF); StrAllocCat(command,line); /* BJS -- 12/05/95 -- allow arbitrary headers sent from browser */ if(extra_headers){ int h; for(h=0;extra_headers[h];h++){ sprintf(line,"%s%c%c",extra_headers[h],CR,LF); StrAllocCat(command,line); } } { char *docname; char *hostname; char *colon; int portnumber; char *auth; docname = HTParse(arg, "", PARSE_PATH); hostname = HTParse(arg, "", PARSE_HOST); if (hostname && NULL != (colon = strchr(hostname, ':'))) { *(colon++) = '\0'; /* Chop off port number */ portnumber = atoi(colon); } else portnumber = 80; if (NULL!=(auth=HTAA_composeAuth(hostname, portnumber, docname))) { sprintf(line, "%s%c%c", auth, CR, LF); StrAllocCat(command, line); } #ifndef DISABLE_TRACE if (www2Trace) { if (auth) fprintf(stderr, "HTTP: Sending authorization: %s\n", auth); else fprintf(stderr, "HTTP: Not sending authorization (yet)\n"); } #endif FREE(hostname); FREE(docname); } } if (do_post && !do_put) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Doing post, content-type '%s'\n", post_content_type); #endif sprintf (line, "Content-type: %s%c%c", post_content_type ? post_content_type : "lose", CR, LF); StrAllocCat(command, line); { int content_length; if (!post_data) content_length = 4; /* 4 == "lose" :-) */ else content_length = strlen (post_data); sprintf (line, "Content-length: %d%c%c", content_length, CR, LF); StrAllocCat(command, line); } StrAllocCat(command, crlf); /* Blank line means "end" */ if (post_data) StrAllocCat(command, post_data); else StrAllocCat(command, "lose"); } else if (do_post && do_put) { sprintf (line, "Content-length: %d%c%c", put_file_size, CR, LF); StrAllocCat(command, line); StrAllocCat(command, crlf); /* Blank line means "end" */ } else { StrAllocCat(command, crlf); /* Blank line means "end" */ } #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "Writing:\n%s----------------------------------\n", command); #endif /* HTProgress ("Sending HTTP request."); */ status = NETWRITE(s, command, (int)strlen(command)); if (do_post && do_put) { char buf[BUFSIZ]; int upcnt=0,n; while (status>0) { n=fread(buf,1,BUFSIZ-1,put_fp); upcnt+= status = NETWRITE(s, buf, n); #ifndef DISABLE_TRACE if (www2Trace) { fprintf(stderr,"[%d](%d) %s",status,n,buf); } #endif if (feof(put_fp)) { break; } } if (status<0 || !feof(put_fp) || upcnt!=put_file_size) { char tmpbuf[BUFSIZ]; sprintf(tmpbuf,"Status: %d -- EOF: %d -- UpCnt/FileSize: %d/%d\n\nThe server you connected to either does not support\nthe PUT method, or an error occurred.\n\nYour upload was corrupted! Please try again!",status,(feof(put_fp)?1:0),upcnt,put_file_size); application_error(tmpbuf,"Upload Error!"); } } /* Twirl on each request to make things look nicer -- SWP */ HTCheckActiveIcon(1); #ifndef DISABLE_TRACE if (httpTrace) { fprintf(stderr,"%s",command); } #endif free (command); if (status <= 0) { if (status == 0) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Got status 0 in initial write\n"); #endif /* Do nothing. */ } else if ((errno == ENOTCONN || errno == ECONNRESET || errno == EPIPE) && !already_retrying && /* Don't retry if we're posting. */ !do_post) { /* Arrrrgh, HTTP 0/1 compability problem, maybe. */ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: BONZO ON WRITE Trying again with HTTP0 request.\n"); #endif /* HTProgress ("Retrying as HTTP0 request."); */ NETCLOSE(s); // SAM extensions = NO; already_retrying = 1; goto try_again; } else { if(keepingalive){ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Timeout on Keep-Alive. Retrying.\n"); #endif HTProgress("Server Timeout: Reconnecting"); goto try_again; } #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Hit unexpected network WRITE error; aborting connection.\n"); #endif NETCLOSE (s); status = -1; HTProgress ("Unexpected network write error; connection aborted."); goto done; } } #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: WRITE delivered OK\n"); #endif HTProgress ("Done sending HTTP request; 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 = (char *) malloc(buffer_length * sizeof(char)); 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)); } #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Trying to read %d\n", buffer_length - length - 1); #endif status = NETREAD(s, line_buffer + length, buffer_length - length - 1); #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Read %d\n", status); #endif if (status <= 0) { /* Retry if we get nothing back too; bomb out if we get nothing twice. */ if (status == HT_INTERRUPTED) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Interrupted initial read.\n"); #endif HTProgress ("Connection interrupted."); status = HT_INTERRUPTED; NETCLOSE (s); goto clean_up; } else if (status < 0 && (errno == ENOTCONN || errno == ECONNRESET || errno == EPIPE) && !already_retrying && !do_post) { /* Arrrrgh, HTTP 0/1 compability problem, maybe. */ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: BONZO Trying again with HTTP0 request.\n"); #endif NETCLOSE(s); if (line_buffer) free(line_buffer); if (line_kept_clean) free(line_kept_clean); extensions = NO; already_retrying = 1; HTProgress ("Retrying as HTTP0 request."); goto try_again; } else { if(keepingalive){ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Timeout on Keep-Alive. Retrying.\n"); #endif HTProgress("Server Timeout: Reconnecting"); goto try_again; } #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Hit unexpected network read error; aborting connection; status %d.\n", status); #endif HTProgress ("Unexpected network read error; connection aborted."); NETCLOSE (s); status = -1; goto clean_up; } } bytes_already_read += status; { char line[256]; sprintf (line, "Read %d bytes of data.", bytes_already_read); HTProgress (line); } if (status == 0) { end_of_file = YES; break; } line_buffer[length+status] = 0; if (line_buffer) { if (line_kept_clean) free (line_kept_clean); line_kept_clean = (char *)malloc (buffer_length * sizeof (char)); /* bcopy (line_buffer, line_kept_clean, buffer_length); */ memcpy (line_kept_clean, line_buffer, buffer_length); } 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 */ /* We now have a terminated unfolded line. Parse it. ** ------------------------------------------------- */ #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "HTTP: Rx: %s\n", line_buffer); #endif { int fields; char server_version[VERSION_LENGTH+1]; int server_status; /*SWP*/ statusError=0; server_version[0] = 0; fields = sscanf(line_buffer, "%20s %d", server_version, &server_status); #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Scanned %d fields from line_buffer\n", fields); #endif #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: line_buffer '%s'\n", line_buffer); #endif /* Rule out HTTP/1.0 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] != '.') { /* HTTP0 reply */ HTAtom * encoding; #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "--- Talking HTTP0.\n"); #endif format_in = HTFileFormat(arg, &encoding, WWW_HTML, &compressed); start_of_data = line_kept_clean; } else { /* Decode full HTTP response */ format_in = HTAtom_for("www/mime"); /* 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 in this situation. */ start_of_data = eol ? eol + 1 : ""; length = eol ? length - (start_of_data - line_buffer) : 0; #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "--- Talking HTTP1.\n"); #endif switch (server_status / 100) { case 3: /* Various forms of redirection */ /* We now support this in the parser, at least. */ doing_redirect = 1; break; case 4: /* "I think I goofed" */ switch (server_status) { case 403: statusError=1; /* 403 is "forbidden"; display returned text. */ break; case 401: /* length -= start_of_data - text_buffer; */ if (HTAA_shouldRetryWithAuth(start_of_data, length, s)) { (void)NETCLOSE(s); lsocket = -1; if (line_buffer) free(line_buffer); if (line_kept_clean) free(line_kept_clean); #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "%s %d %s\n", "HTTP: close socket", s, "to retry with Access Authorization"); #endif HTProgress ("Retrying with access authorization information."); goto try_again; break; } else { statusError=1; /* Fall through. */ } default: statusError=1; break; } /* case 4 switch */ break; case 5: /* I think you goofed */ statusError=1; break; case 2: /* Good: Got MIME object */ switch (server_status) { case 204: return_nothing = 1; format_in = HTAtom_for("text/html"); break; case 200: if (do_head) { if (!start_of_data || !*start_of_data) { headData=NULL; } else { char *ptr; headData=strdup(start_of_data); ptr=strchr(headData,'\n'); *ptr='\0'; } } break; default: break; } break; default: /* bad number */ statusError=1; HTAlert("Unknown status reply from server!"); break; } /* Switch on server_status/100 */ } /* Full HTTP reply */ } /* scope of fields */ /* Set up the stream stack to handle the body of the message */ target = HTStreamStack(format_in, format_out, compressed, sink, anAnchor); if (!target) { char buffer[1024]; /* @@@@@@@@ */ sprintf(buffer, "Sorry, no known way of converting %s to %s.", HTAtom_name(format_in), HTAtom_name(format_out)); HTProgress (buffer); status = -1; NETCLOSE (s); lsocket = -1; goto clean_up; } if (!return_nothing) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Doing put_block, '%s'\n", start_of_data); #endif /* BJS: parse start_of_data...*/ for(p=start_of_data;*p;p++){ /* if(*p=='C' && !strncmp("Content-length: ",p,16)){ i = 0; p+=16; while(*p && isdigit(*p)){ i = i*10 + *p-'0'; p++; } p--; #ifndef DISABLE_TRACE if(www2Trace) fprintf(stderr, "HTTP: Content Length is %d\n",i); #endif } */ if(*p=='K' && !strncmp("Keep-Alive:",p,11)){ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Server Agrees to Keep-Alive\n"); #endif lsocket = s; p+=10; } } #ifndef DISABLE_TRACE if (www2Trace && lsocket == -1) fprintf (stderr, "HTTP: Server does not agree to Keep-Alive\n"); #endif /* Recycle the first chunk of data, in all cases. */ (*target->isa->put_block)(target, start_of_data, length); /* Go pull the bulk of the data down. */ /* if we dont use length, header length is wrong due to the discarded first line - bjs*/ rv = HTCopy(s, target, length /*bytes_already_read*/); if (rv == -1) { (*target->isa->handle_interrupt) (target); status = HT_INTERRUPTED; NETCLOSE (s); lsocket = -1; goto clean_up; } if (rv == -2 && !already_retrying && !do_post) { /* Aw hell. */ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Trying again with HTTP0 request.\n"); #endif /* May as well consider it an interrupt -- right? */ (*target->isa->handle_interrupt) (target); NETCLOSE(s); if (line_buffer) free(line_buffer); if (line_kept_clean) free(line_kept_clean); extensions = NO; already_retrying = 1; HTProgress ("Retrying as HTTP0 request."); goto try_again; } } else { /* return_nothing is high. */ (*target->isa->put_string) (target, "<mosaic-access-override>\n"); HTProgress ("And silence filled the night."); } (*target->isa->end_document)(target); /* Close socket before doing free. */ if(lsocket == -1){ NETCLOSE(s); #ifndef DISABLE_TRACE if(www2Trace) fprintf(stderr,"HTTP: Closing connection\n"); #endif } else { HTProgress("Leaving Server Connection Open"); #ifndef DISABLE_TRACE if(www2Trace) fprintf(stderr,"HTTP: Keeping connection alive\n"); #endif } /* else { NETCLOSE(s); #ifndef DISABLE_TRACE if(www2Trace) fprintf(stderr,"HTTP: Closing connection\n"); #endif } */ (*target->isa->free)(target); if (doing_redirect) { /* OK, now we've got the redirection URL temporarily stored in external variable redirecting_url, exported from HTMIME.c, since there's no straightforward way to do this in the library currently. Do the right thing. */ status = HT_REDIRECTING; } else { status = HT_LOADED; } /* Clean up */ clean_up: if (line_buffer) free(line_buffer); if (line_kept_clean) free(line_kept_clean); done: /* Clear out on exit, just in case. */ do_post = 0; if (statusError) { securityType=HTAA_NONE; #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr,"Resetting security type to NONE.\n"); #endif } return status; }
PRIVATE int server_loop() #endif { int tcp_status; /* <0 if error, in general */ int timeout = -1; /* No timeout required but code exists */ for(;;) { /* If it's a master socket, then find a slave: */ if (role == master) { #ifdef SELECT fd_set read_chans; fd_set write_chans; fd_set except_chans; int nfound; /* Number of ready channels */ struct timeval max_wait; /* timeout in form for select() */ FD_ZERO(&write_chans); /* Clear the write mask */ FD_ZERO(&except_chans); /* Clear the exception mask */ /* If timeout is required, the timeout structure is set up. Otherwise ** (timeout<0) a zero is passed instead of a pointer to the struct timeval. */ if (timeout>=0) { max_wait.tv_sec = timeout/100; max_wait.tv_usec = (timeout%100)*10000; } for (com_soc=(-1); com_soc<0;) { /* Loop while connections keep coming */ /* The read mask expresses interest in the master channel for incoming ** connections) or any slave channel (for incoming messages). */ /* Wait for incoming connection or message */ read_chans = open_sockets; /* Read on all active channels */ if (TRACE) printf( "Daemon: Waiting for connection or message. (Mask=%x hex, max=%x hex).\n", *(int *)(&read_chans), num_sockets); nfound=select(num_sockets, &read_chans, &write_chans, &except_chans, timeout >= 0 ? &max_wait : 0); if (nfound<0) return HTInetStatus("select()"); if (nfound==0) return 0; /* Timeout */ /* We give priority to existing connected customers. When there are ** no outstanding commands from them, we look for new customers. */ /* If a message has arrived on one of the channels, take that channel: */ { int i; for(i=0; i<num_sockets; i++) if (i != master_soc) if (FD_ISSET(i, &read_chans)) { if (TRACE) printf( "Message waiting on socket %d\n", i); com_soc = i; /* Got one! */ break; } if (com_soc>=0) break; /* Found input socket */ } /* block */ /* If an incoming connection has arrived, accept the new socket: */ if (FD_ISSET(master_soc, &read_chans)) { CTRACE(tfp, "Daemon: New incoming connection:\n"); tcp_status = accept(master_soc, (struct sockaddr *)&soc_address, &soc_addrlen); if (tcp_status<0) return HTInetStatus("accept"); CTRACE(tfp, "Daemon: Accepted new socket %d\n", tcp_status); FD_SET(tcp_status, &open_sockets); if ((tcp_status+1) > num_sockets) num_sockets=tcp_status+1; } /* end if new connection */ } /* loop on event */ #else /* SELECT not supported */ if (com_soc<0) { /* No slaves: must accept */ CTRACE(tfp, "Daemon: Waiting for incoming connection...\n"); tcp_status = accept(master_soc, &rsoc->mdp.soc_tcp.soc_address, &rsoc->mdp.soc_tcp.soc_addrlen); if (tcp_status<0) return HTInetStatus("accept"); com_soc = tcp_status; /* socket number */ CTRACE(tfp, "Daemon: Accepted socket %d\n", tcp_status); } /* end if no slaves */ #endif } /* end if master */ /* com_soc is now valid for read */ { struct sockaddr_in addr; int namelen = sizeof(addr); getpeername(com_soc, (struct sockaddr*)&addr, &namelen); strncpy(HTClientHost, inet_ntoa(addr.sin_addr), sizeof(HTClientHost)); } /* Read the message now on whatever channel there is: */ CTRACE(tfp,"Daemon: Reading socket %d from host %s\n", com_soc, HTClientHost); tcp_status=HTHandle(com_soc); if(tcp_status<=0) { /* EOF or error */ if (tcp_status<0) { /* error */ CTRACE(tfp, "Daemon: Error %d handling incoming message (errno=%d).\n", tcp_status, errno); /* DONT return HTInetStatus("netread"); error */ } else { CTRACE(tfp, "Daemon: Socket %d disconnected by peer\n", com_soc); } if (role==master) { NETCLOSE(com_soc); FD_CLR(com_soc, &open_sockets); } else { /* Not multiclient mode */ #ifdef VM return -69; #else return -ECONNRESET; #endif } } else {/* end if handler left socket open */ NETCLOSE(com_soc); FD_CLR(com_soc, &open_sockets); } }; /* for loop */ /*NOTREACHED*/ } /* end server_loop */
/**************************************************************************** * name: HtLoadHTTPANN (PRIVATE) * purpose: Issue a command to a group annotation server. * inputs: * - char *arg: * - char *data: * - int len: * - char *com: * returns: * * remarks: * ****************************************************************************/ static int HtLoadHTTPANN(char *arg, char *data, int len, char *com) { int s; /* Socket number for returned data */ char *command; /* The whole command */ int status; /* tcp return */ SockA soc_address; /* Binary network address */ SockA *sin = &soc_address; char *tptr; int fmt, compressed; int command_len; HTParentAnchor *anchor; /* * Set up defaults: */ #ifdef DECNET sin->sdn_family = AF_DECnet; /* Family = DECnet, host order */ sin->sdn_objnum = DNP_OBJ; /* Default: http object number */ #else /* Internet */ sin->sin_family = AF_INET; /* Family = internet, host order */ sin->sin_port = htons(TCP_PORT); /* Default: http port */ #endif tptr = HTParse(arg, "", PARSE_HOST); status = HTParseInet(sin, tptr); free(tptr); if (status) { return(status); } /* * Now, let's get a socket set up from the server for the data. */ #ifdef DECNET s = socket(AF_DECnet, SOCK_STREAM, 0); #else s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); #endif status = connect (s, (struct sockaddr*)&soc_address,sizeof(soc_address)); if (status < 0) { return(HTInetStatus("connect")); } /* If there's an anchor at this point, leave it in. */ tptr = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION|PARSE_ANCHOR); #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr, "HTParse(%s) returns:\n\t(%s)\n", arg, tptr); } #endif command_len = strlen(com) + strlen(tptr); command = malloc(command_len + len + 1); if (command == NULL) outofmem(__FILE__, "HTLoadHTTP"); strcpy(command, com); strcat(command, tptr); if (len != 0) { char *bptr; bptr = (char *)(command + command_len); /* bcopy(data, bptr, len);*/ memcpy(bptr, data, len); command_len += len; } else { command_len++; } free(tptr); status = NETWRITE(s, command, command_len); free(command); if (status < 0) { return(HTInetStatus("send")); } tptr = HTParse(arg, "", PARSE_ACCESS | PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION); #if 0 /* fmt = HTFileFormat (tptr, WWW_HTML, &compressed); */ fmt = WWW_HTML; anchor = HTAnchor_parent(HTAnchor_findAddress(arg)); HTParseFormat(fmt, anchor, s, 0); #endif free(tptr); status = NETCLOSE(s); return(HT_LOADED); }