playframework akka-http-server : warning Illegal header issue

Programming 2017.07.19 18:16

play 2.5->2.6으로 업그래이드 도중 만나게된 이슈 정리.


Playframework 2.6 부터는 akka-http-server를 기본 back-end 서버로 사용하도록 셋팅되어있다.


 [warn] a.a.ActorSystemImpl - Illegal header: Illegal 'accept-language' header: Invalid input '_', expected weight, OWS, 'EOI', listSep, ALPHA or '-' (line 1, column 3): zh_CN

  ^


만약 이러한 워닝을 만나게 된다면.


이유 : 

http://www.rfc-editor.org/errata_search.php?rfc=7230 규약에 위배 되는 사항들은 warn level로 경고를 날리게 되어있음.

그중 하나가 header에는 언더바(_) 문자가 들어갈 수 없다는 규약에 걸렸기 때문.


해결책 :


가장 좋은 방법은 잘못된 헤더를 안받으면 되는데, 웹서버를 nginx를 쓴다면 기본적으로 nginx가 잘못된 헤더를 걸러버리기 때문에 만나기 힘든 이슈.

그러나 나는 nginx의 header filter옵션을 꺼뒀기에 akka-http-server에서 만나게 됨.


위 warning을 끄는 방법은 


reference.conf 파일에 보면

# Enables/disables the logging of warning messages in case an incoming
# message (request or response) contains an HTTP header which cannot be
# parsed into its high-level model class due to incompatible syntax.
# Note that, independently of this settings, akka-http will accept messages
# with such headers as long as the message as a whole would still be legal
# under the HTTP specification even without this header.
# If a header cannot be parsed into a high-level model instance it will be
# provided as a `RawHeader`.
# If logging is enabled it is performed with the configured
# `error-logging-verbosity`. illegal-header-warnings = on


요런 설정이 있음.

위 설정을 off 시키면 됨.


이걸 playframework의 application.conf에 적용하면..


akka.http.parsing.illegal-header-warnings = off


sbt run으로 그냥 띄우면 적용되지 않음.

Dev 모드에서는 적용하지 않는다고 되어있음.


참고 : https://www.playframework.com/documentation/2.6.x/SettingsAkkaHttp


Note: In dev mode, when you use the run command, your application.confsettings will not be picked up by the server. This is because in dev mode the server starts before the application classpath is available. There are several other optionsyou’ll need to use instead.


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

[scala] String으로 Class method 호출하기. ruby의 send처럼..

Programming/scala 2016.03.23 23:53

스트링 문자를 통해서 해당 문자에 해당하는 클래스를 호출해보자.


나도 잘 모르기때문에 역시나 구글사마의 도움을 받아.. 스택오버플로우를 뒤지기 시작했다.

http://stackoverflow.com/questions/2060395/is-there-any-scala-feature-that-allows-you-to-call-a-method-whose-name-is-stored


2가지 방식으로 호출이 가능한데..

자바의 refliction기법으로 한다면 아래와 같다.


class A {
  def cat(s1: String, s2: String) = s1 + " " + s2
}
val a = new A
val hi = "Hello"
val all = "World"
val method = a.getClass.getMethod("cat",hi.getClass,all.getClass)
method.invoke(a,hi,all)


Scala를 통해서 하고자 한다면..

case class Caller[T>:Null<:AnyRef](klass:T) {
  def call(methodName:String,args:AnyRef*):AnyRef = {
    def argtypes = args.map(_.getClass)
    def method = klass.getClass.getMethod(methodName, argtypes: _*)
    method.invoke(klass,args: _*)
  }
}
implicit def anyref2callable[T>:Null<:AnyRef](klass:T):Caller[T] = new Caller(klass)
a call ("cat","Hi","there")
이러하다..


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

[javascript] convert underscore to camelcase

Programming 2015.05.29 14:47

var a = ["animal_cat","animal_dog","animal_lion"];

for(var i = 0 ; i < a.length ; i++){

a[i] = a[i].replace(/_([a-z])/g, function (g) { return g[1].toUpperCase(); });

}


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

[scala] Pattern matching / Type matching

Programming/scala 2015.04.08 14:03

스칼라를 쓰면서 이해하는데 많은 공이 들어가는게 matching이다.


매칭을 쓰다가 괴팍한 문제에 부닥혔다.

type matching을 이용하여 case문을 쓰려고 한 코드다.

val t = ("string", Int)


t match {

case t:(String, String) => println("second value is string")

case t:(String, Int) => println("second value is int")

case _ => println("others.")

}


이렇게 하면 두번째 매칭인 (String,Int)에 매칭이되어 "second value is int"가  나와야 할것 같은데.. 첫번째인 "second value is string" 가 나온다.

그러면서 워닝이 뜨지.

<console>:10: warning: fruitless type test: a value of type (String, Int) cannot also be a (String, String) (but still might match its erasure)

              case t:(String, String) => println("second value is string")

                     ^

<console>:11: warning: unreachable code

              case t:(String, Int) => println("second value is int")

이는 튜플의 제네릭 타입 추론이 불가능해서 인걸로 추측이 되는데..

t match ...  요부분에서 t의 타입을 정해주지 않았기에 컴파일러가 튜플 내의 타입을 추론하지 못한것 같다.

만약에 tuple이 val t = ("string", 0) 이렇게 되어있다면

아래와 같은 type matching이 가능하다.

(t: (Any, Any))  match {

  case (t1: String, t2: String) => "this is string"

  case (t1: String, t2: Int) => "this is int"

  case _ => "this is other!"

}

왠지 위의 코드로 원래의 문제였던 val t = ("string", Int)를 넣으면 제대로 매칭할 것 같지만.. 실제로 돌려보면 아니다...

실제로 돌려보면 아무것도 매칭되지 않아서 "this is other!"가 출력된다.


이를통해서 하나의 tuple내에서 type matching과 pattern matching이 병합적으로 사용되지 못함을 알 수 있다.

Int의 type은 Int가 아니고 object scala.Int이기 때문이다.


결국은 type matching으로 해결하기 힘들게 되어 나온코드는 pattern matching을 사용하는 코드였다.

val t = ("string", Int)

t._2 match {

case Float => "this is Float"

case Int => "this is Integer"

case _ => "this is other!"

}


두번째 인자인 Int == Int 이기 때문이다.

하지만 이것도 근본적인 해결책은 아니다. 왜냐하면

val t = ("string", "string val") 형태로 들어온것을 잡아내기 위해서는 

case "string val" 이라고 명시되어야 하기 때문이다.

case String 이라고 쓸수 없으니까...

결국 제일 마지막의 _ 에 걸릴수 밖에 없다.





저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

[ROR] benchmark sort

Programming 2014.07.02 17:18

#!/usr/bin/ruby 

require 'benchmark' 

ary = [] 
1000.times { 
 ary << {:bar => rand(1000)} 


n = 500 
Benchmark.bm(20) do |x| 
 x.report("sort")               { n.times { ary.sort{ |a,b| b[:bar] <=> a[:bar] } } } 
 x.report("sort reverse")       { n.times { ary.sort{ |a,b| a[:bar] <=> b[:bar] }.reverse } } 
 x.report("sort_by -a[:bar]")   { n.times { ary.sort_by{ |a| -a[:bar] } } } 
 x.report("sort_by a[:bar]*-1") { n.times { ary.sort_by{ |a| a[:bar]*-1 } } } 
 x.report("sort_by.reverse!")   { n.times { ary.sort_by{ |a| a[:bar] }.reverse } } 
end 



                           user     system      total        real

sort                   0.940000   0.000000   0.940000 (  0.940155)

sort reverse           0.970000   0.000000   0.970000 (  0.969526)

sort_by -a[:bar]       0.390000   0.010000   0.400000 (  0.400547)

sort_by a[:bar]*-1     0.390000   0.000000   0.390000 (  0.391629)

sort_by.reverse!       0.370000   0.000000   0.370000 (  0.373811)


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

[ROR] rails generate not null 제약조건 변경.

Programming 2014.06.18 11:20


> rails g migration change_field_name_to_table_name field_name:data_type


class ChangeFieldNameToTableName < ActiveRecord::Migration

  def change

    change_column_null(:table_name, :field_name, true)

  end

end






저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

[ROR] ActiveRecord module - create on duplicate key update

Programming 2014.03.03 22:13

module ActiveRecord

  # = Active Record Persistence

  module Persistence

    extend ActiveSupport::Concern


    def create_on_duplicate_key_update!(keys)

      #keys = duplicate_keys.is_a? Array ? duplicate_keys : [duplicate_keys] 

      keys.collect! { |k| k.to_s }

      klass = self.class

      attributes_with_values = arel_attributes_with_values_for_create(attribute_names)

      column_hash = klass.connection.schema_cache.columns_hash klass.table_name

      db_columns_with_values = []

      attributes_with_values.map { |attr, value|

        real_column = column_hash[attr.name]

        db_columns_with_values << [real_column, value] if value.present?

        if (value.blank? && ['created_at', 'updated_at'].include?(real_column.name))

          now = DateTime.now

          db_columns_with_values << [real_column, now]

          self.send("#{attr.name}=",now)

        end

      }

      sql = "INSERT INTO #{self.class.quoted_table_name} (#{db_columns_with_values.collect{|c| klass.connection.quote_column_name(c.first.name)}.join(', ')})"

      sql << " VALUES (#{db_columns_with_values.collect{|c| klass.connection.quote(c.last)}.join(', ')}) "

      sql << " ON DUPLICATE KEY UPDATE "

      sql << sql_for_on_duplicate_key_update(keys,db_columns_with_values)


      klass.connection.execute sql

    end


    private

    def sql_for_on_duplicate_key_update(keys,db_columns_with_values)

      connection = self.class.connection

      results = []

      db_columns_with_values.map do |column, value|

        results << "#{self.class.quoted_table_name}.#{connection.quote_column_name(column.name)}=#{connection.quote(value, column)}" unless keys.include?(column.name)

      end

      results.join(',')

    end

  end

end



저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

[ROR 팁]Rails rake test에서 특정 기능만 테스트하기!

Programming 2014.02.26 16:59

rake test를 돌릴 때 원하는 기능의 테스트들만 한번에 돌리고자 할 때... 유용한 팁


만약 디렉토리가


test/controllers/blog_controller_test.rb

test/models/blog_model_test.rb

test/jobs/blog_job_test.rb 

...


이런식으로 작성이 되었다면 blog에 관련된 테스트만 돌리고싶다! 는 needs다.


namespace :test do

  rule "" do |t|

    # rake test:blog

    if /test:(.*)(:([^.]+))?$/.match(t.name)

      arguments = t.name.split(":")[1..-1]

      file_name = arguments.first

      test_name = arguments[1..-1]


      Dir["test/**/#{file_name}*_test.rb"].each {|path|

        result = `ruby -Ilib:test #{path}`

        puts "=========[ #{file_name} test result ]================================================================"

        puts result[result.index("Finished tests")..-1].to_s.split('\n')

        puts "==========================================================================================="

      }

    end

  end

end




아무 .rake 파일을 만들어서 lib/tasks 밑에 넣어주면 된다.

저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

[Java] 초간단 Mail 보내기 클래스.

Programming 2013.01.07 19:47

초간단 Mail 보내기 클래스 만들기!


pom.xml

<dependency>

<groupId>javax.mail</groupId>

<artifactId>mail</artifactId>

<version>1.4.3</version>

</dependency>


MailSender.java

import javax.mail.MessagingException;

import javax.mail.internet.MimeMessage;


import org.springframework.mail.javamail.JavaMailSenderImpl;

import org.springframework.mail.javamail.MimeMessageHelper;


import com.devsisters.game.oven2.domain.IapErrorReport;


public class MailSender {

    private static final String HOST = "smtp.host.com";

    private static final String EMAIL_ADDR = "sender@host.com";

    private static final String EMAIL_PWD = "password";


    private static JavaMailSenderImpl sender = new JavaMailSenderImpl();


    public static void SendIapErrorMail(String title, String msg, String toMail) {


sender.setHost(MAIL_HOST);

sender.setUsername(EMAIL_ADDR);

sender.setPassword(EMAIL_PWD);


MimeMessage message = sender.createMimeMessage();

MimeMessageHelper helper = new MimeMessageHelper(message);

try {

    helper.setTo(toMail);

    helper.setSubject(title);

    helper.setText(msg);

    helper.setFrom(EMAIL_ADDR);

    sender.send(message);

} catch (MessagingException e) {

    e.printStackTrace();

}

    }

}


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

리눅스 한달 이상 지난 로그 삭제 크론.

Programming 2012.12.12 17:56

한달이 지나 쓸모없는 로그를 삭제하는 크론을 등록할때.


00 02 * * * /usr/bin/find /usr/local/tomcat/log -atime +30 -exec rm -f {} \;


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License


티스토리 툴바