I have been digging into creating a custom filtering module written in C that can process post messages. Read the json payload, make a decision whether to proxy the message or return a 204 to the client.
I am having trouble reading the payload of the message, I have read through quite a few of the modules but am missing something. I have tried quite a few ways to parse the payload but can't seem to figure out how to just simply print it.
I have read through the echo module, redis module, Evan Miller's documentation, etc.
I know that I need to read the full message before I can process the payload but the callback has got me confused. I don't really understand the callback in ngx_http_read_client_request_body. I have tried to read the code but there really is almost no documentation in the code.
In the code below I have tried multiple things to get the request body, I tried reading r->request_body->buf, bufs once the full body was read. When I try to print the request body, I get segfault. I think the echo module would have been the closest to what I wanted to do but I got confused in the ngx_http_echo_wev_handler on how it was processing the request.
static ngx_int_t ngx_http_ortb_handler(ngx_http_request_t *r) {
ngx_int_t rc;
ngx_flag_t read_body;
if (r->method != NGX_HTTP_POST) {
read_body = 1;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Received Post");
if (read_body) {
rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "return code %ld", rc);
// What do I need to do here to read the body.
} else {
r->main->count++;
rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK) {
return rc;
}
ngx_http_upstream_init(r);
}
return NGX_DECLINED;
}
Related
When a client ab ends the reactor seems to indefinitely go into a polling state resulting in roughly 15% of the processor being used. If the client reconnects I'm still losing that 15%, I'm trying to determine what is lacking in my code to handle this properly.
When the client ab ends _socket.available() immediately returns false so in the else block I'm attempting to do the right thing. Doing the same thing I do when a client terminates normally, 'delete this' eliminates the processor issue but the next time a client connects I get an allocation error, I'd like to understand why that is, what's the difference? Just putting a sleep in there solves everything but onSocketReadable continues to be called with _socket.available() == false, so it remains as a sort of orphaned active reactor, what am I missing? I also tried stopping the reactor, that stops the processor use but the restarted client will no longer connect, there's something I don't understand there also, seems like a new reactor would be created just as it was initially?
void onSocketReadable(const AutoPtr<ReadableNotification>& pNf)
{
// some socket implementations (windows) report available
// bytes on client disconnect, so we double-check here
if (_socket.available())
{
// No FIFO for now
//int len = _socket.receiveBytes(_fifoIn);
char* buffer = new char[65535];
memset(buffer, 0, 65535);
_socket.setReceiveBufferSize(65535);
int n = _socket.receiveBytes(buffer, 65535);
std::string json = buffer;
delete [] buffer;
if (json == "SHUTDOWN\r\n")
{
delete this;
return;
}
try
{
std::string result = _processor.process(json,_sm);
result.append("\r\n");
_socket.sendBytes(result.data(), (int)result.length());
}
catch (Poco::Exception& e)
{
std::cout << e.message();
}
}
else
{
// delete this;
// return;
// _reactor.stop();
Sleep(10);
}
}
I am using as Web API what uses AuthorisationManager owin middleware to handle token based security.
My problem is that various errors within the response body have various different formats.
Within my api, I usually send errors back with the structure
{"code": "error code", "message": "error message"}
However some of the errors coming from the security may use
{"error": "error code", "error_description": "error message"}
or sometimes just
{"error": "error mesage"}
I would like to unify these to all have the same structure I use elsewhere, ie the
{"code": "error code", "message": "error message"}
I have seen quite a few posts on replacing a response body.
I first tried this method, ie using the DelegatingHandler. This worked in most cases, but it did not catch my authorization failed error messages comding out of my OAuthAuthorizationServerProvider
I next tried using a middleware approach as shown here.
Here is my full interpretation..
public override async Task Invoke(IOwinContext context)
{
try
{
// hold a reference to what will be the outbound/processed response stream object
var stream = context.Response.Body;
// create a stream that will be sent to the response stream before processing
using (var buffer = new MemoryStream())
{
// set the response stream to the buffer to hold the unaltered response
context.Response.Body = buffer;
// allow other middleware to respond
await this.Next.Invoke(context);
// Error codes start at 400. If we have no errors, no more to d0.
if (context.Response.StatusCode < 400) // <---- *** COMMENT1 ***
return;
// we have the unaltered response, go to start
buffer.Seek(0, SeekOrigin.Begin);
// read the stream
var reader = new StreamReader(buffer);
string responseBody = reader.ReadToEnd();
// If no response body, nothing to do
if (string.IsNullOrEmpty(responseBody))
return;
// If we have the correct error fields names, no more to do
JObject responseBodyJson = JObject.Parse(responseBody);
if (responseBodyJson.ContainsKey("code") && responseBodyJson.ContainsKey("message"))
return;
// Now we will look for the known error formats that we want to replace...
byte[] byteArray = null;
// The first one from the security module, errors come back as {error, error_description}.
// The contents are what we set (so are correct), we just want the fields names to be the standard {code, message}
var securityErrorDescription = responseBodyJson.GetValue("error_description");
var securityErrorCode = responseBodyJson.GetValue("error");
if (securityErrorDescription != null && securityErrorCode != null)
byteArray = CreateErrorObject(securityErrorCode.ToString(), securityErrorDescription.ToString());
// The next horrible format, is when a refresh token is just sends back an object with 'error'.
var refreshTokenError = responseBodyJson.GetValue("error");
if (refreshTokenError != null)
{
// We will give this our own error code
var error = m_resourceProvider.GetRefreshTokenAuthorisationError(refreshTokenError.ToString());
byteArray = CreateErrorObject(error.Item2, error.Item3);
}
else
{
byteArray = Encoding.ASCII.GetBytes(responseBody);
}
// Now replace the response (body) with our now contents
// <---- *** COMMENT2 ***
context.Response.ContentType = "application / json";
context.Response.ContentLength = byteArray.Length;
buffer.SetLength(0);
buffer.Write(byteArray, 0, byteArray.Length);
buffer.Seek(0, SeekOrigin.Begin);
buffer.CopyTo(stream);
}
}
catch (Exception ex)
{
m_logger.WriteError($"ResponseFormattingMiddleware {ex}");
context.Response.StatusCode = 500;
throw;
}
}
private byte[] CreateErrorObject(string code, string message)
{
JObject newMessage = new JObject();
newMessage["code"] = code;
newMessage["message"] = message;
return Encoding.ASCII.GetBytes(newMessage.ToString());
}
So this basically seemed to work, and catch ALL responses, which is good.
However, what I was hoping to do, is, when there is no error, (or the error is already in the correct format), just pass the response on without doing anything with it.
I am mainly thinking of some of my GETs, where the data may be large, I was hoping to avoid having to do the extra copying back. In the above code, where I have marked *** COMMENT1 ***, I have an early return to try to avoid this, ie the line...
// Error codes start at 400. If we have no errors, no more to d0.
if (context.Response.StatusCode < 400)
return;
The problem, is when I do this, I get no body at all returned, ie no data for all the GET calls, etc.
Is there a way to avoid this extra copying (ie at the line *** COMMENT2 ***) when we don't want to do any modifications?
Thanks in advance for any suggestions.
Adding an answer since it has a code snippet but this is really just a comment.
Our services use the delegatingHandler approach you mentioned you tried first. Do you have try/catch around the call to base.SendAsync. In this snippet the requestState is just a wrapper around the incoming request with some timers, loggers, etc. In many cases we replace the response as you are trying. I stepped through the exception and used the VS debugger immediate window to modify the error response. It works for me.(TM)
try
{
return base
.SendAsync(request, cancellationToken)
.ContinueWith(
(task, requestState) => ((InstrumentedRequest)requestState).End(task),
instrumentedRequest,
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default)
.Unwrap();
}
catch (Exception ex)
{
instrumentedRequest.PrematureFault(ex);
throw;
}
Is there a way to specify the wait time of retrying a message for a particular exception?
E.g. If object is in SomethingInProgress status, throws an SomethignInProgressException and I want to the message to be retry after 40m. Or is it more appropriate to raise a SomethingInProgressEvent and use bus.defer?
This is part of the reason why Rebus does not have the concept of second-level retries - I've simply not seen any way that this function could be created in a way that was generic and still flexible enough.
To answer your question shortly: No, there's no (built-in) way of varying the time between retries for a particular exception. In fact, there's no way to configure a wait time between retries at all - failing messages will be retried as fast as possibly, and then moved to the error queue if they keep failing to avoid "clogging up the pipes".
In your case, I suggest you do something like this:
public void Handle(MyMessage message) {
var headers = MessageContext.GetCurrent().Headers;
var deliveryAttempt = headers.ContainsKey("attempt_no")
? Convert.ToInt(headers["attempt_no"])
: 0;
try {
DoWhateverWithThe(message);
} catch(OneKindOfException e) {
if (deliveryAttempt > 5) {
bus.Advanced.Routing.ForwardCurrentMessage("error");
return;
}
bus.AttachHeader(message, "attempt_no", deliveryAttempt + 1);
bus.Defer(TimeSpan.FromSeconds(20), message);
} catch(AnotherKindOfException e) {
if (deliveryAttempt > 5) {
bus.Advanced.Routing.ForwardCurrentMessage("error");
return;
}
bus.AttachHeader(message, "attempt_no", deliveryAttempt + 1);
bus.Defer(TimeSpan.FromMinutes(2), message);
}
}
which I just wrote off the top of my head without being 100% certain that it actually compiles ... but the gist of it is that we track how many delivery attempts we've made in a custom header on the message, bus.Deferring the message an appropriate time span for each failed delivery attempt, immediately forwarding the message to the error queue when our max # of delivery attempts has been exceeded.
I hope that makes sense :)
A more recent example of how to do this is:
public async Task Handle(IFailed<MyMessage> message)
{
var maxAttempts = 10;
var optionalHeaders = new Dictionary<string, string>();
if (message.Headers != null && message.Headers.ContainsKey("attemptNumber"))
{
// increment the attempt number
var attemptNumber = int.Parse(message.Headers["attemptNumber"]);
attemptNumber++;
optionalHeaders.Add("attemptNumber", attemptNumber.ToString());
if (attemptNumber > maxAttempts)
{
// log I give up message, message will move to dead queue
return;
}
}
else
optionalHeaders.Add("attemptNumber", "1");
// if message failed to process, defer processing for 5 minutes and try again
await Bus.Defer(TimeSpan.FromMinutes(5), message.Message, optionalHeaders);
}
I'm using the Asp.Net WebClient to create an HTTP post.
The below code has try-catch block around the code which catches WebException:
try
{
using (MyWebClient wc = new MyWebClient())
{
wc.Headers[HttpRequestHeader.ContentType] = _lender.ContentType;
wc.Timeout = 200;
return _lender.GetResult(wc.UploadString(_lender.PostUri, _lender.PostValues));
}
}
catch (WebException ex)
{
return new ServiceError(ex.Status.ToString());
}
The main exceptions I'm looking for are timeouts. I've extended WebClient to allow me to set the timeout.
When I set the timeout to say 100ms, an exception is thrown as expected. I can get the WebException status as per the example (it returns "timeout"), however, I want to return status codes too.
If I extract the httpwebresponse using ex.Response I get a null value returned, when I was expecting an associated status code.
Why do I not get an HttpStatus.Request.Timeout?
I have the same problem and I realise a few things while I search for a solution.
WebExceptionStatus enum is not equivalent to http status code that the API you call returned. Instead it is a enum of possible error that may occour during a http call.
The WebExceptionStatus error code that will be returned when you receive an error (400 to 599) from your API is WebExceptionStatus.ProtocolError aka number 7 as int.
When you need to get the response body or the real http status code returned from the api, first you need to check if WebException.Status is WebExceptionStatus.ProtocolError. Then you can get the real response from WebExceptionStatus.Response and read its content.
Sometimes the timeout is handled by the caller (aka your code) so you do not have a response in that case. So you can look if WebException.Status is WebExceptionStatus.Timeout
This is an example:
try
{
...
}
catch (WebException webException)
{
if (webException.Status == WebExceptionStatus.ProtocolError)
{
var httpResponse = (HttpWebResponse)webException.Response;
var responseText = "";
using (var content = new StreamReader(httpResponse.GetResponseStream()))
{
responseText = content.ReadToEnd(); // Get response body as text
}
int statusCode = (int)httpResponse.StatusCode; // Get the status code
}
else if (webException.Status == WebExceptionStatus.ProtocolError)
{
// Timeout handled by your code. You do not have a response here.
}
// Handle other webException.Status errors. You do not have a response here.
}
I'm writing a thumbnail generator as per an example in the QtWebkit documentation. I would like to avoid screenshots of error pages such as 404 not found or 503 Internal server error.
However, the QWebPage::loadFinished() signal is always emitted with ok = true even when the page gives an HTTP error. Is there a way in QtWebkit to check the HTTP status code on a response?
Turns out you need to monitor the QNetworkAccessManager associated with your QWebPage and wait for a finished(...) signal. You can then inspect the HTTP response and check its status code by asking for the QNetworkRequest::HttpStatusCodeAttribute attribute.
It's better explained in code:
void MyClass::initWebPage()
{
myQWebPage = new QWebPage(this);
connect(
myQWebPage->networkAccessManager(), SIGNAL(finished(QNetworkReply *)),
this, SLOT(httpResponseFinished(QNetworkReply *))
);
}
void MyClass::httpResponseFinished(QNetworkReply * reply)
{
switch (reply->error())
{
case QNetworkReply::NoError:
// No error
return;
case QNetworkReply::ContentNotFoundError:
// 404 Not found
failedUrl = reply->request.url();
httpStatus = reply->attribute(
QNetworkRequest::HttpStatusCodeAttribute).toInt();
httpStatusMessage = reply->attribute(
QNetworkRequest::HttpReasonPhraseAttribute).toByteArray();
break;
}
}
There are more NetworkErrors to choose from, e.g. for TCP errors or HTTP 401.
This is what I'm using in a porting project. It checks the reply and decides to start backing off making request or not. The backing off part is in progress but I left the comments in.
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
Q_CHECK_PTR(reply);
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if (!statusCode.isNull() && statusCode.toInt() >= 400){
//INVALID_SERVER_RESPONSE_BACKOFF;
qDebug() << "server returned invalid response." << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
return;
}else if (!statusCode.isNull() && statusCode.toInt() != 200){
//INVALID_SERVER_RESPONSE_NOBACKOFF;
qDebug() << "server returned invalid response." << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
return;
}