-1

I made a simple client program with C that sends HTTP request to a host. The last part of the code, where the client receives HTTP response from server looks like this:

int main(int argc, char *argv[])
{
    // ...
    
    char buf[BUFSIZ];
    int content_length;

    content_length = recv(clientfd, buf, sizeof(buf) - 1, 0);
    buf[content_length] = 0;

    printf("%s", buf);

    fflush(stdout);
    exit(EXIT_SUCCESS);
}

With this code, I often get the expected result like the following:

GET /foo HTTP/1.0
Host: 127.0.0.1:8080

HTTP/1.0 200 OK
Content-Length: 12
Connection: close

abadakedavra   

But sometimes, the content of the request doesn't show up.

GET /foo HTTP/1.0
Host: 127.0.0.1:8080

HTTP/1.0 200 OK
Content-Length: 12
Connection: close

What could be the reason for this behavior?

cf. My whole client code looks like this:

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

#define MAX_REQUEST     10000
#define MAX_URL         2048

void error(const char *msg)
{
    perror(msg);
    exit(EXIT_FAILURE);
}

void parse_url(char *src, char *hostname, char *port, char *url)
{
    size_t i, j = 0;

    for (i = 7; src[i] != ':'; i++)
        hostname[j++] = src[i];
    hostname[j] = 0;

    j = 0;
    for (i = i + 1; src[i] != '/'; i++)
        port[j++] = src[i];
    port[j] = 0;

    j = 0;
    for (i = i + 1; src[i]; i++)
        url[j++] = src[i];
    url[j] = 0;
}

int main(int argc, char *argv[])
{
    /* 
    Expect
    argv[0] : ./client (Executable name)
    argv[1] : -G (GET) or -P (POST)
    argv[2] : http://hostname:port/url
    */

   
    int clientfd;
    char hostname[MAX_URL], port[6], url[MAX_URL];
    char msg[MAX_REQUEST];
    struct addrinfo hints, *listp, *p;

    if (argc < 3 || (strcmp(argv[1], "-G") != 0 && strcmp(argv[1], "-P") != 0))
    {
        printf("Usage:\n        %s -P <URL>        HTTP 1.0 POST from stdin\n"
               "        %s -G <URL>        HTTP 1.0 GET to stdin\n",
               argv[0], argv[0]);
        exit(EXIT_FAILURE);
    }
    parse_url(argv[2], hostname, port, url);
    if(strcmp(argv[1], "-P") == 0) fgets(msg, MAX_REQUEST, stdin);
    
    /* Client socket creation */

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM; // Use TCP
    hints.ai_flags = AI_NUMERICSERV; // Use numeric port arg

    // Generate a list of addrinfo in listp
    getaddrinfo(hostname, port, &hints, &listp);

    for (p = listp; p; p = p->ai_next)
    {
        // Create a socket based on addrinfo struct
        if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)
            continue;
        if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1)
            break;
        close(clientfd); // Bind fail, loop to try again
    }

    freeaddrinfo(listp); // Not needed anymore

    if (!p) // Entire loop failed
    {
        error("Failed in socket binding");
    }


    /* Send HTTP Request */

    char httpRequest[MAX_REQUEST];

    if(strcmp(argv[1], "-G") == 0) sprintf(httpRequest, "GET /%s HTTP/1.0\r\nHost: %s:%s\r\n\r\n", url, hostname, port);
    else if(strcmp(argv[1]), "-P" == 0) sprintf(httpRequest, "POST /%s HTTP/1.0\r\nHost: %s:%s\r\nContent-Type: plain/text\r\nContent-Length: %lu\r\n\r\n%s\n", url, hostname, port, strlen(msg), msg);
    else error("Invalid request");

    printf("%s", httpRequest);
    send(clientfd, httpRequest, strlen(httpRequest), 0);

    /* Recieve HTTP response */

    char buf[BUFSIZ];
    int content_length;

    content_length = recv(clientfd, buf, sizeof(buf) - 1, 0);
    buf[content_length] = 0;

    printf("%s", buf);

    fflush(stdout);
    exit(EXIT_SUCCESS);
}



JIST
  • 1,139
  • 2
  • 8
  • 30
Dimen
  • 391
  • 1
  • 4
  • 18
  • 4
    Common incorrect assumption. With streaming protocols like TCP you cannot assume that a single `recv` will get everything. You need to keep calling `recv` until you get the expected amount of data. – kaylum Mar 17 '21 at 03:16
  • @kaylum Thanks a lot for the great hint! I will go fix my code again. – Dimen Mar 17 '21 at 03:24

1 Answers1

0

You never implemented the HTTP protocol. If you want to receive an HTTP protocol response, you need to write code that receives an HTTP protocol response in accord with the HTTP protocol. Your code just receives a bunch of bytes, so that's all you get out.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • See my answers to https://stackoverflow.com/questions/16243118/, https://stackoverflow.com/questions/14421008/, and https://stackoverflow.com/questions/7232931/ for how to read an HTTP response properly. – Remy Lebeau Mar 17 '21 at 05:55