I'm trying to create a simple proxy server using BSD sockets, which listens on a port for a request and then passes that request on to another server, before sending the server's response back to the browser.
I am able to receive a REST request from the browser, using the code below:
void *buffer = malloc(512);
long length = 0;
while (1) {
void *tempBuffer = malloc(512);
long response = recv(acceptedDescriptor, tempBuffer, 512, 0);
if (response == 0 || response < 512) {
free(tempBuffer);
printf("Read %lu bytes\n", length);
break;
}
memcpy(buffer + length, tempBuffer, response);
free(tempBuffer);
length += response;
realloc(buffer, length + 512);
}
However, recv()
should return 0 when the connection is closed by the peer (in this case the browser), but this is never the case. The only way I am able to detect whether or not the connection has closed is by checking if the response is less than the maximum amount requested from recv()
, 512 bytes. This is sometimes problematic as some requests I see are incomplete.
If there is no more data to receive, recv()
blocks and never returns, and setting the accepted descriptor to be non-blocking means that the read loop goes on forever, never exiting.
If I:
Set the listening socket descriptor to non-blocking, I get a
EAGAIN
error (resource temporarily unavailable) when I try toaccept()
the connectionSet the accepted socket descriptor to non-blocking,
recv()
never returns 0 and the loop continues on foreverSet them both to non-blocking, I get a 'bad file descriptor' error when trying to
accept()
the connectionDon't set either of them to non-blocking, the loop never exits because
recv()
never returns.
The socket itself is created as follows, but since it is able to detect a request, I can't see anything wrong with its initialisation:
int globalDescriptor = -1;
struct sockaddr_in localServerAddress;
...
int initSocket() {
globalDescriptor = socket(AF_INET, SOCK_STREAM, 0);
if (globalDescriptor < 0) {
perror("Socket Creation Error");
return 0;
}
localServerAddress.sin_family = AF_INET;
localServerAddress.sin_addr.s_addr = INADDR_ANY;
localServerAddress.sin_port = htons(8374);
memset(localServerAddress.sin_zero, 0, 8);
int res = 0;
setsockopt(globalDescriptor, SOL_SOCKET, SO_REUSEADDR, &res, sizeof(res));
//fcntl(globalDescriptor, F_SETFL, O_NONBLOCK);
return 1;
}
...
void startListening() {
int bindResult = bind(globalDescriptor, (struct sockaddr *)&localServerAddress, sizeof(localServerAddress));
if (bindResult < 0) {
close(globalDescriptor);
globalDescriptor = 0;
perror("Socket Bind Error");
exit(1);
}
listen(globalDescriptor, 1);
struct sockaddr_in clientAddress;
int clientAddressLength = sizeof(clientAddress);
while (1) {
memset(&clientAddress, 0, sizeof(clientAddress));
clientAddressLength = sizeof(clientAddress);
int acceptedDescriptor = accept(globalDescriptor, (struct sockaddr *)&clientAddress, (socklen_t *)&clientAddressLength);
//fcntl(acceptedDescriptor, F_SETFL, O_NONBLOCK);
if (acceptedDescriptor < 0) {
perror("Incoming Connection Error");
exit(1);
}
void *buffer = malloc(512);
long length = 0;
while (1) {
void *tempBuffer = malloc(512);
long response = recv(acceptedDescriptor, tempBuffer, 512, 0);
if (response == 0) {
free(tempBuffer);
printf("Read %lu bytes\n", length);
break;
}
memcpy(buffer + length, tempBuffer, response);
free(tempBuffer);
length += response;
realloc(buffer, length + 512);
}
executeRequest(buffer, length, acceptedDescriptor);
close(acceptedDescriptor);
free(buffer);
}
}
...
The startListening()
function is called only if initSocket()
returns 1:
int main(int argc, const char *argv[]) {
if (initSocket() == 1) {
startListening();
}
return 0;
}
I'm probably doing something stupid here, but I'd appreciate any information you may have about this problem and how I could fix it.
Since your REST request is a HTTP method, it has the well-defined HTTP Message Length, so you just have to
recv()
until the complete message has arrived.