Read request body without deleting it - crystal-lang

So I have a MyHandler which has to know what's inside the request body:
class MyHandler
include HTTP::Handler
def call(context)
p "MyHandler got body: " + context.request.body.not_nil!.gets_to_end
call_next(context)
end
end
server = HTTP::Server.new(42, [MyHandler.new]) do |context|
p "Server got body: " + context.request.body.not_nil!.gets_to_end
end
As expected, after MyHandler has read, server receives a empty body. How can copy the body without modifying original context?

Crystal supports streaming request bodies, which means that once you stream in the request, the IO is EOF and the second handler can't read any data.
A simple way to solve this would be to retrieve the entire content using body_string = context.request.body.try(&.gets_to_end), then set the request body to the returned string using context.request.body = body_string. This buffers the entire body to memory then sets the body to the buffer stored in memory. The downside to this approach is that an attacker can send an infinitely sized request body and eat all the memory on your server, causing a DOS attack. Another disadvantage is that if you're working with binary data, you would then need to convert the string into a slice using #to_slice to work with it.
One way to solve the DOS attack problem - if you have a maximum body size in mind - is to fail the request if the body is too large:
if body = context.request.body
body_io = IO::Memory.new
bytes_read = IO.copy(body, body_io, limit: 1_048_576) # 1GiB limit
body_io.rewind
if bytes_read == 1_048_576
# Fail request
end
# use body_io
body_io.rewind # Reset body_io to start
context.request.body = body_io
end
If you need to accept an infinitely sized body, and not buffer it to memory, you should create a custom IO implementation which wraps the existing body IO and runs the required transform inside IO#read(Bytes). This method is quite complex, and the previous method covers almost all situations, so I won't provide a code sample for this option.

Related

Postman accessing the stored results in the database leveldb

So I have a set of results in Postman from a runner on a collection using some data file for iterations - I have the stored data from the runner in the Postman app on Linux, but I want to know how I can get hold of the data. There seems to be a database hidden away in the ~/.config directory (/Desktop/file__0.indexeddb.leveldb) - that looks like it has the data from the results there.
Is there anyway that I can get hold of the raw data - I want to be able to save the results from the database and not faff around with running newman or hacking a server to post the results and then save, I already have 20000 results in a collection. I want to be able to get the responseData from each post and save it to a file - I will not execute the posts again, I need to just work out a way
I've tried KeyLord, FastNoSQL (this crashes), levelDBViewer(Jar), but not having any luck here.
Any suggestions?
inline 25024 of runner.js a simple yet hack for small numbers of results I can do the following
RunnerResultsRequestListItem = __WEBPACK_IMPORTED_MODULE_2_pure_render_decorator___default()(_class = class RunnerResultsRequestListItem extends __WEBPACK_IMPORTED_MODULE_0_react__["Component"] {
constructor(props) {
super(props);
var text = props.request.response.body,
blob = new Blob([text], { type: 'text/plain' }),
anchor = document.createElement('a');
anchor.download = props.request.ref + ".txt";
anchor.href = (window.webkitURL || window.URL).createObjectURL(blob);
anchor.dataset.downloadurl = ['text/plain', anchor.download, anchor.href].join(':');
anchor.click();
it allows me to save but obviously I have to click save for now, anyone know how to automate the saving part - please add something here!

Use Scala Iterator to break up large stream (from string) into chunks using a RegEx match, and then operate on those chunks?

I'm currently using a not-very-Scala-like approach to parse large Unix mailbox files. I'm still learning the language and would like to challenge myself to find a better way, however, I do not believe I have a solid grasp on just what can be done with an Iterator and how to effectively use it.
I'm currently using org.apache.james.mime4j, and I use the org.apache.james.mime4j.mboxiterator.MboxIterator to get a java.util.Iterator from a file, as so:
// registers an implementation of a ContentHandler that
// allows me to construct an object representing an email
// using callbacks
val handler: ContentHandler = new MyHandler();
// creates a parser that parses a SINGLE email from a given InputStream
val parser: MimeStreamParser = new MimeStreamParser(configBuilder.build());
// register my handler
parser.setContentHandler(handler);
// Get a java.util.Iterator
val iterator = MboxIterator.fromFile(fileName).build();
// For each email, process it using above Handler
iterator.forEach(p => parser.parse(p.asInputStream(Charsets.UTF_8)))
From my understanding, the Scala Iterator is much more robust, and probably a lot more capable of handling something like this, especially because I won't always be able to fit the full file in memory.
I need to construct my own version of the MboxIterator. I dug through the source for MboxIterator and was able to find a good RegEx pattern to use to determine the beginning of individual email messages with, however, I'm drawing a blank from now on.
I created the RegEx like so:
val MESSAGE_START = Pattern.compile(FromLinePatterns.DEFAULT, Pattern.MULTILINE);
What I want to do (based on what I know so far):
Build a FileInputStream from an MBOX file.
Use Iterator.continually(stream.read()) to read through the stream
Use .takeWhile() to continue to read until the end of the stream
Chunk the Stream using something like MESSAGE_START.matcher(someString).find(), or use it to find the indexes the separate the message
Read the chunks created, or read the bits in between the indexes created
I feel like I should be able to use map(), find(), filter() and collect() to accomplish this, but I'm getting thrown off by the fact that they only give me Ints to work with.
How would I accomplish this?
EDIT:
After doing some more thinking on the subject, I thought of another way to describe what I think I need to do:
I need to keep reading from the stream until I get a string that matches my RegEx
Maybe group the previously read bytes?
Send it off to be processed somewhere
Remove it from the scope somehow so it doesn't get grouped the next time I run into a match
Continue to read the stream until I find the next match.
Profit???
EDIT 2:
I think I'm getting closer. Using a method like this gets me an iterator of iterators. However, there are two issues: 1. Is this a waste of memory? Does this mean everything gets read into memory? 2. I still need to figure out a way to split by the match, but still include it in the iterator returned.
def split[T](iter: Iterator[T])(breakOn: T => Boolean):
Iterator[Iterator[T]] =
new Iterator[Iterator[T]] {
def hasNext = iter.hasNext
def next = {
val cur = iter.takeWhile(!breakOn(_))
iter.dropWhile(breakOn)
cur
}
}.withFilter(l => l.nonEmpty)
If I understand correctly, you want to lazily chunk a large file delimited by a regex recognizable pattern.
You could try to return an Iterator for each request but the correct iterator management would not be trivial.
I'd be inclined to hide all file and iterator management from the client.
class MBox(filePath :String) {
private val file = io.Source.fromFile(filePath)
private val itr = file.getLines().buffered
private val header = "From .+ \\d{4}".r //adjust to taste
def next() :Option[String] =
if (itr.hasNext) {
val sb = new StringBuilder()
sb.append(itr.next() + "\n")
while (itr.hasNext && !header.matches(itr.head))
sb.append(itr.next() + "\n")
Some(sb.mkString)
} else {
file.close()
None
}
}
testing:
val mbox = new MBox("so.txt")
mbox.next()
//res0: Option[String] =
//Some(From MAILER-DAEMON Fri Jul 8 12:08:34 2011
//some text AAA
//some text BBB
//)
mbox.next()
//res1: Option[String] =
//Some(From MAILER-DAEMON Mon Jun 8 12:18:34 2012
//small text
//)
mbox.next()
//res2: Option[String] =
//Some(From MAILER-DAEMON Tue Jan 8 11:18:14 2013
//some text CCC
//some text DDD
//)
mbox.next() //res3: Option[String] = None
There is only one Iterator per open file and only the safe methods are invoked on it. The file text is realized (loaded) only on request and the client gets just what's requested, if available. Instead of all lines in one long String you could return each line as part of a collection, Seq[String], if that's more applicable.
UPDATE: This can be modified for easy iteration.
class MBox(filePath :String) extends Iterator[String] {
private val file = io.Source.fromFile(filePath)
private val itr = file.getLines().buffered
private val header = "From .+ \\d{4}".r //adjust to taste
def next() :String = {
val sb = new StringBuilder()
sb.append(itr.next() + "\n")
while (itr.hasNext && !header.matches(itr.head))
sb.append(itr.next() + "\n")
sb.mkString
}
def hasNext: Boolean =
if (itr.hasNext) true else {file.close(); false}
}
Now you can .foreach(), .map(), .flatMap(), etc. But you can also do dangerous things like .toList which will load the entire file.

How to get result from WS call in play framework/scala?

I have a big trouble with getting value from WS.url call.
val x =WS.url("https://www.google.com/recaptcha/api/siteverify?
secret=XX&response="+captcha).get().map {
response =>response.body}
When i try
Console.println("X: "+x)
I don't have expected value but:
X: scala.concurrent.impl.Promise$DefaultPromise#e17c7c
BUT, when i try to print value println(response.body) inside map function it works fine.
I also tried playframework tutorial but the same results.
So, how can I assign result of GET call into some variable?
Please don't assemble your own query string, use the withQueryString method.
There are two solutions to your problem: blocking and non-blocking. Blocking will mean that your request's thread will idle until the HTTP call completes. Non-blocking is preferred and you can provide Play with a Future to complete the request with. All you have to do is instead of Action use Action.async in your controller.
val captchaResponse: Future[String] =
WS.url("https://www.google.com/recaptcha/api/siteverify")
.withQueryString("secret" -> "XX", "response" -> "captcha")
.get()
.map(_.body)
// Non-blocking solution:
captchaResponse.map {
body =>
Console.println("X: " + body)
Ok(views.html.page(body.toBoolean))
}
// Blocking solution:
import scala.concurrent.duration._
val x = Await.result(captchaResponse, 3.seconds)
Console.println(x)

Obfuscate email?

Let's start by saying I have a very large project, part of this project is to grab a user recovery action status, and a user email, and send it through a service layer back to the front end of the application. The catch is, the email needs to be altered on the back end so it doesn't get sent plain text. What I mean by this is, when the value gets populated on the back end, I need to have some code to modify it so it will have a format like this: j*****e#domain.com. This absolutely needs to be done in the method that I'm working on(which honestly isn't very big). Here is the method I have that will grab the status from another method within the same class, as well as grabbing the email of the user:
public CredentialRecoveryResponse RecoveryResponse(CredentialRecoveryRequest request)
{
CredentialRecoveryResponse response = new CredentialRecoveryResponse();
response.Status = RecoverCredentials(request);
if (response.Status == UserRecoveryActionStatus.Success)
{
User usr = UserRepository.GetByID(request.UserID);
response.Email = usr.EmailAddress;
}
return response;
}
Somehow, inside this method, I need to take that usr.EmailAddress and modify it do "block" or change the values to "*" for all characters except the first and last characters before the "#domain.com" portion. Is there a quick and easy way to do this within the method that way the whole email address isn't getting sent back through the wire?
Here's one take:
private static string ObfuscateEmail(string email)
{
return Regex.Replace(email, "^(?<name>[^#]+)", m => {
string match = m.Groups["name"].Value;
return match[0] + new String('*', match.Length - 1);
});
}
What is this doing?
The method uses Regex.Replace and passes a lambda function to do the actual replacement
The regex pattern simply says match everything to the left of the # sign and create a named group called 'name'.
The lambda function then takes the first character of the match and appends to it a series of asterisks, using an overload of the String method (char, int) which repeats that char N number of times. It's N-1 here since the first char is unobfuscated.

IIS7 native module - setting request entity with file handle

So I'm working on an IIS7 native module, and part of what it will do is process some fairly large uploaded files. I'm trying to work on ways to reduce the memory footprint of the module while doing this.
One thing I was able to do with the processed response data, which is nice, is pass open file handles to the underlying system instead of memory buffers by using the HttpDataChunkFromFileHandle chunk types. I'm trying to do the same thing with the request data, but so far no joy.
What I am doing is first I am reading all of the request data, processing it, and then setting the entity chunks in the raw HTTP_REQUEST like this:
HTTP_REQUEST* rawRequest = _context->GetRequest()->GetRawHttpRequest();
rawRequest->EntityChunkCount = 1;
rawRequest->pEntityChunks = new HTTP_DATA_CHUNK[1];
rawRequest->pEntityChunks[0].DataChunkType = HttpDataChunkFromFileHandle;
rawRequest->pEntityChunks[0].FromFileHandle.FileHandle = _requestFile.handle();
rawRequest->pEntityChunks[0].FromFileHandle.ByteRange.StartingOffset.QuadPart = 0;
rawRequest->pEntityChunks[0].FromFileHandle.ByteRange.Length.QuadPart = _requestFile.size();
and returning RQ_NOTIFICATION_CONTINUE.
This results in a 403 response from the server.
If I use a memory chunk instead, it works correctly:
char* bufferOut = static_cast<char*>(_context->AllocateRequestMemory( _requestFile.size() ));
std::memcpy( bufferOut, _requestFile.map( 0, _requestFile.size() ), _requestFile.size() );
HTTP_REQUEST* rawRequest = _context->GetRequest()->GetRawHttpRequest();
rawRequest->EntityChunkCount = 1;
rawRequest->pEntityChunks = new HTTP_DATA_CHUNK[1];
rawRequest->pEntityChunks[0].DataChunkType = HttpDataChunkFromMemory;
rawRequest->pEntityChunks[0].FromMemory.pBuffer = (PVOID)bufferOut;
rawRequest->pEntityChunks[0].FromMemory.BufferLength = _requestFile.size()
So... is HttpDataChunkFromFileHandle just not supported for request entities? Or is there something else I need to do for it to work?
Do I need to set any specific security permissions on the file?