Comment faire une requête HTTP get en C sans libcurl?

Je veux écrire un programme C pour générer une requête Get sans utiliser aucune bibliothèque externe. Est-ce possible d’utiliser uniquement des bibliothèques C et des sockets? Je pense créer un paquet http (en utilisant un formatage approprié) et l’envoyer au serveur. Est-ce le seul moyen possible ou existe-t-il un meilleur moyen?

    En utilisant des sockets BSD ou, si vous êtes quelque peu limité, disons que vous avez un RTOS, une stack TCP plus simple, comme lwIP, vous pouvez former la requête GET / POST.

    Il existe un certain nombre d’implémentations open-source. Voir le “happyhttp” comme exemple ( http://scumways.com/happyhttp/happyhttp.html ). Je sais, il s’agit de C ++, pas de C, mais la seule chose qui dépend de C ++ est la gestion des chaînes / des tableaux, de sorte qu’il est facilement porté en C pur.

    Attention, il n’y a pas de “paquets”, étant donné que HTTP est généralement transféré via la connexion TCP, il n’y a donc techniquement qu’un stream de symboles au format RFC. Puisque les requêtes http sont généralement effectuées de manière connect-send-disconnect, on pourrait en fait appeler cela un “paquet”.

    En gros, une fois que vous avez une socket ouverte (sockfd), “tout” vous devez faire est quelque chose comme:

    char sendline[MAXLINE + 1], recvline[MAXLINE + 1]; char* ptr; size_t n; /// Form request snprintf(sendline, MAXSUB, "GET %s HTTP/1.0\r\n" // POST or GET, both tested and works. Both HTTP 1.0 HTTP 1.1 works, but sometimes "Host: %s\r\n" // but sometimes HTTP 1.0 works better in localhost type "Content-type: application/x-www-form-urlencoded\r\n" "Content-length: %d\r\n\r\n" "%s\r\n", page, host, (unsigned int)strlen(poststr), poststr); /// Write the request if (write(sockfd, sendline, strlen(sendline))>= 0) { /// Read the response while ((n = read(sockfd, recvline, MAXLINE)) > 0) { recvline[n] = '\0'; if(fputs(recvline,stdout) == EOF) { cout << ("fputs erros"); } /// Remove the trailing chars ptr = strstr(recvline, "\r\n\r\n"); // check len for OutResponse here ? snprintf(OutResponse, MAXRESPONSE,"%s", ptr); } } 

    Exemple exécutable minimal pour POSIX 7

    Allons chercher http://example.com .

    wget.c

     #define _XOPEN_SOURCE 700 #include  #include  #include  /* getprotobyname */ #include  #include  #include  #include  #include  #include  #include  int main(int argc, char** argv) { char buffer[BUFSIZ]; enum CONSTEXPR { MAX_REQUEST_LEN = 1024}; char request[MAX_REQUEST_LEN]; char request_template[] = "GET / HTTP/1.1\r\nHost: %s\r\n\r\n"; struct protoent *protoent; char *hostname = "example.com"; in_addr_t in_addr; int request_len; int socket_file_descriptor; ssize_t nbytes_total, nbytes_last; struct hostent *hostent; struct sockaddr_in sockaddr_in; unsigned short server_port = 80; if (argc > 1) hostname = argv[1]; if (argc > 2) server_port = strtoul(argv[2], NULL, 10); request_len = snprintf(request, MAX_REQUEST_LEN, request_template, hostname); if (request_len >= MAX_REQUEST_LEN) { fprintf(stderr, "request length large: %d\n", request_len); exit(EXIT_FAILURE); } /* Build the socket. */ protoent = getprotobyname("tcp"); if (protoent == NULL) { perror("getprotobyname"); exit(EXIT_FAILURE); } socket_file_descriptor = socket(AF_INET, SOCK_STREAM, protoent->p_proto); if (socket_file_descriptor == -1) { perror("socket"); exit(EXIT_FAILURE); } /* Build the address. */ hostent = gethostbyname(hostname); if (hostent == NULL) { fprintf(stderr, "error: gethostbyname(\"%s\")\n", hostname); exit(EXIT_FAILURE); } in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list))); if (in_addr == (in_addr_t)-1) { fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list)); exit(EXIT_FAILURE); } sockaddr_in.sin_addr.s_addr = in_addr; sockaddr_in.sin_family = AF_INET; sockaddr_in.sin_port = htons(server_port); /* Actually connect. */ if (connect(socket_file_descriptor, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) { perror("connect"); exit(EXIT_FAILURE); } /* Send HTTP request. */ nbytes_total = 0; while (nbytes_total < request_len) { nbytes_last = write(socket_file_descriptor, request + nbytes_total, request_len - nbytes_total); if (nbytes_last == -1) { perror("write"); exit(EXIT_FAILURE); } nbytes_total += nbytes_last; } /* Read the response. */ fprintf(stderr, "debug: before first read\n"); while ((nbytes_total = read(socket_file_descriptor, buffer, BUFSIZ)) > 0) { fprintf(stderr, "debug: after a read\n"); write(STDOUT_FILENO, buffer, nbytes_total); } fprintf(stderr, "debug: after last read\n"); if (nbytes_total == -1) { perror("read"); exit(EXIT_FAILURE); } close(socket_file_descriptor); exit(EXIT_SUCCESS); } 

    GitHub en amont .

    Comstackr:

     gcc -ggdb3 -std=c99 -Wall -Wextra -o wget wget.c 

    Obtenez http://example.com et exportez vers stdout:

     ./wget example.com 

    Cette commande est bloquée pour la plupart des serveurs jusqu’à l’expiration du délai imparti.

    • soit le serveur ou le client doit fermer la connexion
    • nous (clients) ne faisons pas
    • la plupart des serveurs HTTP laissent la connexion ouverte jusqu’à l’expiration du délai d’attente des autres requêtes, par exemple JavaScript, CSS et les images, après une page HTML
    • nous pourrions parsingr la réponse et fermer lorsque les octets Content-Length sont lus, mais nous ne l’avons pas fait pour des raisons de simplicité. Les en-têtes de réponse HTTP requirejs indiquent que si Content-Length n’est pas envoyé, le serveur peut simplement se fermer pour déterminer la longueur.

    La partie connexion fonctionne également avec l’IP:

     host example.com 

    donne:

     example.com has address 93.184.216.34 example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946 

    et nous le faisons:

     ./wget 93.184.216.34 

    Cependant, la réponse est une erreur, car nous ne définissons pas correctement Host: dans notre programme, ce qui est requirejs dans HTTP 1.1 .

    Vous trouverez un exemple de serveur: Envoyer et recevoir un fichier dans la programmation de sockets sous Linux avec C / C ++ (GCC / G ++)

    Testé sur Ubuntu 18.04.

    «Sans bibliothèques externes» exclurait à proprement parler libc, vous auriez donc à écrire vous-même tous les appels système. Je doute que vous le pensiez si ssortingct, cependant. Si vous ne souhaitez pas créer de lien vers une autre bibliothèque et ne voulez pas copier le code source d’une autre bibliothèque dans votre application, le meilleur moyen consiste à traiter directement avec le stream TCP à l’aide de l’API de socket.

    La création de la requête HTTP et son envoi via une connexion socket TCP sont simples, tout comme la lecture de la réponse. Il s’agit d’parsingr la réponse, ce qui sera très délicat, en particulier si vous souhaitez prendre en charge une partie relativement importante de la norme. Des éléments tels que les pages d’erreur, les redirections, la négociation de contenu, etc. peuvent rendre notre vie plus difficile si vous parlez à des serveurs Web arbitraires. Si, par contre, le serveur est connu pour son bon comportement et qu’un simple message d’erreur convient à toute réponse inattendue du serveur, cela est également relativement simple.