Rascal is currently hosting my simple web server. Here I have an input for users using HTML textarea tag, and a submit button. However, I can't figure out how to request that input data from users when they have submitted it. I also don't see much documentation about it, so any help would be appreciated!
Assuming you either use the Content and/or util::Webserver from the library for serving content from Rascal, you always provide a function of type Response (Request) to the server. This function does everything from serving index.html to receiving form inputs, and handling XMLHttpRequests. All you have to do is write the function's alternatives.
The kinds of Requests you can get are defined like this in Content.rsc:
data Request (map[str, str] headers = (), map[str, str] parameters = (), map[str,str] uploads = ())
= get (str path)
| put (str path, Body content)
| post(str path, Body content)
| delete(str path)
| head(str path)
;
And responses are defined by:
data Response
= response(Status status, str mimeType, map[str,str] header, str content)
| fileResponse(loc file, str mimeType, map[str,str] header)
| jsonResponse(Status status, map[str,str] header, value val, bool implicitConstructors = true, bool implicitNodes = true, str dateTimeFormat = "yyyy-MM-dd\'T\'HH:mm:ss\'Z\'")
;
In the example below I use convenience utility functions such as Content::response(str) which wrap an html string with the right HTTP status and mimetypes.
Example:
// this serves the initial form
Response myServer(get("/"))
= response("\<p\>What is your name?\</p\>
'\<form action=\"/submit\" method=\"GET\"\>
' \<input type=\"text\" name=\"name\" value=\"\"\>
'\</form\>
");
// // this responds to the form submission, now using a function body with a return (just for fun):
Response myServer(p:get("/submit")) {
return response("Hello <p.parameters["name"]>!");
}
// in case of failing to handle a request, we dump the request back for debugging purposes:
default Response myServer(Request q) = response("<q>");
Now we can serve this directly from the REPL. The content will show up in an Eclipse editor window or in your default browser and will stay available for 30 minutes after the last interaction in Rascal's internal application server:
rascal>content("test", myServer)
Serving 'test' at |http://localhost:9050/|
Or we can serve it on our own, then browse to http://localhost:10001 to test the server. We have to shut the thing down manually when we're done:
rascal>import util::Webserver;
ok
rascal>serve(|http://localhost:10001|, myServer)
ok
rascal>shutdown(|http://localhost:10001|)
The initially served page in an editor window
The response after for submission
Related
In a textarea, people can paste pictures. I handle that with AngularJS, the ng-paste directive and this piece of code
ctrl.handlePaste = function(event) {
if(event.clipboardData.items.length > 0) {
for(var i = 0; i < event.clipboardData.items.length; i++) {
var item = event.clipboardData.items[i]; // type: DataTransferItem
if (item.type.indexOf("image") != -1) {
file = item.getAsFile(); // type: File
// it's then stored in a array and will be sent to the server
break;
}
}
}
}
Then I send it to my Flask server (Python 2.7) and it is then sent by emails or pushed via API to another service.
On server side I've checked the type and it's a Flask FileStorage.
My problem is: this file is automatically named "image.png" (I'm using Chrome) and I cant find a way to change this name during the process.
I'm ok with changing it on front side (my favourite option), I'm ok on server side too.
If the code uses the formData API, the filename is the third (optional) argument to the formdata.append method. If the code sends the image as a blob with XHR send the filename can be sent in a query param or a Content-Disposition header.
I am trying to create a signed URL and upload files from my PC to google cloud storage using it.
I am using Advanced REST Client(ARC) as the client side application. On the server side, I have a jersey based server running on Appengine.
I first send a GET request from ARC, on receiving which the app engine generates a signed URL and returns it back in the response.
After that I do a PUT request with the file I want to upload in the body and the request URL set to what was received in the response to GET.
The code snippet to create signed URL:
String encodedUrl = null;
String contentMD5 = "";
String contentType = "";
String httpVerb;
httpVerb = "PUT";
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, 10);
long expiration = calendar.getTimeInMillis() / 1000L;
String canonicalizedResource = "/" + bucketName + "/" + objectName;
String baseURL = "https://storage.googleapis.com" + canonicalizedResource;
String stringToSign =
httpVerb + "\n" + contentMD5 + "\n" + contentType + "\n" + expiration + "\n"
+ canonicalizedResource;
AppIdentityService service = AppIdentityServiceFactory.getAppIdentityService();
String googleAccessId = service.getServiceAccountName();
SigningResult signingResult = service.signForApp(stringToSign.getBytes());
String encodedSignature = null;
try {
encodedSignature =
new String(Base64.encodeBase64(signingResult.getSignature(), false), "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new InternalServerErrorException();
}
String signature = null;
try {
signature = URLEncoder.encode(encodedSignature, "UTF-8").toString();
} catch (UnsupportedEncodingException e) {
throw new InternalServerErrorException();
}
encodedUrl =
baseURL + "?GoogleAccessId=" + googleAccessId + "&Expires=" + expiration
+ "&Signature=" + signature;
System.out.println("Signed URL is: "+encodedUrl);
However I observe the following issue:
Whenever I send the PUT request with any file type, I get the following error:
Error - 403
Code - SignatureDoesNotMatch
Message - The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method
Please note that in my code, I am setting the content Type to "" while creating the string to sign. Also while creating the PUT request I don't include any Content-type header.
As far as I understand, if I don't include the contentType in the stringToSign while creating the signed URL and also not add it as a header while sending PUT request it should be fine. So what could be the reason for the error?
After that I changed by code and added the contentType while creating the stringToSign in the code and also gave the corresponding Content-Type header while sending the PUT request.
In this case I am able to upload the file, however the uploaded file is modified/corrupted.I tried with text/plain and image/jpeg.
The problem is that the following text is added at the beginning of the file:
------WebKitFormBoundaryZX8rPPhnm1WXPrUf
Content-Disposition: form-data; name="fileUpload5"; filename="blob"
Content-Type: text/plain
I can see this in the text file and on opening the .jpg file in the hex editor. The .jpg does not open in standard image application since the file has been corrupted by the text in the beginning
Am I missing something here? Is this any issue in the Advanced REST Client?
Actually whenever I send a PUT request with some file in the body, I get a message in the ARC saying that :
The content-type header will be finally changed to multipart/form-data while sending the request
However, I saved exported all the messages to a file from ARC and I didn't find any message with Content-type header set to multipart/form-data.
So why does this message come and is it actually an issue?
URL-signing code is tricky and notoriously hard to debug. Fortunately, Google's google-cloud library has a signUrl function that takes care of this for you. I highly encourage you to use it instead of rewriting it yourself. Here's the documentation.
Now, if you want to debug it yourself, checking the error message is super useful. It will include a complete copy of the string the server checked the signature of. Print out your stringToSign variable and see how it's different. That'll tell you what's wrong.
Now, on to your specific problem: it sounds like you are generating an acceptable signed URL, but then your client is attempting to upload to GCS as if it were doing a multipart, form upload. The text you're looking at is part of an HTTP multipart request, and the "multipart/form-data" warning also points in that direction. See if the app you're using has some sort of "Form" mode/option that you are perhaps accidentally using?
A server side application requires authorization on file download links. This means a normal <a ng-href="{{createLinkToFile()}}"> is no longer sufficient to get enough parameters passed to the server.
When trying to use a programmatic call to the file download, I get the response data back to Dart client application. Using a simple http GET:
var url = "http://example.com/file";
headers.putIfAbsent("Authorization", () => "bearer " + token;
_http.get(url: url, headers : headers);
The future returned by the GET will hold the data, but how do I instruct the browser to download it as a file, instead of just trying to keep it in memory?
Or is there a way to just do it in a normal link?
After downloading the data from the server like shown in Using Dart to Download a PNG File (Binary File) and displaying it not working you can create a download link like shown at http://blog.butlermatt.me/2014/03/dynamically-generating-download-files/
import 'dart:html';
void main() {
List body = [ 'Some test data ...\n'];
// Create a new blob from the data.
Blob blob = new Blob(body, 'text/plain', 'native');
// Create a data:url which points to that data.
String url = Url.createObjectUrlFromBlob(blob);
// Create a link to navigate to that data and download it.
AnchorElement link = new AnchorElement()
..href = url
..download = 'random_file.txt'
..text = 'Download Now!';
// Insert the link into the DOM.
var p = querySelector('#text');
p.append(link);
}
The code of Seth solves indeed part of the problem. To make it a bit more complete, I'm now using the following:
void doPdfFileRequest(String url) {
var request = new HttpRequest();
request.open('GET', url);
request.responseType = "blob";
request.withCredentials = false;
request.setRequestHeader("Accept", _httpAcceptHeader);
request.setRequestHeader("Authorization", "bearer " + token);
request.onReadyStateChange
.listen((r) => onData(request, "filename.pdf"));
request.send();
}
void onData(HttpRequest request, String filename) {
if (request.readyState == HttpRequest.DONE && request.status == 200) {
if (!isIE()) {
var contentType = request.getResponseHeader("content-type");
AnchorElement downloadLink = new AnchorElement(
href: Url.createObjectUrlFromBlob(request.response));
downloadLink.rel = contentType;
downloadLink.download = filename;
var event = new MouseEvent("click", view: window, cancelable: false);
downloadLink.dispatchEvent(event);
} else {
var href = Url.createObjectUrlFromBlob(request.response);
window.open(href, "_self");
}
}
}
A few things to notice. Instead of using the downloadLink.click(), a mouse event is constructed to ensure that it works on Firefox as well as on Safari and Chrome. Firefox seems not to handle the click() otherwise. Binding it to the DOM as is done in the code of Seth isn't necessary.
Internet Explorer doesn't understand the download attribute, so nothing will happen, therefore a window.open is used to at least have it work (though not ideal) on IE, it's redirecting to self to avoid being hit by the pop up blocker.
There are solutions that convert the result download result to Base64 first and put it in a data:mimetype href, using the blob this isn't necessary.
A nice way to set the filename on the file to download would be through the content disposition header, but this header is marked as unsafe, so cannot be used. The filename is now set in the code.
Another note, notice that a HttpRequest is used instead http.get(), The HttpRequest allows you to set the responseType, in this case blob, which can be transformed into a object url.
I am trying to do HTTP post to a google form, from a C program in my device. For a legacy form, the active form submission URL looks like below. I used these text to do a URL encoded HTTP/1.1 POST, which was successful.
https://spreadsheets.google.com/formResponse?formkey=FORMKEY&ifq&entry.0.single=ENTRY1&entry.2.single=ENTRY2&submit=Submit
For the new google form (whichever you create from google drive now), below is the active submit URL. When I use this for HTTP post, I get the Bad Request with error Code 400.
https://docs.google.com/forms/d/FORMKEY/formResponse?entry.1252261890=ENTRY1&entry.1890412746=ENTRY2
What has changed between old and new google form? I see similar problem faced by somebody elsewhere but no solution so far. Thanks for your help.
This is a javascript (google apps script) POST that is working on a current form (with one field!) Pperhaps you can get what you need from this:
function sendHttpPost() {
var fish = "I am a mackerel";
var payload =
{
"entry.2071121932" : fish
};
// Because payload is a JavaScript object, it will be interpreted as
// an HTML form. (We do not need to specify contentType; it will
// automatically default to either 'application/x-www-form-urlencoded'
// or 'multipart/form-data')
var options =
{
"method" : "POST",
"payload" : payload,
"muteHttpExceptions": true
};
var response = UrlFetchApp.fetch("https://docs.google.com/forms/d/this is the form ... key/formResponse", options);
Logger.log(response.getContentText())
}
Good day, I'm working on a Servlet that must return a PDF file and the message log for the processing done with that file.
So far I'm passing a boolean which I evaluate and return either the log or the file, depending on the user selection, as follows:
//If user Checked the Download PDF
if (isDownload) {
byte[] oContent = lel;
response.setContentType("application/pdf");
response.addHeader("Content-disposition", "attachment;filename=test.pdf");
out = response.getOutputStream();
out.write(oContent);
} //If user Unchecked Download PDF and only wants to see logs
else {
System.out.println("idCompany: "+company);
System.out.println("code: "+code);
System.out.println("date: "+dateValid);
System.out.println("account: "+acct);
System.out.println("documentType: "+type);
String result = readFile("/home/gianksp/Desktop/Documentos/Logs/log.txt");
System.setOut(System.out);
// Get the printwriter object from response to write the required json object to the output stream
PrintWriter outl = response.getWriter();
// Assuming your json object is **jsonObject**, perform the following, it will return your json object
outl.print(result);
outl.flush();
}
Is there an efficient way to return both items at the same time?
Thank you very much
HTTP protocol doesn't allow you to send more than one HTTP response per one HTTP request. With this restriction in mind you can think of the following alternatives:
Let client fire two HTTP requests, for example by specifyingonclick event handler, or, if you returned HTML page in the first response, you could fire another request on window.load or page.ready;
Provide your for an opportunity of choosing what he'd like to download and act in a servlet accordingly: if he chose PDF - return PDF; if he chose text - return text and if he chose both - pack them in an archive and return it.
Note that the first variant is both clumsy and not user friendly and as far as I'm concerned should be avoided at all costs. A page where user controls what he gets is a much better alternative.
You could wrap them in a DTO object or place them in the session to reference from a JSP.