-1

I got a task to implement a http server in c code. it should include handling several connections, but for now I just want to make sure it works with just a single connection. first, here is my code:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>

#define BUF_SIZE 1025

int main(int argc, char **argv)
{

    uint16_t portNum = 80;   
    int connfd = 0, listenFd;
    struct sockaddr_in serv_addr, peer_addr;

    if ((listenFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("socket syscall failed: %s.\nExiting...\n", strerror(errno));
        exit(EXIT_FAILURE);
    }
    memset(&serv_addr, 0, sizeof(serv_addr));


    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); 
    serv_addr.sin_port = htons(portNum);

    if (bind(listenFd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)))
    {
        printf("bind syscall failed: %s\nExiting...\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    if (listen(listenFd, 5))
    {
        printf("listen syscall failed: %s\nExiting...\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    while (1)
    {
        /* new connection */
        socklen_t addrsize = sizeof(struct sockaddr_in);
        connfd = accept(listenFd, (struct sockaddr *) &peer_addr, &addrsize);
        if (connfd < 0)
        {
            printf("\n Error : Accept Failed. %s \n", strerror(errno));
            return 1;
        }

        char httpRequest[BUF_SIZE] = {0};       
        if ((recv(connfd, httpRequest, BUF_SIZE, 0)) == -1)
        {
            printf("recv syscall failed: %s\nExiting...\n", strerror(errno));
            exit(EXIT_FAILURE);
        }

        char msg[BUF_SIZE] = {0};
        strcpy(msg, "200 OK THIS IS A TEST");
        printf("sending message...\n");
        int len = strlen(msg);
        if (send(connfd, msg, len, 0) < 0)
        {
            printf("SEND ERROR\n");
            exit(EXIT_FAILURE);
        }
        printf("message sent!\n");
        close(connfd);
        close(listenFd);
        // THIS IS FOR DEBUG - ignore...
        return 0;

    }

    return EXIT_SUCCESS;
}

here's my problem: to test my code I ran the following command in another linux terminal:

curl -v loaclhost:80/~/ex.txt

(where ex.txt is simply a test file...) here is the problem: I see the http request both in the server and in the curl output, but it seems like the "send" command of the server doesn't work - in the curl window beneath the http rquest it says:

* Empty reply from server
* Connection #0 to host localhost left intact
curl: (52) Empty reply from server

any ideas?

alk
  • 69,737
  • 10
  • 105
  • 255
noamgot
  • 3,962
  • 4
  • 24
  • 44
  • `listenFd`, `maxRequests`, `peer_addr`, and `BUF_SIZE` are undeclared. Required headers are also missing. Why not post a [Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve)? – MikeCAT Jun 02 '16 at 14:14
  • just fixed it... it is not the main thing, anyway... now the could should contatin everything you need to help me. I hope... -_- – noamgot Jun 02 '16 at 14:18
  • Hmmm, couldn't reproduce on my Raspberry Pi with changing `uint16_t portNum = 80;` to `uint16_t portNum = 8098;` so that it won't require root perrmission to bind. – MikeCAT Jun 02 '16 at 14:19
  • `if (listen(listenFd, 5)` -> compile error – MikeCAT Jun 02 '16 at 14:20
  • I am just editing on the go, belive me it compiles... I'll fix it of course... – noamgot Jun 02 '16 at 14:21
  • It is said that doing `close` just after doing `write` isn't good because `close` should be done after the receiver get the data sent. – MikeCAT Jun 02 '16 at 14:29
  • What might be related: [c - close vs shutdown socket? - Stack Overflow](http://stackoverflow.com/questions/4160347/close-vs-shutdown-socket) – MikeCAT Jun 02 '16 at 14:31
  • ok, now it works, but the weird thing - it works as it is (this is a chunk from my code...). I'll see what is wrong in my whole code. thanks everyone! – noamgot Jun 02 '16 at 14:39
  • I recommend firing up some Wireshark to be absolutely sure about what is and isn't getting sent. – yano Jun 02 '16 at 14:39
  • @n.m. Sending HTTP headers isn't required if the server supports only HTTP/0.9. See [RFC1945](https://www.ietf.org/rfc/rfc1945.txt) 6. Response. – MikeCAT Jun 02 '16 at 14:41
  • @MikeCAT I thought the response *is* a header for sone reason. – n. m. could be an AI Jun 02 '16 at 14:43
  • ok, -1 for me, it was probably due to the missing '\n' character in strcpy (as @n.m. mentioned) – noamgot Jun 02 '16 at 14:43
  • OT: This `if ((recv(connfd, httpRequest, BUF_SIZE, 0)) == -1)` is not guaranteed to work. `recv()` might very well return (much) less, (down to one byte) than it was told. To *savely* receive a definite number of byte (>1) the code *needs* to loop around `recv()` counting, by adding up what `recv()` returns (if >0). If it works as is or not is not in under the codes control and can vary from run to run of the program. – alk Jun 02 '16 at 15:04

2 Answers2

1
  1. Buffer of 1024 is too small for anything serious. For just playing locally it will work OK. If you ever want to do some serious work buffer of 8192 bytes works fine even for heavy loads.

  2. recv() is in most implementations blocking function, which means that it will block until it receives the entered number of bytes or a client closes the connection after sending data. I didn't see your socket being defined and treated as non-blocking. So that part is up to curl to overcome.

  3. Your HTTP response is wrong.

The header should look like:

"HTTP/1.1 200 OK\r\nStatus: 200 OK\r\nContent-Type: text/plain\r\n\r\n"

You may also add a header:

"Content-Length: 0"

as you aren't sending any data back except the header.

Also, if you continue using HTTP/0.9 or switch to HtTP/1.0, you should close the connection with a client after sending the data to him.

HTTP/1.1 supports opened connection, but it is usually controlled via Keep-Alive and Connection headers.

Dalen
  • 4,128
  • 1
  • 17
  • 35
  • The HTTP response is not wrong if your server will support only HTTP/0.9 protocol. See [RFC1945](https://www.ietf.org/rfc/rfc1945.txt) 6. Response. `Simple-Response` only contains `[ Entity-Body ]`. – MikeCAT Jun 02 '16 at 14:37
  • I don't care. It's wrong because it doesn't end in \r\n\r\n. Because it doesn't contain any headers, which clients ask for regardless the protocol number. And because omiting the protocol version doesn't necessarily garantee that new clients would like it at all. 0.9 is obsolete. Also, just because something is according to the spec it doesn't mean it works. I wrote HTTP servers before, so, believe me, it is a mess with no ending when it comes to what should be and what it is in real world. – Dalen Jun 02 '16 at 14:45
  • @Dalen this is just a chunk of code, I am just messing around - I know this is not a complete http respond. the real problem was that I didn't terminate the string with a '\n' – noamgot Jun 02 '16 at 14:47
  • What I did tell you to do. So where is the problem? I don't use curl so I don't know what tweaks you can throw at it without messing things up. It's nice of it if it does support 0.9. – Dalen Jun 02 '16 at 14:53
  • 1
    "*recv() [..] will block until it receives the entered number of bytes.*" this is not correct. `recv()` might as well return with less bytes received (down to 1 byte) than it was told even on a blocking socket. – alk Jun 02 '16 at 15:08
  • @alk : As I wrote, it depends on implementation. If a socket is blocking one it is usually blocking until it receives asked number of bytes or a sender closes the connection. In that case it returns with whatever it received. This holds for most socket libs. What else did I forgot? – Dalen Jun 02 '16 at 15:56
  • (on a non-blocking filedescriptor,) `recv()` is blocking until it can return any data. – Stian Skjelstad Jun 03 '16 at 07:25
  • @stian : Exactly. But there are tricks to avoid even that. I.e. not to call recv() until you know that you have some data to receive. So you can go totally non-blocking. But we were talking about blocking sockets here, so... – Dalen Jun 03 '16 at 11:24
  • @StianSkjelstad : did you mean I should add that to my answer? – Dalen Jun 03 '16 at 11:26
-1

ok, -1 for me, it didn't work due to the missing '\n' character in strcpy..

noamgot
  • 3,962
  • 4
  • 24
  • 44