Grails Cannot create a session after the response has been committed? - grails

I added a grails filter to redirect urls with www to non www url. After this change many errors have been triggered of this nature.
The change was to add a filter as shown below
class DomainFilters {
def filters = {
wwwCheck(uri:'/**') {
before = {
if (request.getServerName().toLowerCase().startsWith("www.")) {
int port = request.getServerPort();
if (request.getScheme().equalsIgnoreCase("http") && port == 80) {
port = -1;
}
URL redirectURL = new URL(request.getScheme(), request.getServerName().replaceFirst("www.",""), port, request.forwardURI);
response.setStatus(301)
response.setHeader("Location", redirectURL.toString())
response.flushBuffer()
}
}
}
}
}
The point where error occurs is
session['products-ids'] = sizes.join(",")
and the error is as follows
ERROR 2021-07-15 13:48:48,478 [ajp-bio-8109-exec-720] errors.GrailsExceptionResolver: IllegalStateException occurred when processing request: [GET] /payment/productsPurchaseSummary/976634
Cannot create a session after the response has been committed. Stacktrace follows:
java.lang.IllegalStateException: Cannot create a session after the response has been committed
at registration.PaymentController.productsPurchaseSummary(PaymentController.groovy:621)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
I think the cause is linked to the added filter but i am not sure what is causing the cannot create session error. I appreciate any insights. Thanks!
UPDATE:
In the logs at the point of error is this request which was 301 redirected because of the filter above.
185.191.171.18 - - [15/Jul/2021:13:48:48 -0600] "GET /payment/productsPurchaseSummary/976634 HTTP/1.1" 301 950
185.191.171.5 - - [15/Jul/2021:13:48:49 -0600] "GET /payment/productsPurchaseSummary/976634 HTTP/1.1" 200 6341

It seems to have fixed it after adding return false. I think if there is no return then it will continue to execute the controller action.
class DomainFilters {
def filters = {
wwwCheck(uri:'/**') {
before = {
if (request.getServerName().toLowerCase().startsWith("www.")) {
int port = request.getServerPort();
if (request.getScheme().equalsIgnoreCase("http") && port == 80) {
port = -1;
}
URL redirectURL = new URL(request.getScheme(), request.getServerName().replaceFirst("www.", ""), port, request.forwardURI);
response.setStatus(301)
response.setHeader("Location", redirectURL.toString())
response.flushBuffer()
return false
}
}
}
}
}

Related

How to expose only some routes depending on host

I have a Vapor app that needs to do most things authenticated via HTTPS, but also needs to receive unauthenticated PUT requests via HTTP.
Can I conditionalize my route definitions based on the server's host name or authentication type? How can I capture that information from the server?
If you start up the different instances of vapor using the command line argument --hostname, you can put this code in your configure.swift and then include different routes as needed per host. You will then get 404s if invalid routes are attempted on the wrong hosts.
if let index = env.arguments.index(of: "--hostname")
{
if env.arguments.count > index
{
let hostname = env.arguments[index+1]
if hostname == "hostA"
{
// load routes
}
else
{
// load other routes
}
}
}
An alternative is to use custom Middleware. Something like this enables the hostname being called in the request to be inspected and prohibited routes can be re-directed:
struct HostSpecificMiddleware:Middleware
{
func respond( to request: Request, chainingTo next: Responder ) throws -> Future<Response>
{
let host = request.http.headers.filter{ (arg) in let (name, _) = arg; return name == "Host" }[0]
if host.1 == "hostA:8080"
{
if request.http.url.path == "routeA"
{
throw Abort.redirect(to:"routeNotAllowed")
}
}
return try next.respond(to: request)
}
}
You can then configure the middleware into routes in configure.swift using:
let soMW = HostSpecificMiddleware()
let users = router.grouped(uriUsers).grouped(soMW)
The second approach gives you much more flexibility.

HTTPS routing suddenly stopped working in Grails

In my Grails 2.5.1 application , i was using a filter to use HTTPS with some controllers , everything was working fine but suddenly this filter is not working any more .
Filter :
def filters = {
all(controller:'checkout', action:'onlinePayment') {
before = {
if (!request.isSecure() /*&& !Environment.isDevelopmentMode()*/) {
def url = "https://" + request.serverName+':8443' + request.forwardURI
println "in filter"
redirect(url: url, permanent: true)
return false
}
}
after = { Map model ->
}
afterView = { Exception e ->
}
}
}
Here is the checkout page :
Also i found that no requests came to the filter as in filter was not printed out, is there something i need to check to fix this issue rather than this filter

BlackBerry ConnectionFactory timeout

I upgraded an existing application and in the new version I require OS 5 - one of the reasons was that I wanted to use ConnectionFactory for communicating via HTTP/HTTPS with our server without having to add all the URL parameters for using BES, BIS, Direct TCP, Wifi and so on.
The ConnectionFactory is now configured to choose the best way to connect to our services via preferred types.
My connection code looks like this:
ConnectionFactory connectionFactory = new ConnectionFactory();
BisBOptions bisOptions = new BisBOptions(BIS_SECRET);
connectionFactory.setTransportTypeOptions(TransportInfo.TRANSPORT_BIS_B, bisOptions);
connectionFactory.setConnectionMode(ConnectionFactory.ACCESS_READ_WRITE);
connectionFactory.setEndToEndDesired(true);
connectionFactory.setPreferredTransportTypes(new int[] { TransportInfo.TRANSPORT_BIS_B, TransportInfo.TRANSPORT_MDS,
TransportInfo.TRANSPORT_TCP_WIFI, TransportInfo.TRANSPORT_TCP_CELLULAR });
ConnectionDescriptor connectionDescriptor = connectionFactory.getConnection("https://myserver.com/serviceurl");
try {
HttpConnection con = (HttpConnection) connectionDescriptor.getConnection();
byte[] bytes = parameter.toString().getBytes(UTF_8);
con.setRequestProperty(CONTENT_LENGTH, String.valueOf(bytes.length));
os = con.openOutputStream();
os.write(bytes);
os.flush();
int responseCode = con.getResponseCode();
if (responseCode == 401) {
throw new InvalidCredentialsException("Invalid credentials");
} else if (responseCode != 200 && responseCode != 500) {
EventLogger.logEvent(RTSID, ("Response code " + responseCode + " " + con
.getResponseMessage()).getBytes(), EventLogger.ERROR);
EventLogger.logEvent(RTSID, bytes, EventLogger.ERROR);
throw new IOException("Invalid request");
}
is = con.openInputStream();
if (is != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int c = 0;
try {
c = is.read();
} catch (Exception ex) {
c = -1;
}
while (c >= 0) {
baos.write(c);
try {
c = is.read();
} catch (Exception ex) {
c = -1;
}
}
String response = new String(baos.toByteArray(), UTF_8);
try {
JSONObject jsonObject;
if (response.startsWith("[")) {
jsonObject = new JSONObject();
jsonObject.put(ARRAY, new JSONArray(response));
} else {
jsonObject = new JSONObject(response);
}
if (responseCode == 500) {
throw new Exception(jsonObject.getString("message"));
}
return jsonObject;
} catch (JSONException e) {
EventLogger.logEvent(RTSID, ("Exception occured: " + e.toString()).getBytes(),
EventLogger.ERROR);
}
}
} finally {
if (is != null) {
try {
is.close();
} catch (Exception e) {
}
}
if (os != null) {
try {
os.close();
} catch (Exception e) {
}
}
if (con != null) {
try {
con.close();
} catch (Exception e) {
}
}
}
My problem is that this works not as well as when I added the connection parameters to my URL manually. I get errors in the server logs looking like the clients close the connection after some kind of timeout.
Here are some log examples:
93.186.30.120 - - [28/Jun/2012:15:50:08 +0200] "POST /service/methodX HTTP/1.1" 400 145 "-" "myapp VendorID/301" 10012567
93.186.22.118 - - [28/Jun/2012:16:30:56 +0200] "POST /service/methodY HTTP/1.1" 400 145 "-" "myapp VendorID/137" 10012435
74.82.68.35 - - [28/Jun/2012:16:53:23 +0200] "POST /service/methodZ HTTP/1.1" 400 145 "-" "myapp BlackBerry9650/6.0.0.524 VendorID/105" 10012644
The IP Adresses are from RIM Networks - so these are Connections comming from BIS
Those connections got status code 400 (Bad Request) from the server
The large numbers at the end of the line (e.g. 10012644) show the time the request was processed on the server in microseconds: 10012644 = about 10 seconds
Do the RIM servers add a connection timeout of 10 seconds? That seems fairly short!
The problem is difficult to reproduce - has anybody experienced something like that before?
I found the reason. Problem was caused by default configuration of Apache module mod_reqtimeout:
<IfModule reqtimeout_module>
# mod_reqtimeout limits the time waiting on the client to prevent an
# attacker from causing a denial of service by opening many connections
# but not sending requests. This file tries to give a sensible default
# configuration, but it may be necessary to tune the timeout values to
# the actual situation. Note that it is also possible to configure
# mod_reqtimeout per virtual host.
# Wait max 20 seconds for the first byte of the request line+headers
# From then, require a minimum data rate of 500 bytes/s, but don't
# wait longer than 40 seconds in total.
# Note: Lower timeouts may make sense on non-ssl virtual hosts but can
# cause problem with ssl enabled virtual hosts: This timeout includes
# the time a browser may need to fetch the CRL for the certificate. If
# the CRL server is not reachable, it may take more than 10 seconds
# until the browser gives up.
RequestReadTimeout header=20-40,minrate=500
# Wait max 10 seconds for the first byte of the request body (if any)
# From then, require a minimum data rate of 500 bytes/s
RequestReadTimeout body=10,minrate=500
</IfModule>
I guess BlackBerry clients wer hit harder because sending request body via RIM BIS infrastructure takes longer.
Set the value to 100 seconds and monitoring if clients are still affected.

Logic block in Grails URLMappings

My site has urls like 'http://someRandomUsername.mysite.com'.
Sometimes users will try urls like
'http://www.someRandomeUsername.mysite.com'. I'd like to have some
logic in my url mappings to deal with this.
With the mappings below when I hit the page , with or without the
unneeded www, I get:
2012-03-01 14:52:16,014 [http-8080-5] ERROR [localhost].[/ambit] -
Unhandled exception occurred whilst decorating page
java.lang.IllegalArgumentException: URL mapping must either provide a
controller or view name to map to!
Any idea how to accomplish this? The mapping is below.
Thanks!
Jason
static mappings = {
name publicMap: "/$action?/$id?" {
def ret = UrlMappings.check(request)
controller = ret.controller
userName = ret.userName
}
}
static check =
{ request ->
def tokens = request?.serverName?.split(/\./) as List ?: []
def ret = [controller:'info']
if(tokens.size() > 3 && token[0] == 'www')
{
ret.userName = tokens[1]
ret.controller = 'redirect'
ret.action = 'removeWWW'
}
else if(tokens.size() == 3)
{
ret.userName = tokens[0]
ret.controller = 'info'
}
return ret
}
Honestly, like DmitryB said, the best way to do this is via the web server, whether it's IIS, Apache, or Tomcat.
Having said that, I feel the best way to accomplish this in Grails would be using filters.
You could create something like this in your ~/conf directory:
public class StripFilters {
def filters = {
stripWWWFilter(controller: '*', action: '*') {
before = {
def tokens = request.serverName.tokenize(/\./) ?: []
if(tokens.size() > 3 && tokens[0] == 'www') {
def url = request.request.requestURL.toString().replace('www.', '')
redirect([url:url, params: [userName: tokens[1]], permanent: true])
return false
}
}
}
}
}
This should do the trick.

Getting details of the error inside a custom error page

I implemented a controller that handles HTTP error codes:
class ErrorController {
// 500
def internalserver = {
}
// 504
def timeout = {
}
// 404
def notfound = {
// just testing the values
log.debug "params: ${params}"
log.debug "response: ${response}"
log.debug "url: ${response.redirectURL}"
log.debug "object: ${response.content}"
}
// 403
def forbidden = {
}
}
Note that i already updated the UrlMappings too.
"500"(controller:'error', action:'internalserver')
"504"(controller:'error', action:'timeout')
"404"(controller:'error', action:'notfound')
"403"(controller:'error', action:'forbidden')
Is there a way to retrieve details inside each action?
i.e. for 404, the URL that was requested. for 500, the exception message or something.
looks like by simply referring to:
grails-app/views/error.gsp
will reveal all needed information.

Resources