mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-19 11:09:42 +00:00
188 lines
5.4 KiB
C
188 lines
5.4 KiB
C
#include "internal.h"
|
|
|
|
// Return number of bytes left to read for this connection
|
|
static int64_t left_to_read(const struct mg_connection *conn) {
|
|
return conn->content_len + conn->request_len - conn->num_bytes_read;
|
|
}
|
|
|
|
// Write data to the IO channel - opened file descriptor, socket or SSL
|
|
// descriptor. Return number of bytes written.
|
|
static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
|
|
int64_t len) {
|
|
int64_t sent;
|
|
int n, k;
|
|
|
|
(void) ssl; // Get rid of warning
|
|
sent = 0;
|
|
while (sent < len) {
|
|
|
|
// How many bytes we send in this iteration
|
|
k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);
|
|
|
|
#if !defined(NO_SSL)
|
|
if (ssl != NULL) {
|
|
n = SSL_write(ssl, buf + sent, k);
|
|
} else
|
|
#endif
|
|
if (fp != NULL) {
|
|
n = (int) fwrite(buf + sent, 1, (size_t) k, fp);
|
|
if (ferror(fp))
|
|
n = -1;
|
|
} else {
|
|
n = send(sock, buf + sent, (size_t) k, MSG_NOSIGNAL);
|
|
}
|
|
|
|
if (n <= 0)
|
|
break;
|
|
|
|
sent += n;
|
|
}
|
|
|
|
return sent;
|
|
}
|
|
|
|
// Read from IO channel - opened file descriptor, socket, or SSL descriptor.
|
|
// Return negative value on error, or number of bytes read on success.
|
|
static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) {
|
|
int nread;
|
|
|
|
if (len <= 0) return 0;
|
|
if (fp != NULL) {
|
|
// Use read() instead of fread(), because if we're reading from the CGI
|
|
// pipe, fread() may block until IO buffer is filled up. We cannot afford
|
|
// to block and must pass all read bytes immediately to the client.
|
|
nread = read(fileno(fp), buf, (size_t) len);
|
|
#ifndef NO_SSL
|
|
} else if (conn->ssl != NULL) {
|
|
nread = SSL_read(conn->ssl, buf, len);
|
|
#endif
|
|
} else {
|
|
nread = recv(conn->client.sock, buf, (size_t) len, 0);
|
|
}
|
|
if (nread > 0) {
|
|
conn->num_bytes_read += nread;
|
|
}
|
|
|
|
return conn->ctx->stop_flag ? -1 : nread;
|
|
}
|
|
|
|
static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) {
|
|
int n, nread = 0;
|
|
|
|
while (len > 0 && conn->ctx->stop_flag == 0) {
|
|
n = pull(fp, conn, buf + nread, len);
|
|
if (n < 0) {
|
|
nread = n; // Propagate the error
|
|
break;
|
|
} else if (n == 0) {
|
|
break; // No more data to read
|
|
} else {
|
|
nread += n;
|
|
len -= n;
|
|
}
|
|
}
|
|
|
|
return nread;
|
|
}
|
|
|
|
int mg_read(struct mg_connection *conn, void *buf, int len) {
|
|
int n, buffered_len, nread = 0;
|
|
int64_t left;
|
|
|
|
if (conn->content_len <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
// conn->buf body
|
|
// |=================|==========|===============|
|
|
// |<--request_len-->| |
|
|
// |<-----------data_len------->| conn->buf + conn->buf_size
|
|
|
|
// First, check for data buffered in conn->buf by read_request().
|
|
if (len > 0 && (buffered_len = conn->data_len - conn->request_len) > 0) {
|
|
char *body = conn->buf + conn->request_len;
|
|
if (buffered_len > len) buffered_len = len;
|
|
if (buffered_len > conn->content_len) buffered_len = (int)conn->content_len;
|
|
|
|
memcpy(buf, body, (size_t) buffered_len);
|
|
memmove(body, body + buffered_len,
|
|
&conn->buf[conn->data_len] - &body[buffered_len]);
|
|
len -= buffered_len;
|
|
conn->data_len -= buffered_len;
|
|
nread += buffered_len;
|
|
}
|
|
|
|
// Read data from the socket.
|
|
if (len > 0 && (left = left_to_read(conn)) > 0) {
|
|
if (left < len) {
|
|
len = (int) left;
|
|
}
|
|
n = pull_all(NULL, conn, (char *) buf + nread, (int) len);
|
|
nread = n >= 0 ? nread + n : n;
|
|
}
|
|
|
|
return nread;
|
|
}
|
|
|
|
int mg_write(struct mg_connection *conn, const void *buf, int len) {
|
|
return (int) push(NULL, conn->client.sock, conn->ssl, (const char *) buf,
|
|
(int64_t) len);
|
|
}
|
|
|
|
// Keep reading the input (either opened file descriptor fd, or socket sock,
|
|
// or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
|
|
// buffer (which marks the end of HTTP request). Buffer buf may already
|
|
// have some data. The length of the data is stored in nread.
|
|
// Upon every read operation, increase nread by the number of bytes read.
|
|
static int read_request(FILE *fp, struct mg_connection *conn,
|
|
char *buf, int bufsiz, int *nread) {
|
|
int request_len, n = 0;
|
|
|
|
request_len = get_request_len(buf, *nread);
|
|
while (conn->ctx->stop_flag == 0 &&
|
|
*nread < bufsiz &&
|
|
request_len == 0 &&
|
|
(n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0) {
|
|
*nread += n;
|
|
assert(*nread <= bufsiz);
|
|
request_len = get_request_len(buf, *nread);
|
|
}
|
|
|
|
return request_len <= 0 && n <= 0 ? -1 : request_len;
|
|
}
|
|
|
|
// Send len bytes from the opened file to the client.
|
|
static void send_file_data(struct mg_connection *conn, FILE *fp,
|
|
int64_t offset, int64_t len) {
|
|
char buf[MG_BUF_LEN];
|
|
int num_read, num_written, to_read;
|
|
|
|
// If offset is beyond file boundaries, don't send anything
|
|
if (offset > 0 && fseeko(fp, offset, SEEK_SET) != 0) {
|
|
return;
|
|
}
|
|
|
|
while (len > 0) {
|
|
// Calculate how much to read from the file in the buffer
|
|
to_read = sizeof(buf);
|
|
if ((int64_t) to_read > len) {
|
|
to_read = (int) len;
|
|
}
|
|
|
|
// Read from file, exit the loop on error
|
|
if ((num_read = fread(buf, 1, (size_t) to_read, fp)) <= 0) {
|
|
break;
|
|
}
|
|
|
|
// Send read bytes to the client, exit the loop on error
|
|
if ((num_written = mg_write(conn, buf, (size_t) num_read)) != num_read) {
|
|
break;
|
|
}
|
|
|
|
// Both read and were successful, adjust counters
|
|
conn->num_bytes_sent += num_written;
|
|
len -= num_written;
|
|
}
|
|
}
|
|
|