Skip to content

UTF-8 Javascipt is not served properly in development mode #62

Closed
@crystalcyril

Description

@crystalcyril

I have been using Struts2 and recently switched to Grails 2.3.1.

I have tried using asset pipeline version 1.5.3. However, when I am trying to serving a Javascript library which contains Unicode characters, I found that it does not properly serve the Javacsript file.

Environment

How to reproduce

  1. create a new grails project using command "grails create-app"
  2. copy the momentjs javascript file to the grails-app/asset/javascripts/moment-with-langs.js
  3. in the GSP file, use the <asset:javascript src="moment-with-langs.js" />
  4. in grails console, enter "run-app" and visit the URL of the javascript. e.g. http://localhost:8080/foobar/asset/moment-with-langs.js?compile=false
  5. search for the keyword "japan"
  6. You can see that the unicode character is currupted.

Things to note

  • The same Javascript file is used in my old Apache Struts2 framework without any modification.

What I have tried

  • I tried specifying character encoding in the tag e.g. <asset:javascript charset="utf-8" >. But it does not work
  • I have tried modifying the momentjs javascript source to DOS/UNIX format as well as playing around with UTF-8 and Unicode format.

Thoughts and Possible Solution

Obviously it is something related to character encoding, however it can happen in several areas

  • Reading the source Javascript file.
  • Serving to user agent via controller.
  • Tomcat connector.

After some tracing I found that the Grails controller AssetsController maybe the cause of problem.

Git commit: 3266b01
Source file: https://github.com/bertramdev/asset-pipeline/blob/3266b0168219723753b617c1ea391c4469ec68e8/grails-app/controllers/asset/pipeline/AssetsController.groovy

if(assetFile) {
  response.setContentType(format)
  response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
  response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
  response.setDateHeader("Expires", 0); // Proxies.
  if(format == 'text/html') {
    render contentType: 'text/html', text: new String(assetFile)
  } else {
    response.outputStream << assetFile
    response.flushBuffer()
}

The code "response.outputStream << assetFile" seems not specifying any encoding.

So I made some minor changes to:

  1. specify HTTP response character encoding if "encoding" parameter is specified.
  2. use HttpResponse.getWriter() to write instead of outputStream.

Here is the code fix: (marked with // FIX between)

if(assetFile) {
  response.setContentType(format)
  response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
  response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
  response.setDateHeader("Expires", 0); // Proxies.
  // FIX begins
  if (params.encoding != null) {
    response.setCharacterEncoding(params.encoding);
  }
  // FIX ends
  if(format == 'text/html') {
    render contentType: 'text/html', text: new String(assetFile)
  } else {
    // FIX begins
    if (params.encoding != null) {
      def w = response.writer
      w.write(assetFile)
      w.flush()
    } else {
      response.outputStream << assetFile
      response.flushBuffer()
    }
    // FIX ends
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions