Http dialog with an Server

Home Forums SwiftForth Forum Http dialog with an Server

This topic contains 3 replies, has 2 voices, and was last updated by  jean-pierre schmitt 8 months ago.

Viewing 4 posts - 1 through 4 (of 4 total)
  • Author
    Posts
  • #13192

    Hello,
    I’m using SwiftForth on Windows 10. I’m looking for help to make a simple vocabulary to easily install an http dialog.
    The specifications are as follows:

    1) Install a Socket
    2) Send a request to a server
    3) Save the answer in a Buffer

    Thank you for your answers

    Jean Pierre

    #13365

    Budsy
    Participant

    Jean Pierre. I happen to have some code to get you started. Needs work, but does what you are looking for.

    `{ ====================================================================
    http.f
    alpha work on HTTP GET Request and Response fetching.

    Bob Dickow, 2017
    adapted and inspired by Rick VanNorman’s port
    ‘sendmail.f’ demo program

    V 0.0.1 very alpha, but works.
    Gets a response and saves in a buffer, header and all.

    in progress,

    TODO:
    (1) error checking. Current many checks abandoned (stubbed out).
    (2) only does a GET for the default page. URL query string needs
    to be placed in the GET first line. Just modify RESOURCE string.
    (3) Needs to parse the response, remove header junk, and display,
    or do other meaningful processing, eventually.
    (4) Needs to deal with input of preferred URL. Current hard coded
    to “www.google.com” for quick debugging/testing.
    (5) This version only does a GET request.
    (6) This version might need tweaking for https
    (7) Using an allocated 100k buffer (arbitrary) from the heap for the response here, for testing.
    Should probably use some streaming buffer approach… we don’t know
    how big these responses can be, do we?
    (8) does not deal with redirects (such as 301 to https for example)
    ==================================================================== }

    { ——————————————————————–
    Uses Win32 Winsock API functions to perform http GET

    WINSOCK FUNCTIONS USED:

    WSAStartup Initializes specific winsock version.
    gethostbyname Attempts to lookup host name from IP #.
    getservbyname Looks up service port from service name.
    socket Creates a socket of given type.
    htons Converts a USHORT from host byte order.
    connect Establishes connection on a socket.
    recv Receives data through socket.
    send Transmits data through socket.
    closesocket Closes (deallocates) a socket.
    WSACleanup Deallocates internal winsock resources.

    ——————————————————————– }
    REQUIRES SINGLESTEP

    REQUIRES WINSOCK

    \ MARKER HTTP

    THROW#
    S” Cannot find Winsock v1.1 or later” >THROW ENUM IOR_HTTP_WINSOCK
    S” Cannot find web server” >THROW ENUM IOR_HTTP_MISSING
    S” Cannot open web server socket” >THROW ENUM IOR_HTTP_SOCKET
    S” Error connecting to socket” >THROW ENUM IOR_HTTP_CONNECT
    S” Error during transfer” >THROW ENUM IOR_HTTP_XFER
    TO THROW#

    { ——————————————————————–
    Messages to send via email are built one line at a time at PAD, and
    used via the XT passed to TYPEFILE.

    +CRLF appends a crlf to the string at the address.
    +ZCRLF appends a crlf to the zstring at the address.

    ——————————————————————– }
    100000 CONSTANT RESPONSE-LENGTH \ memory to alloc for the response.
    VARIABLE Output Output OFF

    CREATE <CRLF> 2 C, $0D C, $0A C, 0 C,
    : +CRLF ( addr — ) <CRLF> COUNT ROT APPEND ;
    : +ZCRLF ( zaddr — ) <CRLF> COUNT ROT ZAPPEND ;

    { ————————————————————————
    Usage is a little bit of help for SENDMAIL

    CHECK reports an error named by the zstring if the status is SOCKET_ERROR.
    ———————————————————————— }

    : Usage
    CR .” Usage: HTTP-GET CR
    CR .” Example here will load hard-coded (http://)www.google.com”
    ;

    : CHECK ( status zstr — )
    OVER SOCKET_ERROR = THIRD 0= OR IF
    CR .” Error during call to ” ZCOUNT TYPE .” : ”
    . .” , error: ” GetLastError .
    IOR_HTTP_XFER THROW
    THEN 2DROP ;

    { ————————————————————————
    Buffers for composing the message header

    SERVER has the name of the smtp server.

    ARGS parses from the forth command line the next 4 blank delimited words
    which will serve as the to, from, etc of the email.
    ———————————————————————— }

    CREATE DOMAIN 256 ALLOT
    CREATE URL 256 ALLOT
    CREATE RESOURCE 256 ALLOT

    \ Currently ARGS just hard-codes ‘www.google.com.’ Should parse post-string from cmd line.
    : ARGS ( — )
    \ { BL WORD COUNT URL } Z” http://skeleton/” ZCOUNT URL ZPLACE
    \ the idea is eventually to parse the URL into simply a host hame (no protocol part)
    Z” http://www.google.com” ZCOUNT DOMAIN ZPLACE \ testing stub
    Z” /” ZCOUNT RESOURCE ZPLACE \ testing, ask just for the default page
    ;

    \ ARGS http://www.google.com

    { ——————————————————————–
    Socket interfaces

    HOST has the address of the server after opening.
    HTTPSERVER has the socket number allocated for talking to the host.
    PORT has the port number of the communication path to the
    host. This is normally 80 unless getservbyname
    tells us differently.
    ——————————————————————– }

    0 VALUE HOST
    0 VALUE HTTPSERVER
    0 VALUE PORT

    { ——————————————————————–
    OPEN-SOCKS creates a socket session and
    CLOSE-SOCKS closes one.
    ——————————————————————– }

    : OPEN-SOCKS ( — ) $101 PAD WSAStartup IOR_HTTP_WINSOCK ?THROW ;

    : CLOSE-SOCKS ( — ) WSACleanup DROP ;

    \ if the server address is a valid ip address, we use it as is
    \ otherwise we try to have our dns resolve it to an ip address for us.

    [DEBUG
    : FIND-HOST ( — hostaddr )
    DOMAIN inet_addr DUP 1+ ?EXIT
    DROP DOMAIN gethostbyname ( a)
    DUP 0= IOR_UNRESOLVED ?THROW
    3 CELLS + @ @ @ ;
    DEBUG]

    [DEBUG
    : CREATE-SOCKET ( — )
    PF_INET SOCK_STREAM IPPROTO_TCP socket TO HTTPSERVER
    HTTPSERVER INVALID_SOCKET = IOR_HTTP_SOCKET ?THROW ;
    DEBUG]

    { reference for the servent struct from Microsoft Winsock API:
    typedef struct servent {
    char FAR *s_name;
    char FAR FAR **s_aliases;
    short s_port;
    char FAR *s_proto;
    — SERVENT, *PSERVENT, FAR *LPSERVENT;
    }
    [DEBUG
    : CONNECT-SOCKET ( — )
    PAD 16 ERASE
    \ AF_INET PAD H! PORT PAD 2+ H! HOST PAD 4 + !
    AF_INET PAD H! PORT PAD 2+ H! HOST PAD 4 + !
    HTTPSERVER PAD 16 connect drop 2 Z” connect() CONNECT-SOCKET” CHECK
    ;
    DEBUG]

    [DEBUG
    : SLINE ( addr n — )
    DUP 0= IF 2DROP S” ” THEN
    HTTPSERVER -ROT 0 send drop \ 2 { <> } IOR_HTTP_CONNECT ?THROW
    ;
    DEBUG]

    [DEBUG
    : SET-PORT ( — )
    Z” www” 0 getservbyname ?DUP IF
    8 + U@
    ELSE
    80 htons
    THEN TO PORT ;
    DEBUG]

    [DEBUG
    : HTTP-REQUEST ( — )
    RESPONSE-LENGTH ALLOCATE DROP Output ! Output @ RESPONSE-LENGTH ERASE
    \ set up the header part of the request. End of this is signalled by 2 CRLF
    Output @ MEM
    RESOURCE ZCOUNT Z\” GET <RESOURCE> HTTP/1.0″ ZCOUNT Z\” <RESOURCE>” ZCOUNT REPLACE
    Output @ ZPLACE Output @ +ZCRLF
    Z\” User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.4″ ZCOUNT Output @ ZAPPEND Output @ +ZCRLF
    DOMAIN ZCOUNT Z\” Host: <DOMAINHOST>” ZCOUNT Z\” <DOMAINHOST>” ZCOUNT REPLACE
    Output @ ZAPPEND Output @ +ZCRLF
    Z\” Connection: Keep-Alive” ZCOUNT Output @ ZAPPEND Output @ DUP DUP +ZCRLF +ZCRLF +ZCRLF
    Output @ ZCOUNT CR { 2DUP TYPE }
    SLINE Output @ RESPONSE-LENGTH ERASE
    HTTPSERVER Output @ RESPONSE-LENGTH 0 recv drop \ 2 { <> } IOR_HTTP_XFER ?THROW

    ;
    DEBUG]

    [DEBUG
    : SEND-REQUEST ( — )
    ARGS
    OPEN-SOCKS
    FIND-HOST
    TO HOST
    CREATE-SOCKET
    SET-PORT
    CONNECT-SOCKET
    HTTP-REQUEST
    ;
    DEBUG]

    : FREE-OUTPUT ( — )
    Output @ ?DUP IF FREE DROP OUTPUT OFF THEN
    ;

    [DEBUG
    -? : HTTP-GET ( — )
    [‘] SEND-REQUEST CATCH IF
    CR .” errors attempting GET”
    GetLastError cr .” error: ” .
    ELSE
    Output @ zcount TYPE \ The buffer is ‘Output’
    THEN
    CLOSE-SOCKS
    FREE-OUTPUT
    ;
    DEBUG]
    CR CR
    CR .( A rudimentary HTTP GET demo in SwiftForth. GETs google.com, puts response in buffer, and sort of displays text in the console window. )
    CR
    Usage
    CR
    CR

    \ End of script

    #13439

    Budsy
    Participant

    Actually, my code above only fetches the response header data from the server. To get the BODY of the message, just do another similar recv immediately after the first one. For refinements, check the return code from the headers, and if 200, then fetch the body. Also, parse out the content-length only allocate the bytes required for the response body.

    #13795

    That’s the end of the holidays
    An Big Thank you Budsy for your answer.
    I’ll get to work tomorrow and
    start testing all this.
    A hello from Switzerland
    Jean Pierre

Viewing 4 posts - 1 through 4 (of 4 total)

You must be logged in to reply to this topic.