0

I have the following code that I have written which is suppose to send a simple http request over a TCP socket, I get a response but as soon as I try to read in the loop it hangs, in the 2nd read operation (tried it manually)

if anyone has an idea of why this might fail I will appreciate it a lot

attached below is the entire code

I am running the program like this: ./http_client yahoo.com

I get this response text at first:

HTTP/1.1 301 Moved Permanently
Date: Sat, 06 Aug 2022 08:07:11 GMT
Connection: keep-alive
Server: ATS
Cache-Control: no-store, no-cache
Content-Type: text/html
Content-Language: en
X-Frame-Options: SAMEORIGIN
Location: https://www.yahoo.com/
Content-Length: 8

redirect

and then it hangs and closes the socket, it shouldn't hang at all, it should run and exit without a delay or anything


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


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

    int sockfd;
    struct sockaddr_in cli_name;
    struct sockaddr_in *saddr;
    char *hostname;
    struct addrinfo *res;
    int port = 80;


    if (argc != 2) {
        perror("Usage: establish tcp connection to: <hostname>\n");
        exit(1);
    }

    hostname = argv[1];
    printf("Client is alive and establishing socket connection %s.\n", hostname);

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("Error opening channel");
        close(sockfd);
        exit(1);
    }

    if (0 != getaddrinfo(hostname, NULL, NULL, &res)) {
        fprintf(stderr, "Error in resolving hostname %s\n", hostname);
        exit(1);
    }

    bzero(&cli_name, sizeof(cli_name));
    cli_name.sin_family = AF_INET;
    saddr = (struct sockaddr_in *) res->ai_addr;
    cli_name.sin_addr.s_addr = inet_addr(inet_ntoa(saddr->sin_addr));
    cli_name.sin_port = htons(port);

    fflush(stdout);
    if (connect(sockfd, (struct sockaddr *) &cli_name, sizeof(cli_name)) < 0) {
        perror("Error establishing communications");
        close(sockfd);
        exit(1);
    }

    char header[100];
    int cx;
    char buf[2056];
    size_t byte_count = 0;
    size_t sent_byte_count = 0;
    cx = snprintf(header, 100, "GET / HTTP/1.1\r\nHost: %s:%d\r\n\r\n", hostname, port);

    size_t total = strlen(header);
    size_t sent = 0;
    do {
        sent_byte_count = write(sockfd, header + sent, total - sent);
        if (sent_byte_count < 0)
            printf("ERROR writing message to socket");
        if (sent_byte_count == 0)
            break;
        sent += sent_byte_count;
    } while (sent < total);

    memset(buf,0,sizeof(buf));
    while ((byte_count = read(sockfd, buf, 2054)) > 0) {
        buf[byte_count] = '\0';
        printf("%s", buf); // <-- give printf() the actual data size
        fflush(stdout);
    }


    printf("Exiting now.\n");
    close(sockfd);
    exit(0);

}
Eitank
  • 570
  • 8
  • 21
  • `and than it hangs and closes the socket`. Closing the socket is just before the (unnecessary) `exit(0);`. So the program can't hang it just exists. The HTTP response tells you that you should use a different URL. I tried some minutes ago https://stackoverflow.com/questions/73258192 and it just worked as expected. – harper Aug 06 '22 at 08:19
  • I know what the response means, it should still receive all the data from the server and I should be able to reach the `close(sock)` line and it doesn't reach it – Eitank Aug 06 '22 at 08:21
  • @harper, I can give you the makefile for compiling it if you want to and you will see that it hangs after receiving the response instead of just receiving the response and closing the socket – Eitank Aug 06 '22 at 08:23
  • *" it should still receive all the data "* - All of *what* data? You got it all (according to the content-length and actual content). If you're expecting the server to slam the door shut and finish with a res-zero immediately thereafter the server is under no obligation to do so. Yahoo *should* eventually timeout after ~10sec and slam the door regardless, if you wait it out. – WhozCraig Aug 06 '22 at 08:29
  • the `read` function should return 0 when I have read the entire payload and this is the moment where I should just close the socket, and it doesn't happen. this is the problem @WhozCraig – Eitank Aug 06 '22 at 08:31
  • You're misunderstanding. `read` typically returns 0 when the other end closes the socket. Otherwise, it blocks. The server isn't closing the socket (probably because it is opting you to send another request if desired). As I said, parse the header. you already know you received "all the data". If you got what you needed, *you* close the socket. – WhozCraig Aug 06 '22 at 08:35
  • The content length is 8 while the size of the buffer I receive from the server is 280, how do I know what is the total length of the response I should get from the server ? @WhozCraig – Eitank Aug 06 '22 at 08:40
  • `redirect` - last I checked, that's 8 octets. The [content-length](https://stackoverflow.com/questions/2773396/whats-the-content-length-field-in-http-header) denotes the number of octets in the response body *following* the double-crlf at the tail of the header. And from the looks of it, the server isn't slamming the socket shut, even on the redirect, based on that `Connection: keep-alive` in the header. It's waiting for another request, which you're not giving it. Parse the header, determine you have the full monte, then stop bothering the server and just close the socket. – WhozCraig Aug 06 '22 at 08:43
  • I was just thinking about a simple if statement to check whether or not the entire buffer is full or not – Eitank Aug 06 '22 at 08:47
  • There are libs that handle most of this housekeeping for you (ex: libcurl). Ever wonder why? This is it. raw man-handling http request/response is a royal PITA. – WhozCraig Aug 06 '22 at 08:48
  • I have to do it for a project in a networking class I am taking in the university, hence the stupid manual handling – Eitank Aug 06 '22 at 08:48
  • And you can't use libcurl? That's too bad, because it would probably make most of this academic (apparently in more ways than one). – WhozCraig Aug 06 '22 at 08:49
  • the requirement is to use tcp sockets , parse a given URL and send a custom HTTP request through that socket – Eitank Aug 06 '22 at 08:52
  • @WhozCraig, I have found an amazing solution which was to add this :` Connection: close` header in the request. and it worked ! haha amazing – Eitank Aug 06 '22 at 11:34
  • 1
    [That would do it](https://serverfault.com/questions/790197/what-does-connection-close-mean-when-used-in-the-response-message) – WhozCraig Aug 06 '22 at 16:25

0 Answers0