Recent

Author Topic: how to do a HTTP POST with sockets  (Read 3284 times)

Key-Real

  • Sr. Member
  • ****
  • Posts: 372
how to do a HTTP POST with sockets
« on: June 03, 2023, 04:36:46 am »
Hi,

I'm trying to send a SOAPAction to my Router.

It looks like this:

Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC} {$H+}
  2. uses
  3.     sockets, SysUtils;
  4.  
  5. procedure SocketError(s:string);
  6. begin
  7.     writeln('Socket Error: ', s);
  8.     halt(1);
  9. end;
  10.  
  11. const
  12.         MS : string =  
  13.                 'POST /upnp/control/WANIPConnection0 HTTP/1.1' + #$0d + #$0a +
  14.                 'SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping"' + #$0d + #$0a +
  15.                 'Host: 192.168.0.4:49153' + #$0d + #$0a +
  16.                 'Content-Type: text/xml' + #$0d + #$0a +
  17.                 'Content-Length: ';
  18.  
  19. var
  20.         socket : longint;
  21.         addr : sockaddr_in;
  22.         f : text;
  23.         post : string;
  24.         s : string;
  25. begin
  26.         assignfile(f, 'add.xml');
  27.         reset(f);
  28.         post:= '';
  29.         repeat
  30.                 readln(f, s);
  31.                 post:= post + s + #$0a + #$0d;
  32.         until eof(f);
  33.         close(f);
  34.  
  35.         ms:=ms + intToStr(length(post)) + #$0d + #$0a + #$0d + #$0a + post + #$0d +#$0a;
  36.  
  37.         writeln(ms);
  38.  
  39.         socket:= fpsocket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  40.         if socket = -1 then SocketError('Init Socket');
  41.  
  42.         fillchar(addr, 0, sizeof(addr));
  43.         addr.sin_family:= AF_INET;
  44.         addr.sin_addr:= StrToNetAddr('192.168.0.1');
  45.         addr.sin_port:= htons(49153);
  46.  
  47.         if fpconnect(socket, @addr, sizeof(addr)) < 0 then SocketError('unable to connect');
  48.  
  49.         //if fpsendto(socket, pchar(MS), length(MS), 0, @addr, sizeof(addr)) = -1 then SocketError('send MS message'); 
  50.         if fpsend(socket, @ms, sizeof(ms), 0) < 0 then SocketError('unable to send');
  51.  
  52.     closeSocket(socket);
  53. end.
  54.  


my add.xml:
Code: Pascal  [Select][+][-]
  1. <?xml version="1.0"?>
  2. <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  3. <SOAP-ENV:Body>
  4.     <m:AddPortMapping xmlns:m="urn:schemas-upnp-org:service:WANIPConnection:1">
  5.         <NewPortMappingDescription>It is my uPnP test</NewPortMappingDescription>
  6.         <NewLeaseDuration>0</NewLeaseDuration>
  7.         <NewInternalClient>192.168.0.4</NewInternalClient>
  8.         <NewEnabled>True</NewEnabled>
  9.         <NewExternalPort>3333</NewExternalPort>
  10.         <NewRemoteHost></NewRemoteHost>
  11.         <NewProtocol>UDP</NewProtocol>
  12.         <NewInternalPort>3333</NewInternalPort>
  13.     </m:AddPortMapping>
  14. </SOAP-ENV:Body>
  15. </SOAP-ENV:Envelope>
  16.  


so my question: Do I do the HTTP POST request right?

dbannon

  • Hero Member
  • *****
  • Posts: 3156
    • tomboy-ng, a rewrite of the classic Tomboy
Re: how to do a HTTP POST with sockets
« Reply #1 on: June 03, 2023, 06:00:29 am »
Have you considered  fcl-web ?

https://wiki.freepascal.org/fpWeb_Tutorial

In use, about line 1321 of https://github.com/tomboy-notes/tomboy-ng/blob/master/source/transgithub.pas where I talk to github with POST.

Davo
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

Warfley

  • Hero Member
  • *****
  • Posts: 1749
Re: how to do a HTTP POST with sockets
« Reply #2 on: June 03, 2023, 12:02:52 pm »
I don't know why you would want to implement HTTP yourself, but if you really want to do, to use HTTPS you must use TLS.

You can either directly use the openssl unit and use the functions like "sslConnect" instead of "fpConnect" (of course requires to setup an ssl context first, but thats just a bit of boilerplate code), as described here (for C): https://stackoverflow.com/questions/7698488/turn-a-simple-socket-into-an-ssl-socket

Or you can use the ssockets (+opensslsocket) units instead:
Code: Pascal  [Select][+][-]
  1. uses
  2.   ssockets, opensslsockets;
  3.  
  4. var
  5.   Sock: TInetSocket;
  6. begin
  7.   Sock := TInetSocket.Create('127.0.0.1', 1337, TOpenSSLSocketHandler.create);
  8.   try
  9.     Sock.Connect;
  10.     Sock.Write('Hello World'[0], Length('Hello WOrld'));
  11.   finally
  12.     Sock.Free;
  13.   end;
  14. end.

edap

  • Newbie
  • Posts: 1

rvk

  • Hero Member
  • *****
  • Posts: 6579
Re: how to do a HTTP POST with sockets
« Reply #4 on: June 03, 2023, 03:51:05 pm »
I don't know why you would want to implement HTTP yourself, but if you really want to do, to use HTTPS you must use TLS.
Seeing the code this is not https.
It's for UPnP to a router which is simple HTTP.

So just using THttpSend from Synapse or fcl-web is enough.

(I've done UPnP with THttpSend)

Key-Real

  • Sr. Member
  • ****
  • Posts: 372
Re: how to do a HTTP POST with sockets
« Reply #5 on: June 15, 2023, 09:11:33 pm »
I'm writing a bigger program and I do not wanna use 3rd Party Libraries like Synapse etc.
I'm working with the socket unit.

So I'm trying to do a HTTP Post only with Raw Sockets.

Can you please provide an example?

rvk

  • Hero Member
  • *****
  • Posts: 6579
Re: how to do a HTTP POST with sockets
« Reply #6 on: June 15, 2023, 10:01:27 pm »
I'm writing a bigger program and I do not wanna use 3rd Party Libraries like Synapse etc.
I'm working with the socket unit.

So I'm trying to do a HTTP Post only with Raw Sockets.

Can you please provide an example?
I can't provide an example for raw sockets.

I could adjust the example from earlier with the standard fcl-web package (which should be installed as default) but with using raw sockets it would become much more complex.

Why are you against using fcl-web?

Or can you provide simple raw socket code (with the emphasis on simple)?

« Last Edit: June 15, 2023, 10:05:43 pm by rvk »

Key-Real

  • Sr. Member
  • ****
  • Posts: 372
Re: how to do a HTTP POST with sockets
« Reply #7 on: June 16, 2023, 08:34:33 am »
I'm not using Lazarus, the Project is in pure FPC

rvk

  • Hero Member
  • *****
  • Posts: 6579
Re: how to do a HTTP POST with sockets
« Reply #8 on: June 16, 2023, 08:39:01 am »
I'm not using Lazarus, the Project is in pure FPC
Fcl-web is pure FPC.
Nothing to do with Lazarus.

Don't you have a $(fpcdirectory)\packages\fcl-web directory?

rvk

  • Hero Member
  • *****
  • Posts: 6579
Re: how to do a HTTP POST with sockets
« Reply #9 on: June 16, 2023, 02:02:08 pm »
I changed my example with just using fcl-web and socket.
It works out of the box on Windows and Linux with only FPC.

https://github.com/rvk01/upnp

TRon

  • Hero Member
  • *****
  • Posts: 3623
Re: how to do a HTTP POST with sockets
« Reply #10 on: June 16, 2023, 02:44:57 pm »
I changed my example with just using fcl-web and socket.
Thank you for your class.

Tested on my machine (Linux x86_64 with upnp disabled on my router). At creation of the class:
The broadcast option seems to do as suggested as it returns a zero then setting the timeout returns a -1 for me (edit: should that not be a ttimeval struct = 2x 64 bit on 64-bit Linux ?).
The following bind also returns a -1 for me.
Strangely enough the sendto then returns a 101 to finally "hang" indefinitely at the call to fprecvfrom.

Not knowing how serious you wish the class to be(come) I thought to mention it.

« Last Edit: June 16, 2023, 02:55:32 pm by TRon »
This tagline is powered by AI (AI advertisement: Free Pascal the only programming language that matters)

rvk

  • Hero Member
  • *****
  • Posts: 6579
Re: how to do a HTTP POST with sockets
« Reply #11 on: June 16, 2023, 03:33:38 pm »
Not knowing how serious you wish the class to be(come) I thought to mention it.
It should at least work on Windows and Linux with Router UPnP enabled and disabled.
I've just switched to socket (from Synapse) so it could contain some bugs.

But I've just tested it here and even when disabling the UPnP, it timeout correctly for me.

The broadcast option seems to do as suggested as it returns a zero then setting the timeout returns a -1 for me (edit: should that not be a ttimeval struct = 2x 64 bit on 64-bit Linux ?).
Yes, Thank you. I fixed that one.

The following bind also returns a -1 for me.
Strangely enough the sendto then returns a 101 to finally "hang" indefinitely at the call to fprecvfrom.
I'm not entirely sure the fpbind() is needed. I can comment it out and it still works for me (I receive several other M-SEARCH results).

I'm new to the whole raw sockets so there might be an easier way to do this in fpc (maybe with fcl-net).

It works now for me when UPnP is disabled (it searches and waits about 6 seconds before giving up).
If it still hangs for you I need to look at the timeouts again.

TRon

  • Hero Member
  • *****
  • Posts: 3623
Re: how to do a HTTP POST with sockets
« Reply #12 on: June 16, 2023, 03:42:51 pm »
@rvk:
switching to struct ttimeval lets the setsocket option succeed and also will now correctly "stop" the call to receive when the timeout occurs. Thank you !

The raw sockets themselves is not that difficult (I take my queue from examples written with/for bsdsockets) but I have never searched for how upnp works with bsdsockets (nor was I able to find something useful when key-real asked for it).
This tagline is powered by AI (AI advertisement: Free Pascal the only programming language that matters)

Key-Real

  • Sr. Member
  • ****
  • Posts: 372
Re: how to do a HTTP POST with sockets
« Reply #13 on: June 17, 2023, 07:24:42 pm »
@rvk:

I tested Your lib under windows, it doesn't work :(
It tells me: "UPnP is not available."

under Linux it works perfect, I tested it.

under Mac it looks good,
it does the job but I can't verify if it succeed.

rvk

  • Hero Member
  • *****
  • Posts: 6579
Re: how to do a HTTP POST with sockets
« Reply #14 on: June 17, 2023, 07:36:31 pm »
I tested Your lib under windows, it doesn't work :(
It tells me: "UPnP is not available."
Not sure why. I just tested it here one last time and it works.
You could put a writeln(S) in the Create() (just below the SetLength() on line 140).
Just to see if you receive some M-SEARCH discover packets.

Maybe it can fail with multiple network interfaces. But here it worked on Windows and Linux.
In that case you can try to do UPnP := TUPnP.Create('192.168.0.1'); in your upnptest.lpr.

Quote
UPnP device is available at http://192.168.2.1:35608/ctl/IPConn

Internal IP: 192.168.2.11
External IP: x.x.x.x
Status: Connected
Last error: ERROR_NONE
Uptime router: 26 days 12 hours 20 minutes and 17 seconds

SetPortMapping OK
Portmappings:
0 TCP 8000->192.168.2.11:8081  0 s

Check port. Press Enter to delete and terminate


DeletePortMapping OK

No portmappings

We are done, press enter

under Mac it looks good,
it does the job but I can't verify if it succeed.
If the output is as above then it should work fine. If, after that, the port doesn't seem open, then it must be something in the program or the mac itself blocking the port. This code can only deal with the UPnP part of the router.

BTW. There also seems to be something like NAT-PMP and PCP. But I'm not familiar with that.

 

TinyPortal © 2005-2018