Hatena::ブログ(Diary)

メモなのですよ

2010-09-11

Multi-column Layout Module

| 14:09

早見表

プロパティ初期値
column-width <length> | auto auto
column-count <integer> | auto auto
columns <'column-width'> || <'column-count'> 各項目を参照
column-gap <length> | normal normal
column-rule-width <'border-width'> medium
column-rule-style <'border-style'> none
column-rule-color <color> -
column-rule <column-rule-width> || <border-style> || [ <color> | transparent ] 各項目を参照
break-before auto | always | avoid | left | right | page | column | avoid-page | avoid-column auto
break-after auto | always | avoid | left | right | page | column | avoid-page | avoid-column auto
break-inside auto | avoid | avoid-page | avoid-column auto
column-span 1 | all 1
column-fill auto | balance balance

参考

2010-07-31

FORM の action の URI にクエリをつけたまま GET で送信できなかったのでなんでか調べてみた

| 08:04

あれ?タイトルわかりづらい?

要はこういうことです

  1 <html>
  2 
  3 <body>
  4   <form method="GET" action="./form.html?key1=value1">
  5     <input type="text" name="key2" value="value2" />
  6     <input type="submit" value="test" />
  7   </form>
  8 </body>
  9 
 10 </html>

これでボタン押したときにクエリとして

  • key1 : value1
  • key2 : value2

が送られてきてほしいのです

しかし、結果として key2 しか送られてきません

...おい、key1 どこいった?

というわけでアクセスログを見てみると

::1 - - [25/Jul/2010:23:06:04 +0900] "GET /~xxx/form.html?key2=value2 HTTP/1.1" 200 191

となっています。はい、 key1 どっかに消えました

そこで以下のように GET, POST の両方を作って実験してみました

  1 <html>
  2 
  3 <body>
  4   <form method="GET" action="./form.php?key1=value1">
  5     method : GET <br />
  6     <input type="text" name="key2" value="value2" />
  7     <input type="submit" value="test" />
  8   </form>
  9 
 10   <form method="POST" action="./form.php?key1=value1">
 11     method : POST <br />
 12     <input type="text" name="key2" value="value2" />
 13     <input type="submit" value="test" />
 14   </form>
 15 
 16   <pre>
 17     $_GET : <?php var_dump($_GET); ?>
 18   </pre>
 19 
 20   <pre>
 21     $_POST : <?php var_dump($_POST); ?>
 22   </pre>
 23 
 24 
 25 </body>
 26 
 27 </html>

GET の結果

    $_GET : array(1) {
  ["key2"]=>
  string(6) "value2"
}
  
    $_POST : array(0) {
}

POST の結果

    $_GET : array(1) {
  ["key1"]=>
  string(6) "value1"
}
  
    $_POST : array(1) {
  ["key2"]=>
  string(6) "value2"
}

ここからわかる状況

  • GET : action で指定した URL の ? 以下を無視している
  • POST : action で指定した URL の通りに動作している

なんでこのようなことになっているのかは調べきれなかったので、また今度調べます

なんとなくブラウザの仕様なんじゃないかなーと思ったりもしました

(あれ?ブラウザの仕様なら HTML の定義のどっかに書いてあるのかな。。。でも見つけられんかった。。。

参考

http://labs.uechoco.com/blog/2007/11/htmlformactionget.html

http://chaichan.web.infoseek.co.jp/perlnote/perlnote2010-05-31.htm

2010-05-25

websocket の echo server を作ってみた

| 13:16

目的

  • html5 の websocket を使って簡単な通信をしてみる

ソース

websocket.html

  1 <!DOCTYPE html>
  2 
  3 <html>
  4 
  5 <head>
  6   <meta charset="utf-8">
  7 
  8   <script type="text/javascript">
  9     var ws = new WebSocket("ws://localhost:8080/");
 10 
 11     ws.onopen = function(event) {
 12       alert("websocket " + event + " is opened");
 13     };
 14 
 15     ws.onmessage = function(event) {
 16       alert(event.data);
 17     };
 18 
 19     function send() {
 20       var msg = document.getElementById("msg").value;
 21       ws.send(msg);
 22     }
 23 
 24   </script>
 25 </head>
 26 
 27 <body>
 28   <input type="text" id="msg" />
 29   <button onclick="send();">send</button>
 30 </body>
 31 
 32 </html>

echo_server.py

  1 import socket
  2 
  3 BUFSIZE = 4096
  4 
  5 host = socket.gethostbyname('localhost')
  6 port = 8080
  7 
  8 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  9 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 10 sock.bind((host, port))
 11 sock.listen(1)
 12 
 13 print 'waiting for connection...'
 14 
 15 (client_sock, client_addr) = sock.accept()
 16 msg = client_sock.recv(BUFSIZE)
 17 
 18 [head, body] = msg.split("\r\n", 1)
 19 header = {}
 20 
 21 for line in body.splitlines():
 22   if line == "":
 23     break
 24   else:
 25     [key, value] = line.split(": ", 1)
 26     header[key] = value.strip()
 27 
 28 # websocket handshake
 29 handshake  = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
 30 handshake += "Upgrade: %s\r\n" % header['Upgrade']
 31 handshake += "Connection: %s\r\n" % header['Connection']
 32 handshake += "WebSocket-Origin:%s\r\n" % header['Origin']
 33 handshake += "WebSocket-Location: ws://localhost:8080/\r\n"
 34 handshake += "\r\n"
 35 
 36 client_sock.send(handshake)
 37 
 38 print 'connection start'
 39 print '---------- ---------- ----------'
 40 
 41 while True:
 42   msg = client_sock.recv(BUFSIZE)
 43 
 44   if msg == "":
 45     break
 46   else:
 47     print "client: %s" % msg[0:len(msg)-1]
 48     client_sock.send(msg)
 49 
 50 client_sock.close()
 51 
 52 sock.close()
 53   
 54 print '---------- ---------- ----------'
 55 print 'connection end'
 56     

実行

connection start
---------- ---------- ----------
client: this
client: is
client: a
client: test
---------- ---------- ----------
connection end

考察

  • そもそも最初は通信自体がうまくいなかった
    • handshake を行わないと通信を確立してくれない
  • 47行目で受け取った文字列の後ろ2バイトを削っている
    • 終端文字として何か入ってるみたいだが文字化けするので削った

参考

2010-05-18

memcache クライアントを実装してみた その2

| 22:50

目的

  • 前回 methaneさんにコメントで指摘された部分の修正

ソース

  • 修正した関数は以下の2つなので今回はその部分のソースのみ載せます
    • set
    • get
 21   def set(self, key, value, exptime=0, flags=0):
 22     # translate value to binary
 23     pickled_value = pickle.dumps(value, 2)
 24     length = len(pickled_value)
 25     totalsent = 0
 26 
 27     # request
 28     sendmsg  = "set %s %d %s %d" % (key, flags, exptime, length)
 29     sendmsg += "\r\n"
 30     sendmsg += pickled_value
 31     sendmsg += "\r\n"
 32 
 33     while totalsent < length:
 34       sent = self.__sock.send(sendmsg[totalsent:])
 35       totalsent += sent
 36 
 37     # response
 38     recvmsg = ''
 39 
 40     while True:
 41       chunk = self.__sock.recv(BUFSIZE)
 42       recvmsg += chunk
 43 
 44       if recvmsg.find("\r\n") != -1:
 45         break
 46 
 47     # return
 48     if recvmsg == "STORED\r\n":
 49       return True
 50     else:
 51       return False
 52 
 53 
 54   def get(self, key):
 55     # request
 56     sendmsg = "get %s\r\n" % key
 57     length = len(sendmsg)
 58     totalsent = 0
 59 
 60     while totalsent < length:
 61       sent = self.__sock.send(sendmsg[totalsent:])
 62       totalsent += sent
 63 
 64     # response
 65     recvmsg = ''
 66 
 67     ## header
 68     header = ''
 69 
 70     while True:
 71       chunk = self.__sock.recv(BUFSIZE)
 72       recvmsg += chunk
 73 
 74       if header == '' and recvmsg.find("\r\n") != -1:
 75         [header, dummy] = recvmsg.split("\r\n", 1)
 76         break
 77 
 78     [dummy1, dummy2, dummy3, length] = header.split()
 79 
 80     ## body
 81     body = ''
 82     length = len(header) + len("\r\n") + int(length) + len("\r\n") + len("END\r\n")
 83 
 84     totalrecv = len(recvmsg)
 85 
 86     while totalrecv < length:
 87       chunk = self.__sock.recv(BUFSIZE)
 88       recvmsg += chunk
 89       totalrecv += len(chunk)
 90 
 91     [header, body] = recvmsg.split("\r\n", 1)
 92     [pickled_value, dummy1, dummy2] = body.rsplit("\r\n", 2)
 93 
 94     # return
 95     return pickle.loads(pickled_value)

修正点

  • set
    • value をバイナリに変換
    • メッセージを確実に全て送信
    • メッセージを少なくとも必要な分は全て受信
  • get
    • メッセージを確実に全て送信
    • メッセージの一つ目の改行文字"\r\n"で分割して header とする
      • header から set した value の長さを取得
    • メッセージの全ての長さを計算
      • header + "\r\n" + value + "\r\n" + "END\r\n"
    • メッセージを少なくとも必要な分は全て受信
    • バイナリから value へ変換

結果

  • BUFSIZE = 2 (recv が一度に受信する最大バイト数) として実行したが、前回と同様の結果が得られた

2010-05-03

memcache クライアントを実装してみた

| 13:32

目的

  • 主に自分の勉強です
  • クライアントはいろんな言語で実装されてるけど、やっぱり自分でやってみることに意義があるよね

実装項目

以下の3つを実装する

  • set
  • get
  • stats

ソース

my_memcache.py

  1 # -*- coding:utf-8 -*-
  2 import socket
  3 import pickle
  4 import base64
  5 
  6 class MyMemcache:
  7 
  8   def __init__(self):
  9     self.__sock = socket.socket()
 10 
 11   def __del__(self):
 12     self.__sock.close()
 13 
 14   def connect(self, host, port):
 15     self.__sock.connect((host, port))
 16 
 17   def close(self):
 18     self.__sock.close()
 19 
 20   def set(self, key, value, exptime=0, flags=0):
 21     serialized_value = base64.b64encode(pickle.dumps(value))
 22     self.__sock.send("set %s %d %s %d\r\n" % (key, flags, exptime, len(serialized_value)) )
 23     self.__sock.send("%s\r\n" % serialized_value)
 24 
 25     result = self.__sock.recv(64)
 26 
 27     if result == "STORED\r\n":
 28       return True
 29     else:
 30       return False
 31 
 32   def get(self, key):
 33     self.__sock.send("get %s\r\n" % key)
 34     lines = self.__sock.recv(1024).splitlines()
 35     serialized_value = lines[1]
 36     value = pickle.loads(base64.b64decode(serialized_value))
 37     return value
 38 
 39   def stats(self):
 40     self.__sock.send("stats\r\n")
 41     lines = self.__sock.recv(1024).splitlines()
 42 
 43     values = {}
 44 
 45     for line in lines:
 46       if line == 'END':
 47         break
 48 
 49       else:
 50         [dummy, key, value] = line.split()
 51         values[key] = value
 52 
 53     return values
 54 

解説

set

  • 整数文字列以外に、リストや辞書などもくることを考慮し、pickle を使って value を直列化した
  • 返り値が "STORED\r\n" のときに "True" を返すようにした
    • それ以外は "False" としたため、若干処理が甘いかもしれない

get

  • 返り値が3行の文字列なので、まず改行文字で分割した
    • そのうち2行目が set で登録した値である

stats

  • 以下のフォーマットで返ってくる
    • STAT version 1.2.8
  • なので、文字列を分割して辞書に登録する
    • 文字列 "END" が返ってきたら終了する

テストコード

my_memcache.py

 55 if __name__ == '__main__':
 56   memcache = MyMemcache()
 57   memcache.connect("localhost", 11211)
 58 
 59   print "########## stats ##########"
 60   print memcache.stats()
 61   print "\n"
 62 
 63   print "########## string ##########"
 64   key = "string_key"
 65   value = "string_value"
 66   print "key :" , key
 67   print "value :" , value
 68   print "---------- set ----------"
 69   print memcache.set(key, value)
 70   print "---------- get ----------"
 71   print memcache.get(key)
 72   print type(memcache.get(key))
 73   print "\n"
 74 
 75   print "########## list ##########"
 76   key = "list_key"
 77   value = ['a', 'b', 'c']
 78   print "key :" , key
 79   print "value :" , value
 80   print "---------- set ----------"
 81   print memcache.set(key, value)
 82   print "---------- get ----------"
 83   print memcache.get(key)
 84   print type(memcache.get(key))
 85   print "\n"
 86 
 87   print "########## tuple ##########"
 88   key = "tuple_key"
 89   value = (1, 2, 3)
 90   print "key :" , key
 91   print "value :" , value
 92   print "---------- set ----------"
 93   print memcache.set(key, value)
 94   print "---------- get ----------"
 95   print memcache.get(key)
 96   print type(memcache.get(key))
 97   print "\n"
 98 
 99   print "########## dict ##########"
100   key = "dict_key"
101   value = {'a' : 1, 'b' : 2, 'c' : 3}
102   print "key :" , key
103   print "value :" , value
104   print "---------- set ----------"
105   print memcache.set(key, value)
106   print "---------- get ----------"
107   print memcache.get(key)
108   print type(memcache.get(key))
109   print "\n"
110 

実行

$ python my_memcache.py
########## stats ##########
{'pid': '11509', 'total_items': '64', 'uptime': '47141', 'listen_disabled_num': '0', 'version': '1.2.8', 'limit_maxbytes': '134217728', 'rusage_user': '0.138425', 'bytes_read': '4793', 'accepting_conns': '1', 'rusage_system': '0.238628', 'cmd_get': '86', 'curr_connections': '6', 'threads': '2', 'total_connections': '35', 'cmd_set': '65', 'curr_items': '6', 'get_misses': '0', 'cmd_flush': '0', 'evictions': '0', 'bytes': '618', 'connection_structures': '7', 'bytes_written': '8006', 'time': '1272860539', 'pointer_size': '64', 'get_hits': '86'}


########## string ##########
key : string_key
value : string_value
---------- set ----------
True
---------- get ----------
string_value
<type 'str'>


########## list ##########
key : list_key
value : ['a', 'b', 'c']
---------- set ----------
True
---------- get ----------
['a', 'b', 'c']
<type 'list'>


########## tuple ##########
key : tuple_key
value : (1, 2, 3)
---------- set ----------
True
---------- get ----------
(1, 2, 3)
<type 'tuple'>


########## dict ##########
key : dict_key
value : {'a': 1, 'c': 3, 'b': 2}
---------- set ----------
True
---------- get ----------
{'a': 1, 'c': 3, 'b': 2}
<type 'dict'>
  • 格納した値が、ちゃんと返ってきてるよ、やったね
    • リストとか辞書は、型も正しいよ、やったね

参考

methanemethane 2010/05/06 17:02 memcacheのデータ部分は、バイナリデータを保存できるので、base64エンコードしない方が効率良いと思います。受け取るときには、splitlinesするのではなく、最初の\r\nを見つけて、そこまではヘッダとして解析し、ヘッダ内に記述しているバイト数だけ読み込むという動作をしたほうが良いと思います。バイナリ列の中に「\r\n」が混じっている可能性があるからです。

また、根本的な問題として、ソケットの send や recv は、1回で送受信したいデータを全部送受信してくれるとは限りません。試しに、数百キロバイト以上のデータを送受信してみてください。
英語が読めるのであれば、 http://docs.python.org/howto/sockets.html を参照してください。
send/recvが使いこなせるようになったら、今度はTCP_NODELAYなどについても調べてみると、より速いクライアントを作れるようになるかもしれません。

haru_tonharu_ton 2010/05/07 19:40 > methaneさん
memcache のデータ部分は、自分がまだしっかりと理解できていないようなので、調べ直してみます。
ソケットの send や recv はたしかに実装が甘かったので、紹介いただいたサイトを見て修正してみます。
ご指摘ありがとうございました!