зеркало из https://github.com/mozilla/gecko-dev.git
478 строки
12 KiB
C
478 строки
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.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 <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
|