How to get the URL from a localhost server without using libcurl - html

I've made a custom HTTP server that serves an HTML page with some text, an input text box for entering a command, and a submit button. Upon receiving a form submission, the server is supposed to run the given command and serve a response containing the results.
The issue I cant quite figure out is how to get the URLs of form submission requests so as to parse out the command to run. As presently implemented, the server runs on localhost:3838, and when the client browses to that URL the server responds correctly with the form. When the user enters (say) the command ls in the text box and clicks the "run" submission button, a request is issued to localhost:3838/run?command=ls. How can I obtain that URL in the server, so as to parse out and execute the command?
Here's the present server code:
CwebServer.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#define PORT "3838" //port being connected to
#define MAXLEN 800
#define BACKLOG 10 //number of pending connections to be held in queue
//format of html page
char header []=
"HTTP/1.1 200 Ok\r\n"
"Content-Type: text/html; charset=UTF-8\r\n\r\n"
"<!DOCTYPE html>\r\n"
"<html>\n"
"<head>\n"
"<title>Web-Based Remote Command Server</title>\r\n"
"</head>\n"
"<body>\n\n";
char input []=
"<form action= \"/run\" method= \"GET\"> \n"
"Command: <input type=\"text\" size=\"100\" name=\"command\">\n"
"<input type=\"submit\" value=\"Run\">\n"
"</form>";
char output []=
"<p>Command that was run and testing this:</p>\n"
"<pre>Your server will include the command that was just run here.</pre>\n\n"
"<p>Standard Output:</p>\n""<pre>Your server will include the stdout results here.</pre>\n\n"
"<p>Standard Error:</p>\n"
"<pre>Your server will include the stderr results here.</pre>\r\n\r\n"
"</body>\r\n""</html>\r\n";
char *buff = header;
void sigchld_handler(int s)
{
(void)s; // quiet unused variable warning
// waitpid() might overwrite errno, so we save and restore it:
int saved_errno = errno;
while(waitpid(-1, NULL, WNOHANG) > 0);
errno = saved_errno;
}
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main (void){
int sockfd;
int new_fd;
struct addrinfo hints;
struct addrinfo *serverinfo;
struct addrinfo *p;
struct sockaddr_storage client_addr;
socklen_t addrsize;
struct sigaction sa;
int yes = 1;
char s[INET6_ADDRSTRLEN];
int status;
memset(&hints, 0, sizeof hints); //makes struct empty
hints.ai_family = AF_UNSPEC; //IPv4 or v6
hints.ai_socktype = SOCK_STREAM; //TCP type need
hints.ai_flags = AI_PASSIVE; //Fill in IP for us
//if can't get address info print error
if((status = getaddrinfo(NULL, PORT, &hints, &serverinfo)) != 0){
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return 1;
}
for(p = serverinfo; p != NULL; p = p->ai_next){
if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
perror("server: socket");
continue;
}
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){
perror("setsockopt");
exit(1);
}
if(bind(sockfd, p->ai_addr, p->ai_addrlen) == -1){
close(sockfd);
perror("server: bind");
continue;
}
break;
}
freeaddrinfo(serverinfo);
if(p == NULL){
fprintf(stderr, "server: failed to bind\n");
exit(1);
}
if(listen(sockfd, BACKLOG) == -1){
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
printf("server: waiting for connections....\n");
while(1){
addrsize = sizeof client_addr;
new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &addrsize);
if(new_fd == -1){
perror("Did not accept");
continue;
}
inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), s, sizeof s);
printf("server: got connection from %s\n", s);
if(!fork()){
close(sockfd);
int bufsize = 1024;
char *buffer = malloc(bufsize);
send(new_fd, header, bufsize, 0);
//write(new_fd, "HTTP/1.1 200 OK\n", 16);
//write(new_fd, "Content-length: 46\n", 19);
//write(new_fd, "Content-type: text/html\n\n", 25);
//write(new_fd, "<html><head>\n<head>\n<title>The CAvengers Web Page</title>\n</head>\n</html>", 46);
if(send(new_fd, buffer, MAXLEN, 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
close(new_fd);
}
return 0;
}

Web servers and clients communicate via the HTTP protocol. That's what makes them web servers as opposed to some other kind. HTTP is a request / response protocol: the client sends a request to the server that contains information about what it's requesting, and the server processes that request to determine how to respond.
Your particular server seems to intend to implement HTTP 1.1. This is not the most recent version of the protocol, but that's ok. Pretty much every HTTP client in the world understands that dialect. But yours is a pretty minimally-functioning server, responding to every established connection with the same HTTP response, even if the client does not actually send an HTTP request.
Your question is how to get the request URI. The answer is to read it from the client, via the connected socket returned by accept(). The read() or recv() function would be appropos for that. The format you should expect the client to use is described in the HTTP specifications I linked above, but in very brief, you should expect the request to start with the request method name (GET), at least one space, the request URI, and a carriage-return / linefeed pair. You will need to parse the request URI to distinguish between an initial request for the form and a form submission, and in the latter case you will be able to parse the query parameters as well.
DO NOTE, however, that although HTTP is a comparatively simple protocol, it is still much more complicated than I've just described.

Related

How to take an Input integer from HTM? - Arduino / NodeMCU ESP32

Hi im currently doing Servo NodeMCU controlling. my plan is to create an Input Box where i can type, then whatever number i type will be use as servo angle using Webserver or 194.168.4.1 or so on. (ex: i type 90, servo angle will be 90) the problem i si do not know how to get it. here is the code:
#include<Servo.h>
Servo ServoPin;
int angle = 0;
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiAP.h>
#define ServoPin 34; // Set the GPIO pin where you connected your test LED or comment this line out if your dev board has a built-in LED
// Set these to your desired credentials.
const char *ssid = "XXXXXX";
const char *password = "XXXXXX";
WiFiServer server(80);
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println("Configuring access point...");
pinMode(ServoPin,OUTPUT);
// You can remove the password parameter if you want the AP to be open.
WiFi.softAP(ssid, password);
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
server.begin();
Serial.println("Server started");
}
void loop() {
WiFiClient client = server.available(); // listen for incoming clients
if (client) { // if you get a client,
Serial.println("New Client."); // print a message out the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
// the content of the HTTP response follows the header:
client.print("<!DOCTYPE html>");
client.print("<html>");
client.print("<body>");
client.print("<p>Change the text of the text field, and then click the button below.</p>"); //
client.print("INPUT NUMBER: <input type='number' id='servo'>"); //in this area, I WILL TYPE number 0-255.
client.print("<button type='button' '>Go</button>");
ServoPin.attach(number); //the area where i will assign the servo to the angle i type.
// break out of the while loop:
break;
} else { // if you got a newline, then clear currentLine:
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// close the connection:
client.stop();
Serial.println("Client Disconnected.");
}
}
So as you can see in client.print"INPUT NUMBER.... so on line, that is the place where my code will design an input box. the problem is it is inside a " " or double quotation mark. i wonder what should i do to get the 'number' input then use it on ServoPin.attach(number);
im very beginner on HTML(zero actually) because i dont have knowledge here yet. this code is mostly taken from internet w3school website then i modify it a bit to Servo controlling. so im really hoping that someone can tell me how to do it....
Board: Node32
actual Board: NodeMCU ESP 32
Here is web server code that parses two variables from a GET response of a HTML form, I suppose it should be no problem to adopt it for your needs: http://playground.arduino.cc/Code/WebServerST
Hey so this is the how I do this, I've made a form in your html that would take the input and send a get request to the server and can be dealt with there. this is the process:
input put into form, sent to server in get request.
the get request is send stored char by char in header variable.
if statement checks if the parameter is in the header. The string is searched for the value and the value is saved into a string.
good luck this should work.
#include<Servo.h>
Servo ServoPin;
int angle = 0;
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiAP.h>
#define ServoPin 34; // Set the GPIO pin where you connected your test LED or comment this line out if your dev board has a built-in LED
// Set these to your desired credentials.
const char *ssid = "XXXXXX";
const char *password = "";
WiFiServer server(80);
String header;
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println("Configuring access point...");
pinMode(ServoPin,OUTPUT);
// You can remove the password parameter if you want the AP to be open.
WiFi.softAP(ssid, password);
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
server.begin();
Serial.println("Server started");
}
void loop() {
WiFiClient client = server.available(); // listen for incoming clients
if (client) { // if you get a client,
Serial.println("New Client."); // print a message out the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read();// read a byte, then
header += c; //write request to the header
Serial.write(c); // print it out the serial monitor
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
if (header.indexOf("input1=") >= 0) { //if input1 is in the header then...
Serial.println(header);
String input_string = "";
int reqEnd = header.indexOf(" HTTP/1.1");
for (int n = 16; n < reqEnd; ++n) { //put the input value from the header to the variable
input_string += header[n];
}
Serial.println(input_string);
}
// the content of the HTTP response follows the header:
client.print("<!DOCTYPE html>");
client.print("<html>");
client.print("<body>");
client.print("<form action=\"/get\">"); //added a form to take a text input and send a get request.
client.print("input1: <input type=\"text\" name=\"input1\">");
client.print("<input type=\"submit\" value=\"Submit\">");
client.print("</form><br>");
ServoPin.attach(number); //the area where i will assign the servo to the angle i type.
// break out of the while loop:
break;
} else { // if you got a newline, then clear currentLine:
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// close the connection:
client.stop();
Serial.println("Client Disconnected.");
}
}
#include "WiFi.h"
#include "ESPAsyncWebServer.h"
const char* ssid = "yourNetworkName";
const char* password = "yourNetworkPass";
AsyncWebServer server(80);
void setup(){
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
Serial.println(WiFi.localIP());
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
int paramsNr = request->params();
Serial.println(paramsNr);
for(int i=0;i<paramsNr;i++){
AsyncWebParameter* p = request->getParam(i);
Serial.print("Param name: ");
Serial.println(p->name());
Serial.print("Param value: ");
Serial.println(p->value());
Serial.println("------");
}
request->send(200, "text/plain", "message received");
});
server.begin();
}
void loop()
{}

esp32 arduino ide Web Socket stream json no errors but no values

Thank you for considering this problem.
I'm streaming a small json from a Web socket and can see the stringified json arrive to the client because it prints to the serial monitor, but then it deserializes to a 1 or 0 instead of my key:value pairs. I just want it to parse the json so that the rest of my program can use the values. I get no errors. Tried both Dynamic and Static json docs. Tried triple the memory requirement.
Arduino:
#include <WiFi.h>
#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1
#include <ArduinoJson.h>
#include <StreamUtils.h>
const char* ssid = "ssid";
const char* password = "pw";
const char* host = "10.0.0.250";
void setup()
{
Serial.begin(115200);
delay(10);
// We start by connecting to a WiFi network
int loopCount = 0;
StaticJsonDocument<384> doc;
DeserializationError error;
void loop()
{
//delay(5000);
++loopCount;
if (loopCount > 1) return;
Serial.print("connecting to ");
Serial.println(host);
// Use WiFiClient class to create TCP connections
WiFiClient client;
const int httpPort = 1337;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
// This will send the request to the server
client.print(String("GET ") + "HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) {
Serial.println(">>> Client Timeout !");
client.stop();
return;
}
}
// Read all the lines of the reply from server and print them to Serial
while (client.available() > 0) {
ReadLoggingStream loggingClient(client, Serial);
error = deserializeJson(doc, loggingClient);
}
Serial.println("");
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
//this doesn't work
int id = doc["id"]; // Should be 5 but I get 0 for every value
Serial.print("id: "); Serial.println(id);
}
/*Serial monitor:
14:21:25.905 ->
07:16:36.574 -> WiFi connected
07:16:36.574 -> IP address:
07:16:36.574 -> 10.0.0.113
07:16:36.574 -> connecting to 10.0.0.250
07:16:36.849 -> "{\"id\":5,\"nom\":\"whynot\",\"delayStart\":200,\"rampePWM\":11,\"pulseWelding\":200,\"speedBalayage\":0.4,\"speedWelding\":0.5,\"speedWire\":1.1,\"balayage\":0.8,\"pulseWire\":5,\"retractWire\":7}"
07:16:36.849 -> id: 0
*/
The tcp-socket is in my node express setup. The file projet.json is only the json seen above ^^ no white space.
var net = require('net');
var serverN = net.createServer(function(socket) {
fs.readFile("./data/projet.json", 'utf-8', (err, data) => {
if (err) {
throw err;
}
socket.write(JSON.stringify(data));
socket.pipe(socket);
});
});
serverN.listen(1337, '10.0.0.250');
I can only show you how i use it to get the right values. i use a DynamicJsonDocument in my solution:
DynamicJsonDocument root(2048);
DeserializationError err = deserializeJson(root, http.getString());
String TravelTimes = root["travelTime"];
Otherwise you can also try to output the values directly via the jsonobject
JsonObject object = doc.to<JsonObject>();
const char* id = object["id"];
The code parses the JSON string but then calls JsonDocument::to<T>() to obtain a JsonObject. This method clears the document - from https://arduinojson.org/v6/api/jsondocument/to/
Clears the JsonDocument and converts it to the specified type.
JsonDocument::as<T>() should be used instead:
JsonObject object = doc.as<JsonObject>();
From https://arduinojson.org/v6/api/jsondocument/as/
Casts JsonDocument to the specified type.
Unlike JsonDocument::to(), this function doesn’t change the content of the JsonDocument.
You can also use serializeJsonPretty() to display the JsonObject on the serial output. Instead of:
JsonObject object = doc.to<JsonObject>();
Serial.println(object);
this can be done with:
serializeJsonPretty(doc, Serial);
Thanks to bblanchon of ArduinoJson - The node socket was stringifying the json twice. I changed the socket to socket.write(data) instead of socket.write(JSON.stringify(data)) and it works.
See full explanation here
https://github.com/bblanchon/ArduinoJson/issues/1507
Thanks again!

Parsing data from incoming HTTP Post request on ESP8266/Wemos

I'm sending an HTTP Post request on my Android App to my Wemos D1 mini pro and want to parse the incoming data (which is a json). My current code just prints out the whole POST request and I need to trim it so I only get the needed data. There are several examples out there but nothing matched my needs or worked at all.
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ArduinoJson.h>
const char* ssid = "myssid";
const char* password = "mypassword";
char c;
String readString = String(100);
WiFiServer wifiServer(80);
void setup() {
Serial.begin(9600);
delay(1000);
WiFi.begin(ssid, password);
WiFi.mode(WIFI_STA);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting..");
}
Serial.print("Connected to WiFi. IP:");
Serial.println(WiFi.localIP());
wifiServer.begin();
}
//for parsing the actual JSON later
//you can ignore this at this moment because I don't even get the needed string to parse it from JSON
void handleReceivedMessage(String message){
StaticJsonBuffer<500> JSONBuffer; //Memory pool
JsonObject& parsed = JSONBuffer.parseObject(message); //Parse message
if (!parsed.success()) { //Check for errors in parsing
Serial.println("Parsing failed");
return;
}
const char * name3 = parsed["name"]; //Get name from HTTP
Serial.println("name3");
}
void loop() {
WiFiClient client = wifiServer.available();
if (client) {
Serial.println("Client connected");
while (client.connected()) {
while (client.available()>0) {
//instream from mobile device
char c = client.read();
if (readString.length() < 100) {
//store characters to string
readString.concat(c);
//Serial.print(c);
}
Serial.print(c);
//if HTTP request has ended
if (c == '\n') {
//Serial.println(readString);
delay(50);
//handleReceivedMessage(readString);
readString = "";
client.stop();
}
}}}}
Well first of all you seem to be using ArduinoJson lib version 5, now I could share the code I worked with and never failed me with version 5. But i'm going to encourage you to update the library to version 6 and share with you my piece of code.
I use this normally when I need to get information out of API's
DynamicJsonDocument doc(1024);
char* payload1 = (char*)malloc(http.getSize() + 1);
http.getString().toCharArray(payload1, http.getSize() + 1);
Serial.println(payload1);
http.end();
auto error = deserializeJson(doc, payload1);
free(payload1);
if (error) {
Serial.print(F("deserializeJson() failed with code "));
Serial.println(error.c_str());
return;
}
serializeJsonPretty(doc, Serial);
now as you can see, I'm using a getString method from httpClient lib in order to fill my char array and than parse it into json object (pretty much the same thing you was attempting, only difference is the memory pointers and Memory allocations.
Hopefully this will work with you.

Server displaying text instead of HTML

I'm attempting to make a C server that will take inputs and be able to spit them back out to the user via html format where the server works as a user interface. My current issue I cant seem to figure out is why the C server spits out the HTML code as text at localhost:3838 instead of displaying it as a proper web page.
How can I solve this and have it be able to work to send user commands back to the server and spit out the proper response? Do I do this by using recieve and then putting the message into a buffer, parsing that message, and then putting that response into a buffer to send back using send?
CSERVER.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#define PORT "3838" //port being connected to
#define MAXLEN 800
#define BACKLOG 10 //number of pending connections to be held in queue
//format of html page
char header []=
"<!DOCTYPE html>\n"
"<html>\n"
"<head>\n"
"<title>Web-Based Remote Command Server</title>\n"
"</head>\n"
"<body>\n\n";
char input []=
"<form action= \"/run\" method= \"get\"> \n"
"Command: <input type=\"text\" size=\"100\" name=\"command\">\n"
"<input type=\"submit\" value=\"Run\">\n"
"</form>";
char output []= "<p>Command that was run:</p>\n"
"<pre>Your server will include the command that was just run here.</pre>\n\n"
"<p>Standard Output:</p>\n""<pre>Your server will include the stdout results here.</pre>\n\n"
"<p>Standard Error:</p>\n"
"<pre>Your server will include the stderr results here.</pre>\n\n"
"</body>\n""</html>";
char *buff = header;
void sigchld_handler(int s)
{
(void)s; // quiet unused variable warning
// waitpid() might overwrite errno, so we save and restore it:
int saved_errno = errno;
while(waitpid(-1, NULL, WNOHANG) > 0);
errno = saved_errno;
}
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main (void){
int sockfd;
int new_fd;
struct addrinfo hints;
struct addrinfo *serverinfo;
struct addrinfo *p;
struct sockaddr_storage client_addr;
socklen_t addrsize;
struct sigaction sa;
int yes = 1;
char s[INET6_ADDRSTRLEN];
int status;
memset(&hints, 0, sizeof hints); //makes struct empty
hints.ai_family = AF_UNSPEC; //IPv4 or v6
hints.ai_socktype = SOCK_STREAM; //TCP type need
hints.ai_flags = AI_PASSIVE; //Fill in IP for us
//if can't get address info print error
if((status = getaddrinfo(NULL, PORT, &hints, &serverinfo)) != 0){
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return 1;
}
for(p = serverinfo; p != NULL; p = p->ai_next){
if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
perror("server: socket");
continue;
}
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){
perror("setsockopt");
exit(1);
}
if(bind(sockfd, p->ai_addr, p->ai_addrlen) == -1){
close(sockfd);
perror("server: bind");
continue;
}
break;
}
freeaddrinfo(serverinfo);
if(p == NULL){
fprintf(stderr, "server: failed to bind\n");
exit(1);
}
if(listen(sockfd, BACKLOG) == -1){
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
printf("server: waiting for connections....\n");
while(1){
addrsize = sizeof client_addr;
new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &addrsize);
if(new_fd == -1){
perror("Did not accept");
continue;
}
inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), s, sizeof s);
printf("server: got connection from %s\n", s);
if(!fork()){
close(sockfd);
int bufsize = 1024;
char *buffer = malloc(bufsize);
recv(new_fd, buffer, bufsize, 0);
write(new_fd, "HTTP/1.1 200 OK\n", 16);
write(new_fd, "Content-length: 46\n", 19);
write(new_fd, "Content-type: text/html\n\n", 25);
write(new_fd, "<html><head>\n<title>The Web Page</title>\n</head>\n</html>", 46);
if(send(new_fd, header, MAXLEN, 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
close(new_fd);
}
return 0;
}
You need to add the response headers like this one :
for example the response header send by SO for this page is:
HTTP/2.0 200 OK
cache-control: private
content-type: text/html; charset=utf-8
content-encoding: gzip
x-frame-options: SAMEORIGIN
x-request-guid: 599a5768-3cc6-4b94-86e1-e1d1daa8acd5
strict-transport-security: max-age=15552000
content-security-policy: upgrade-insecure-requests
accept-ranges: bytes
date: Tue, 07 May 2019 13:17:15 GMT
via: 1.1 varnish
x-served-by: cache-lcy19237-LCY
x-cache: MISS
x-cache-hits: 0
x-timer: S1557235035.073229,VS0,VE89
vary: Accept-Encoding,Fastly-SSL
x-dns-prefetch-control: off
content-length: 35669
X-Firefox-Spdy: h2

Get HTML of url link with Visual Studio

The code below works for the main site, but it does not work for the sub-links. For example, the code works when the url link is "www.reddit.com" or "www.wikipedia.org" but it will not work when it is "www.reddit.com/r/nba" or "www.wikipedia.org/wiki/2007_NASCAR_Nextel_Cup_Series" Do you know how to make it work to include sub-links as well?
#include "stdafx.h"
#include <winsock2.h>
#include <WS2tcpip.h>
#include <windows.h>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main(){
// Initialize Dependencies to the Windows Socket.
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
cout << "WSAStartup failed.\n";
system("pause");
return -1;
}
// We first prepare some "hints" for the "getaddrinfo" function
// to tell it, that we are looking for a IPv4 TCP Connection.
struct addrinfo hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; // We are targeting IPv4
hints.ai_protocol = IPPROTO_TCP; // We are targeting TCP
hints.ai_socktype = SOCK_STREAM; // We are targeting TCP so its SOCK_STREAM
// Aquiring of the IPv4 address of a host using the newer
// "getaddrinfo" function which outdated "gethostbyname".
// It will search for IPv4 addresses using the TCP-Protocol.
struct addrinfo* targetAdressInfo = NULL;
DWORD getAddrRes = getaddrinfo("www.wikipedia.org/wiki/2007_NASCAR_Nextel_Cup_Series", NULL, &hints, &targetAdressInfo);
if (getAddrRes != 0 || targetAdressInfo == NULL)
{
cout << "Could not resolve the Host Name" << endl;
system("pause");
WSACleanup();
return -1;
}
// Create the Socket Address Informations, using IPv4
// We dont have to take care of sin_zero, it is only used to extend the length of SOCKADDR_IN to the size of SOCKADDR
SOCKADDR_IN sockAddr;
sockAddr.sin_addr = ((struct sockaddr_in*) targetAdressInfo->ai_addr)->sin_addr; // The IPv4 Address from the Address Resolution Result
sockAddr.sin_family = AF_INET; // IPv4
sockAddr.sin_port = htons(80); // HTTP Port: 80
// We have to free the Address-Information from getaddrinfo again
freeaddrinfo(targetAdressInfo);
// Creation of a socket for the communication with the Web Server,
// using IPv4 and the TCP-Protocol
SOCKET webSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (webSocket == INVALID_SOCKET)
{
cout << "Creation of the Socket Failed" << endl;
system("pause");
WSACleanup();
return -1;
}
// Establishing a connection to the web Socket
cout << "Connecting...\n";
if (connect(webSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr)) != 0)
{
cout << "Could not connect";
system("pause");
closesocket(webSocket);
WSACleanup();
return -1;
}
cout << "Connected.\n";
// Sending a HTTP-GET-Request to the Web Server
const char* httpRequest = "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n";
int sentBytes = send(webSocket, httpRequest, strlen(httpRequest), 0);
if (sentBytes < strlen(httpRequest) || sentBytes == SOCKET_ERROR)
{
cout << "Could not send the request to the Server" << endl;
system("pause");
closesocket(webSocket);
WSACleanup();
return -1;
}
// Receiving and Displaying an answer from the Web Server
char buffer[10000];
ZeroMemory(buffer, sizeof(buffer));
int dataLen;
while ((dataLen = recv(webSocket, buffer, sizeof(buffer), 0) > 0))
{
int i = 0;
while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r') {
cout << buffer[i];
i += 1;
}
}
// Cleaning up Windows Socket Dependencies
closesocket(webSocket);
WSACleanup();
system("pause");
return 0;
}
You have to replace your GET / with GET /path/you/want/to/access.html and leave the server name as www.wikipedia.org but for heavens sake use WinHttpClient or Curl or something else more hi-level.
A URL is built by protocol, host and link. e.g. protocol://host/link --> http://www.wikipedia.org/wiki/2007_NASCAR_Nextel_Cup_Series. Here, http is the protocol, www.wikipedia.org is the host and /wiki/2007_NASCAR_Nextel_Cup_Series is the link.
Web Browsers internally split those parts up and then establish a Connection. e.g
http --> use port 80
www.wikipedia.org --> search for host and establish Connection
link (Here Comes the interesting part) --> Send the link through the TCP Connection as header
With a raw TCP Connection you can't directly open URL liks, you Need to send the link through the TCP Connection as Header. Or you use the Standart WebClients which do it for you.
I think the ultimate Problem is that you are trying to establish a TCP Connection with the entire URL instead of just the host.