/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include "mkutils.h" #include "mktcp.h" #include "mkparse.h" #include "prerror.h" #include "mkgeturl.h" /* for error codes */ #include "fe_proto.h" /* for externs */ #include "merrors.h" #include "ssl.h" extern int MK_HTTP_TYPE_CONFLICT; extern int XP_ERRNO_EWOULDBLOCK; #ifdef XP_UNIX /* #### WARNING, this is duplicated in mkconect.c */ # include /* * mcom_db.h is only included here to set BYTE_ORDER !!! * MAXDNAME is pilfered right out of arpa/nameser.h. */ # include "mcom_db.h" # if defined(__hpux) || defined(_HPUX_SOURCE) # define BYTE_ORDER BIG_ENDIAN # define MAXDNAME 256 /* maximum domain name */ # else # include # endif #include #if !defined(__osf__) && !defined(AIXV3) && !defined(_HPUX_SOURCE) && !defined(__386BSD__) && !defined(__linux) && !defined(SCO_SV) && !defined(DGUX) #include #endif #endif /* XP_UNIX */ #ifdef PROFILE #pragma profile on #endif /* Global TCP Read/Write variables */ PUBLIC char * NET_Socket_Buffer=0; PUBLIC int NET_Socket_Buffer_Size=0; #if defined(XP_UNIX) && defined(XP_BSD_UNIX) PRIVATE char * net_real_socket_buffer_ptr=0; #include #include typedef unsigned int intptr_t; char *GetPageAlignedBuffer(int size) { char *rv; static int pageSize = 0; if (pageSize == 0) { /* Cache this value to avoid syscall next time */ pageSize = getpagesize(); } net_real_socket_buffer_ptr = 0; /* Allocate too much memory */ rv = (char *) PR_Malloc(size + pageSize - 1); if (rv) { intptr_t r = (intptr_t) rv; intptr_t offset = r & (pageSize - 1); net_real_socket_buffer_ptr = rv; if (offset) { /* Have to round up address */ r = r + pageSize - offset; } else { /* ** Could be generous here and realloc to shrink since we ** don't need the extra space... */ } rv = (char*) r; } return rv; } #endif /* XP_UNIX */ /* allocate memory for the TCP socket * buffer */ PUBLIC int NET_ChangeSocketBufferSize (int size) { NET_Socket_Buffer_Size = 0; #if !defined(XP_MAC) if(size < 1) size = 10*1024; /* default */ if(size > 31*1024) size = 31*1024; #else /* On the Mac we want a biiiig buffer */ if (size < 1 || size > 64*1024) size = 64*1024; #endif /* XP_MAC */ #if defined(XP_UNIX) && defined(XP_BSD_UNIX) FREEIF(net_real_socket_buffer_ptr); NET_Socket_Buffer = GetPageAlignedBuffer(size); #else FREEIF(NET_Socket_Buffer); NET_Socket_Buffer = (char *) PR_Malloc(size); #endif /* XP_UNIX */ if(!NET_Socket_Buffer) return(0); /* else */ NET_Socket_Buffer_Size = size; return(1); } /* this is a very standard network write routine. * * the only thing special about it is that * it returns MK_HTTP_TYPE_CONFLICT on special error codes * */ PUBLIC int NET_HTTPNetWrite (PRFileDesc *fildes, CONST void * buf, unsigned int nbyte) { static int status; status = (int) NET_BlockingWrite(fildes, buf, nbyte); #ifdef XP_UNIX /* these status codes only work on UNIX */ if ((status < 0) && (status == PR_NOT_CONNECTED_ERROR || status == PR_CONNECT_RESET_ERROR || status == PR_PIPE_ERROR)) return MK_HTTP_TYPE_CONFLICT; #endif /* XP_UNIX */ /* else */ return(status); } PUBLIC int32 NET_BlockingWrite (PRFileDesc *filedes, CONST void * buf, unsigned int nbyte) { int32 length_written = 1; unsigned int tot_len_written = 0; while(nbyte > 0 && length_written > -1) { length_written = PR_Write(filedes, (const char*) buf+tot_len_written, nbyte); if(length_written > -1) { nbyte -= (unsigned int) length_written; tot_len_written += (unsigned int) length_written; } else { int rv = PR_GetError(); if(rv == PR_WOULD_BLOCK_ERROR) { #if defined(XP_WIN) && defined(MOZILLA_CLIENT) FEU_StayingAlive(); #endif length_written = 1; /* this will let it continue looping */ } else { return (rv < 0) ? rv : (-rv); } } } return(length_written); /* postive or negative */ } #ifdef DEBUG /* only a debugging routine! * Prints the output to stderr as well as the socket */ MODULE_PRIVATE int NET_DebugNetWrite (PRFileDesc *fildes, CONST void *buf, unsigned nbyte) { if(MKLib_trace_flag && nbyte > 0) { #ifdef XP_UNIX write(2, "Tx: ", 4); write(2, buf, nbyte); write(2, "\n", 1); #endif } return(PR_Write(fildes, buf, nbyte)); } /* This is a pretty standard read routine for debugging * * It prints whatever it reads to stderr for debugging purposes */ MODULE_PRIVATE int NET_DebugNetRead (PRFileDesc *fildes, void * buf, unsigned nbyte) { static int status; /* read return code */ status = PR_Read (fildes, buf, nbyte); if(MKLib_trace_flag && status != PR_SUCCESS) { #ifdef XP_UNIX write(2,"Rx: ", 4); write(2, (const char *)buf, status); write(2, "\n", 1); #endif } return(status); } #endif /* DEBUG */ /* This is a pretty standard read routine * * The only special thing that is does is * that it checks for the special errors encountered * in a HTTP 1/.9 conflict and returns MK_HTTP_TYPE_CONFLICT * when encountered */ /* fix Mac warning of missing prototype */ MODULE_PRIVATE int NET_HTTPNetRead (PRFileDesc *fildes, void *buf, unsigned nbyte); MODULE_PRIVATE int NET_HTTPNetRead (PRFileDesc *fildes, void *buf, unsigned nbyte) { static int status; /* read return code */ status = PR_Read (fildes, buf, nbyte); #ifdef XP_UNIX /* check for HTTP server type conflict */ if (status == ENOTCONN || status == ECONNRESET || status == EPIPE) return MK_HTTP_TYPE_CONFLICT; #endif /* XP_UNIX */ /* else */ return(status); } /* net_BufferedReadLine * * will do a single read on the passed in socket * and try and grok a single line from it. * * a '\n' is the demarkation of the end of a line * * if a '\n' exists the line will be returned into 'line' as * a separately malloc'd line. Any extra data * will be malloc'd into the passed in 'buffer' pointer. * * if a '\n' is not found the data read from the * socket will be appended (realloc'd) to the end of * the passed in buffer. * * the status of the socket read will be passed back * as a return value * * if 'line' is non-zero then a line was available and * was malloc'd */ #define LINE_BUFFER_SIZE 1024 MODULE_PRIVATE int NET_BufferedReadLine (PRFileDesc * sock, char ** line, char ** buffer, int32 * buffer_size, Bool * pause_for_next_read) { char *strptr, *linefeed=0; int status=1; static char line_buffer[LINE_BUFFER_SIZE]; /* maybe this should be static? */ int line_length; char *far_end; *line = 0; /* init */ *pause_for_next_read = TRUE; /* This is the default it may change */ /* scan for line in existing buffer */ if(*buffer_size > 0) { for(strptr = *buffer; strptr < *buffer+*buffer_size; strptr++) if(*strptr == LF) { linefeed = strptr; break; } } if(!linefeed) { /* get some more data from the socket */ int32 read_size = MIN(LINE_BUFFER_SIZE, NET_Socket_Buffer_Size); status = PR_Read(sock, NET_Socket_Buffer, read_size); TRACEMSG(("Read %d bytes from socket %d", status, sock)); if(status < 0) { int rv = PR_GetError(); if (rv == PR_WOULD_BLOCK_ERROR) { /* defaults to *pause_for_next_read = TRUE; */ return(1); } *pause_for_next_read = FALSE; return (rv < 0) ? rv : (-rv); } TRACEMSG(("Read %d bytes from socket\n",status)); if(status > 0) { BlockAllocCat(*buffer, *buffer_size, NET_Socket_Buffer, status); *buffer_size += status; } if(*buffer_size > 0) { for(strptr = *buffer; strptr < *buffer+*buffer_size; strptr++) if(*strptr == LF) { linefeed = strptr; break; } } } if(linefeed) { int32 tot_buf_size; *linefeed = '\0'; /* kill the '\r' if it exits */ if(linefeed > *buffer && *(linefeed-1) == '\r') *(linefeed-1) = '\0'; /* the number of bytes that the line tekes up */ line_length = (linefeed+1) - *buffer; /* the farthest end of the memory buffer */ far_end = *buffer+*buffer_size; if(line_length == *buffer_size) { /* the line is the whole buffer * no copying is required and we know that * there can't be any new \n's in the buffer * so lets return now */ *buffer_size = 0; *line = *buffer; return(status); } /* set the line pointer now since we know where it * will end up */ *line = far_end-line_length; /* I'm doing some optimization here to try and reduce malloc's * I want to copy the line that the calling function needs * to the end of the buffer and move the part of the buffer * that needs saveing to the beginning of the buffer * that way no mallocs are required. Space will be compacted * or enlarged by the AllocCat above. * * If the line_buffer isn't large enough to hold the whole * line then we need to do the copy in segments and shift * the contents of the remaining line segment and buffer * to the left each time. This is very inefficient * but the line_buffer is large enough to handle every * expected line size (since lines should always be less * than 512). The segmenting should only come into * play in degenerate cases. */ tot_buf_size = *buffer_size; while(line_length) { if(line_length > LINE_BUFFER_SIZE) { memmove(line_buffer, *buffer, LINE_BUFFER_SIZE); *buffer_size -= LINE_BUFFER_SIZE; line_length -= LINE_BUFFER_SIZE; /* move everything over includeing the parts * of the buffer already moved */ memmove(*buffer, (*buffer)+LINE_BUFFER_SIZE, tot_buf_size-LINE_BUFFER_SIZE); memmove(far_end-LINE_BUFFER_SIZE, line_buffer, LINE_BUFFER_SIZE); } else { memmove(line_buffer, *buffer, line_length); *buffer_size -= line_length; /* move everything over includeing the parts * of the buffer already moved */ memmove(*buffer, (*buffer)+line_length, tot_buf_size-line_length); memmove(far_end-line_length, line_buffer, line_length); line_length = 0; } } /* check for another linefeed in the buffered data * if there is one then we don't want to pause for * read yet. */ linefeed = 0; for(strptr = *buffer; strptr <= *buffer+*buffer_size; strptr++) if(*strptr == LF) { linefeed = strptr; break; } if(linefeed) *pause_for_next_read = FALSE; } return(status); } #ifdef PROFILE #pragma profile off #endif