I'm trying to submit a simple HTTP GET request in WebAssembly. For this purpose, I wrote this program (copied from Emscripten site with slight modifications):
#include <stdio.h>
#include <string.h>
#ifdef __EMSCRIPTEN__
#include <emscripten/fetch.h>
#include <emscripten.h>
#endif
void downloadSucceeded(emscripten_fetch_t *fetch) {
printf("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url);
// The data is now available at fetch->data[0] through fetch->data[fetch->numBytes-1];
emscripten_fetch_close(fetch); // Free data associated with the fetch.
}
void downloadFailed(emscripten_fetch_t *fetch) {
printf("Downloading %s failed, HTTP failure status code: %d.\n", fetch->url, fetch->status);
emscripten_fetch_close(fetch); // Also free data on failure.
}
unsigned int EMSCRIPTEN_KEEPALIVE GetRequest() {
emscripten_fetch_attr_t attr;
emscripten_fetch_attr_init(&attr);
strcpy(attr.requestMethod, "GET");
attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
attr.onsuccess = downloadSucceeded;
attr.onerror = downloadFailed;
emscripten_fetch(&attr, "http://google.com");
return 1;
}
When I compile it using $EMSCRIPTEN/emcc main.c -O1 -s MODULARIZE=1 -s WASM=1 -o main.js --emrun -s FETCH=1 I get the error
ERROR:root:FETCH not yet compatible with wasm (shared.make_fetch_worker is asm.js-specific)
Is there a way to run HTTP requests from WebAssembly? If yes, how can I do it?
Update 1: The following code attempts to send a GET request, but fails due to CORS issues.
#include <stdio.h>
#include <string.h>
#ifdef __EMSCRIPTEN__
#include <emscripten/fetch.h>
#include <emscripten.h>
#endif
unsigned int EMSCRIPTEN_KEEPALIVE GetRequest() {
EM_ASM({
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://google.com");
xhr.send();
});
return 1;
}
No, you cannot execute HTTP request from WebAssembly (or access DOM, or any other browser APIs). WebAssembly by itself doesn’t have any access to its host environment, hence it doesn’t have any built in IO capabilities.
You can however export functions from WebAssembly, and import functions from the host environment. This will allow you to make HTTP requests indirectly via the host.
I recently ran across this issue with esmcripten fixed it in: https://github.com/kripken/emscripten/pull/7010
You should now be able to use FETCH=1 and WASM=1 together.
Unfortunately, there is no way to make a CORS request to Google.com from any website other than Google.com.
From MDN:
For security reasons, browsers restrict cross-origin HTTP requests initiated from within scripts. For example, XMLHttpRequest and the Fetch API follow the same-origin policy. This means that a web application using those APIs can only request HTTP resources from the same origin the application was loaded from, unless the response from the other origin includes the right CORS headers.
Google does not include those headers.
Because JavaScript/WebAssembly runs on the client's machine (not yours) you could do nasty things if this wasn't in place, like make POST requests to www.mybankingwebsite.com/makeTransaction with the client's cookies.
If you want to point the code you have in Update 1 to your own site, or run it on Node.js, it should work fine.
Related
I am writing a downloader for crawler with libcurl in linux c. But I am stuck on a curl error.
long http_version_code; // http version
test_rc = curl_easy_getinfo(easy_handle, CURLINFO_HTTP_VERSION, &http_version_code);
hd_easy_check_curl_code(test_rc);
The test_rc is CURLE_BAD_FUNCTION_ARGUMENT.
Libcurl document says:
SYNOPSIS
#include <curl/curl.h>
CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_HTTP_VERSION, long *p);
DESCRIPTION
Pass a pointer to a long to receive the version used in the last http
connection. The returned value will be CURL_HTTP_VERSION_1_0,
CURL_HTTP_VERSION_1_1, or CURL_HTTP_VERSION_2_0, or 0 if the version
can't be determined.
Curl ErrorCode:
CURLE_BAD_FUNCTION_ARGUMENT (43)
Internal error. A function was called with a bad parameter.
I have followed the document, any suggestions?
And my libcurl version is curl-7.50.3
I'm having trouble getting an FCGI application working in C using nginx. I'm using spawn-fcgi to create the socket and run my application (which I have named paste)
I'm thinking it must be a problem with my application, but I'm fairly certain I copied all the relevant parts from the example source located here.
This is the error nginx is giving me:
[error] 53300#0: *4 upstream prematurely closed connection while
reading response header from upstream, client: 127.0.0.1, server:
localhost, request: "GET /test HTTP/1.1", upstream:
"fastcgi://unix:/tmp/cfcgi.sock:", host: "localhost"
Here's the source for the application:
#include <fcgi_stdio.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv) {
while(FCGI_Accept() >= 0) {
printf("Content-type: text/html\r\n\r\n");
printf("Hostname: %s", getenv("SERVER_HOSTNAME"));
}
return EXIT_SUCCESS;
}
The relevant config changes in nginx:
location /test {
include fastcgi_params;
fastcgi_pass unix:/tmp/cfcgi.sock;
}
And the spawn-fcgi command:
spawn-fcgi -s /tmp/cfcgi.sock -M 0777 -P cfcgi.pid -- paste
The variables that Nginx passes to a FastCGI server are listed in /etc/nginx/fastcgi_params, which you include in your configuration file. And there is no such variable as SERVER_HOSTNAME. The closest one is SERVER_NAME.
The function getenv() returns 0 if the requested variable is not found, which happens in your case. Then this value is referenced by printf (%s), which causes a segmentation fault to occur.
So, to fix the problem you can either add the parameter SERVER_HOSTNAME to your fastcgi_params file (don't forget to reload Nginx after that), or replace SERVER_HOSTNAME with SERVER_NAME in your application.
I'm developing a system that tracks objects with a P(an)T(ilt)Z(oom) camera which can be controlled via HTTP requests. The C application I develop is supposed to receive position data of the tracked object and to send commands to the camera to control the pan and tilt angle. In addition to these commands the camera has to receive a session refresh command every 5 seconds. HTTP Digest Authorization has to be used for the connection.
I'm sending the HTTP request with libcurl. I figured already out that for digest auth one needs to use on and the same curl handle for all requests in this stackoverflow post.
For sending the session refresh command periodically I tried to use a thread which is just doing this:
while(1)
{
usleep(5000000);
sessionContinue(g_Config.cam_ip);
}
With sessionContinue looking like this:
CURLcode sessionContinue(char* url)
{
CURLcode res;
char requestURL[40];
char referer[47];
struct curl_slist *headers=NULL;
strcpy(requestURL , url);
strcat(requestURL, CAM_SESSION_CONTINUE);
strcpy(referer , "Referer: http://");
strcat(referer , url);
strcat(referer , CAM_MONITOR);
headers = curl_slist_append(headers,"Connection:keep-alive");
headers = curl_slist_append(headers, camCookie);
// In windows, this will init the winsock stuff
curl_global_init(CURL_GLOBAL_ALL);
curl_easy_reset(curl);
if(curl)
{
// First set the URL that is about to receive our POST. This URL can
//just as well be a https:// URL if that is what should receive the
//data.
curl_easy_setopt( curl , CURLOPT_URL , requestURL );
curl_easy_setopt( curl , CURLOPT_HTTPHEADER , headers );
curl_easy_setopt( curl , CURLOPT_HTTPGET , 1 );
curl_easy_setopt( curl , CURLOPT_USERNAME , "root" );
curl_easy_setopt( curl , CURLOPT_PASSWORD , "password" );
curl_easy_setopt( curl , CURLOPT_HTTPAUTH , CURLAUTH_BASIC | CURLAUTH_DIGEST );
// Perform the request, res will get the return code
res = curl_easy_perform(curl);
// Check for errors
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed # %s:%d : %s\n", curl_easy_strerror(res) , __FILE__ , __LINE__ );
}
return res;
}
The application always crashed with segmentation fault after executing curl_easy_perform(curl). So I read the libcurl tutorial again and now I know that using one curl handle in multiple threads is a no go.
What I tried then was to use a timer with SIGALRM to implement the periodic session refresh. This didn't change the problem with the crash at curl_easy_perform(curl). The strange thing is that the application doesn't crash when sending the normal command to control the pan and tilt position which uses the same curl handle. The only difference between session refresh and pan/tilt command is that session refresh uses GET and pan/tilt uses POST.
Are there any other possibilities to send pan/tilt commands continuously with a short pause every 5 seconds used to send the session refresh?
You have a long range of problems in one small program. Here's a few:
You might overflow one of those small fixed-size buffers with the dangerous unbounded C functions you use. Quite likely one of them is the reason for the segfault.
curl_global_init() is documented to be called once, you call it over and over again - this even without calling curl_global_cleanup() in between. You obviously call curl_easy_init() somewhere out of the function and you should move the global init there.
'referer' gets filled with data but is never used otherwise
Another advice is to use CURLOPT_ERRORBUFFER to get error messages in rather than curl_easy_strerror() as you may get some extra details then. And of course to set CURLOPT_VERBOSE while debugging the request to see that things look the way you want it.
Thanks for your comment Daniel Stenberg. I'm now calling curl_global_init() just once when the handle has been set up. referer wasn't really needed here, but I had forgotten to remove it before pasting the code here.
The reason for the segmentation fault was that the session refresh command and the commands for pan and tilt tried to use one and the same curl handle at the same time, which obviously can't really work. So the solution with the timer and SIGALRM wasn't the problem. The segmentation faults have been solved by adding a mutex lock to avoid concurrent accesses to the curl handle.
Background:
I'm working on my first C program with the library and I need to gather responses from each command sent to a SMTP server.
I've gotten as far as sending commands to the SMTP server and printing the response headers using curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, parse_head), but I'm using multi threaded options. It is not at all clear when I get a response which command it was caused by. I am assuming that they will not necessarily be received in the same order sent. Is that correct?
Making it more difficult, since the library handles some calls (like setting up the initial connection) without my explicit request, I would need to handle more headers than explicit requests. That would be predictable and repeatable, but definitely adds an extra level of complexity.
Question:
Is there a "good" way to determine exactly which command resulted in which response header using multi thread?
Also, moderately related, does the library support returning the numeric return code or do I have to manually parse that out? Looking through the library, it seems that it doesn't. I just want to be sure.
I am assuming that they will not necessarily be received in the same order sent. Is that correct?
Yes, it is. That's how multithreading works.
Is there a "good" way to determine exactly which command resulted in which response header using multi thread?
Yes. You can set user data (context info, whatever you call it) using the CURLOPT_HEADERDATA option - this will be passed in as the 4th argument of your header function. So you can write code like this:
CURL *hndl = curl_easy_init();
// ...
curl_easy_setopt(hndl, CURLOPT_HEADERFUNCTION, parse_head);
curl_easy_setopt(hndl, CURLOPT_HEADERDATA, some_pointer_that_identifies_the_thread);
// ...
size_t parse_head(void *buf, void *size_t sz, size_t nmemb, void *context)
{
// context will be the pointer identifying the thread
}
does the library support returning the numeric return code or do I have to manually parse that out?
Yes, it does:
long httpStatus;
curl_easy_getinfo(hndl, CURLINFO_RESPONSE_CODE, &httpStatus);
if (200 <= httpStatus && httpStatus < 300) {
// HTTP 2XX OK
} else {
// Error (4XX, 5XX) or redirect (3XX)
}
I have a website where each webpage is compiled into a binary (I have 100 webpages, therefore I have 100 binaries). Apache's .htaccess contains the line "SetHandler cgi-script" which instructs apache to use CGI when a binary (webpage) is requested.
How can I modify this website to use FastCGI instead of CGI ?
Do I just have to include this header and use this while loop (FastCGI.com) in each of the 100 binaries and modify .htaccess to "SetHandler fastcgi-script" ?
#include "fcgi_stdio.h" // instead of stdio.h
while(FCGI_Accept() >= 0)
So how will FastCGI work exactly ? Apache will dispatch webpages using 1 persistent process for the entire website or will there be 1 persistent process for each of the 100 binaries ?
A FastCGI script is a network server that listens for connections in a loop. The web server forward requests to the FCGI server which sends back some dynamically generated content - all over a socket connection. Thus a FCGI script is faster than CGI as it is not re-spawned for each request.
I don't understand why you need 100 binaries for 100 pages. A single script is enough to generate content for 100 pages, based on some request parameter. The FCGI server should also scale pretty well for multiple connections as it is usually made to poll on the socket file descriptor. (Look at the code of the implementation to make sure of this).
To generate 100 pages you don't necessarily need 100 if statements. Consider this pseudo-code:
hash_table page_generators; // map page types to function objects (or function pointers)
page_generators["login_page"] = handle_login_page_fn;
page_generators["contact_page"] = handle_contact_page_fn;
// ... and so on
// request handler
page_type = request.get("page_type");
fn = page_generators[page_type];
if (fn == NULL)
return "<html><body>Invalid request</body></html>";
else
return fn(request);