pserv/src/bufio.c
Godmar Back 7ba2670512 various fixes for Spring 2021
- support for 206 return code
- fixes to sendfile
- add URL variable to testloginapi.sh
2021-04-14 00:31:37 -04:00

214 lines
5.1 KiB
C

/*
* Support for buffered I/O
*
* The 'bufio' struct implements buffered I/O for a socket, similar
* to Java's BufferedReader.
*
* Since it encapsulates a connection's socket, it also provides
* methods for sending data.
*
* Written by G. Back for CS 3214 Spring 2018
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sendfile.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "bufio.h"
/*****************************************************************/
struct bufio {
int socket; // underlying socket file descriptor
size_t bufpos; // offset of next byte to be read
buffer_t buf; // holds data that was received
};
static const int BUFSIZE = 8192;
static const int READSIZE = 2048;
static int min(int a, int b) { return a < b ? a : b; }
/* Create a new bufio object from a socket. */
struct bufio *
bufio_create(int socket)
{
struct bufio * rc = malloc(sizeof(*rc));
if (rc == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
rc->bufpos = 0;
rc->socket = socket;
buffer_init(&rc->buf, BUFSIZE);
return rc;
}
/* Close a bufio object, freeing its storage and closing its socket. */
void
bufio_close(struct bufio * self)
{
if (close(self->socket))
perror("close");
buffer_delete(&self->buf);
free(self);
}
static ssize_t
bytes_buffered(struct bufio *self)
{
return self->buf.len - self->bufpos;
}
const int TRUNCATE_THRESHOLD = 10000; // tune me
/*
* Discard already read data, shifting any buffered data into a new buffer.
* This will invalidate all offsets.
*
* This method is provided to avoid accumulating all data received
* on a long-running HTTP/1.1 connection into a single buffer.
*/
void bufio_truncate(struct bufio * self)
{
if (self->buf.len > TRUNCATE_THRESHOLD) {
int unread = bytes_buffered(self);
assert(unread >= 0);
if (unread == 0) {
buffer_reset(&self->buf, BUFSIZE);
} else {
buffer_t oldbuf = self->buf;
buffer_init(&self->buf, BUFSIZE);
buffer_append(&self->buf, oldbuf.buf + self->bufpos, unread);
buffer_delete(&oldbuf);
}
self->bufpos = 0;
}
}
static ssize_t
read_more(struct bufio *self)
{
char * buf = buffer_ensure_capacity(&self->buf, READSIZE);
int bread = recv(self->socket, buf, READSIZE, MSG_NOSIGNAL);
if (bread < 1)
return bread;
self->buf.len += bread;
return bread;
}
/* Given an offset into the buffer, return a char *.
* This pointer will be valid only until the next call
* to any of the bufio_read* function.
*
* The returned pointer is not guaranteed to point to a
* zero-terminated string.
*/
char *
bufio_offset2ptr(struct bufio *self, size_t offset)
{
assert (offset < self->buf.len);
return self->buf.buf + offset;
}
/* Given an pointer into the buffer, return an offset
* that can be used to mark the position of something
* in the buffer (e.g., a header)
*/
size_t
bufio_ptr2offset(struct bufio *self, char *ptr)
{
size_t offset = ptr - self->buf.buf;
assert (0 <= offset && offset < self->buf.len);
return offset;
}
/* Read one byte from the socket into the buffer.
* Returns 0 on EOF, -1 on error, else returns 1.
*/
ssize_t
bufio_readbyte(struct bufio *self, char *out)
{
if (bytes_buffered(self) == 0) {
int rc = read_more(self);
if (rc <= 0)
return rc;
}
*out = self->buf.buf[self->bufpos++];
return 1;
}
/* Read until newline (\n) is encountered.
* Sets *line_offset to the buffer offset where the next line
* starts.
*
* Returns number of bytes read, and -1 on error.
*
* If EOF is encountered, the line may not be terminated
* with a \n.
*/
ssize_t
bufio_readline(struct bufio *self, size_t *line_offset)
{
*line_offset = self->bufpos;
char p = 0;
while (p != '\n') {
int rc = bufio_readbyte(self, &p);
if (rc < 0)
return rc;
if (rc == 0)
break;
}
return self->bufpos - *line_offset;
}
/* Attempt to read a fixed number of bytes into the buffer.
* Sets *buf_offset to the offset in the buffer pointing to
* the first byte read.
*
* Returns the actual number of bytes read, or -1 on error.
*/
ssize_t
bufio_read(struct bufio *self, size_t count, size_t *buf_offset)
{
*buf_offset = self->bufpos;
while (bytes_buffered(self) < count) {
int rc = read_more(self);
if (rc < 0)
return rc;
if (rc == 0)
break;
}
ssize_t bytes_read = min(bytes_buffered(self), count);
self->bufpos += bytes_read;
return bytes_read;
}
/* Send a file out to the socket.
* See sendfile(2) for return value.
*/
ssize_t
bufio_sendfile(struct bufio *self, int fd, off_t *off, size_t filesize)
{
return sendfile(self->socket, fd, off, filesize);
}
/*
* Send data contained in 'resp' to the socket.
* See send(2) for return value.
*/
ssize_t
bufio_sendbuffer(struct bufio *self, buffer_t * resp)
{
return send(self->socket, resp->buf, resp->len, MSG_NOSIGNAL);
}