gecko-dev/network/main/mksockrw.c

482 lines
12 KiB
C

/* -*- 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.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#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 <sys/ioctl.h>
/*
* 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 <arpa/nameser.h>
# endif
#include <resolv.h>
#if defined(HAVE_SYS_FILIO_H)
#include <sys/filio.h>
#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 <unistd.h>
#include <malloc.h>
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