[
  {
    "path": ".gitignore",
    "content": "*.o\n*.swp\n*github.com*\n*bin/\n*pkg/\n*.log*\n*jzlservice/\n.DS_Store\nnohup.out\n"
  },
  {
    "path": "README.md",
    "content": "\n## 短链接服务\n\n#### 短连接的原理\n\n很多人一定想的是**短连接是通过一定的算法将长链接变成短连接的，然后访问的时候再还原**，恩，非常高大上，但是仔细想想，怎么可能，那得多牛逼的压缩算法，多长的url都可以压缩为几个字节，而且还能还原，还是无损压缩。\n\n所以，实际上，短连接生成核心就两个字：**数数**，就是不停的自增一个数，然后有个表保存每个数和原始链接的对应关系，访问短连接的时候将原是连接取出来。\n\n知道了原理就好弄了，最简单的办法，就是用一个数组来存储，数组的索引就是短链接，数组的值就是原始链接，恩，完美，由于数组下标是短链接，那么获取短链接的时间复杂度是O(1)，同时生成短链接的时间复杂度也是O(1)\n\n#### 短链接服务的实现\n\n实现一个短链接服务，用数组固然可能，但也显得太LOW了吧，所以为了实现这个服务，从以下几个部分来实现。\n\n首先，给两个概念\n\n- **解析短链接**，就是请求是短连接，返回一个跳转的原始链接\n- **生成短链接**，就是有个长链接，返回生成的短链接\n\n##### 存储\n\n持久化的部分使用Redis数据库来实现，很明显，key-value的结构很适合存在Redis中\n这部分主要在 shortlib/RedisAdaptor.go中\n\n##### 计数器\n\n数数的功能可以用Redis的自增功能实现，这样也保证了原子性，同样这部分也可以自己实现，因为go语言开线程很容易，专门开一个线程实现这个功能，通过channl来接受请求，保证是串行的就行了，不就是数数嘛，大家都会\n这部分在shortlib/RedisAdaptor.go和shortService/CountThread.go中，具体实现的时候通过配置文件的参数，返回一个高阶函数，调用的时候自动分配到不同的函数实现。\n\n##### 缓存服务\n\nRedis固然很快，但是我们还需要更快，要是热门数据存在内存中就更快了，而且还有个问题，就是热门的url要是有人不停的申请短连接会造成浪费，为了防止这个问题，自己实现了一个LRU模块，解析短链接的时候，命中了话直接返回结果，否则从Redis返回数据，如果是申请短链接的话，如果在LRU中，那不再重新生成短链接了。\n这部分主要在 shortlib/LRU.go中。\n\n##### 对外服务\n\n这一部分用的go的http框架，很容易实现高并发，没啥好说的，现在编程高并发不是问题了，连语言都自带这框架了。\n这部分包括shortlib/Router.go , shortService/OriginalProcessor.go,shortService/ShortProcessor.go 这几个文件。\n"
  },
  {
    "path": "config.ini",
    "content": "\n[server]\nport = 26719\nhostname = http://t.cn/ #前缀域名\n\n[service]\nthreads = 200\ncounter = redis # inner 使用内部还是Redis进行计数\n\n\n[redis]\nredishost = 10.254.33.20\nredisport = 32079\nstatus = false # 是否初始化Redis计数器\n\n"
  },
  {
    "path": "install.sh",
    "content": "#!/bin/zsh\n\ngo install shortService\n"
  },
  {
    "path": "src/shortService/CountThread.go",
    "content": "/*************************************************************************\n\t> File Name: CountThread.go\n\t> Author: Wu Yinghao\n\t> Mail: wyh817@gmail.com\n\t> Created Time: 一  6/15 16:19:18 2015\n ************************************************************************/\n\npackage main\n\nimport (\n\t//\t\"fmt\"\n\t\"shortlib\"\n)\n\nfunc CountThread(count_chan_in chan shortlib.CountChannl) {\n\n\tvar count int64\n\tcount = 1000\n\tfor {\n\t\tselect {\n\t\tcase ok := <-count_chan_in:\n\t\t\tcount = count + 1\n\t\t\tok.CountOutChan <- count\n\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/shortService/OriginalProcessor.go",
    "content": "/*************************************************************************\n\t> File Name: OriginalProcessor.go\n\t> Author: Wu Yinghao\n\t> Mail: wyh817@gmail.com\n\t> Created Time: 日  6/14 16:00:54 2015\n ************************************************************************/\n\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"shortlib\"\n)\n\ntype OriginalProcessor struct {\n\t*shortlib.BaseProcessor\n\tCount_Channl chan shortlib.CountChannl\n}\n\nconst POST string = \"POST\"\nconst TOKEN string = \"token\"\nconst ORIGINAL_URL string = \"original\"\nconst SHORT_URL string = \"short\"\n\n/*\n*\n*\n*\n{\n\t\"original\" : \"http://XX.XX.com/XXTTYD\",\n\t\"token\" : \"DFEdafaeaqh43da\"\n}\n*\n*\n*/\nfunc (this *OriginalProcessor) ProcessRequest(method, request_url string, params map[string]string, body []byte, w http.ResponseWriter, r *http.Request) error {\n\tif method != POST {\n\t\treturn errors.New(\"Create short url must be POST the information\")\n\t}\n\tvar bodyInfo map[string]interface{}\n\terr := json.Unmarshal(body, &bodyInfo)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ttoken, has_token := bodyInfo[TOKEN].(string)\n\toriginal_url, has_original_url := bodyInfo[ORIGINAL_URL].(string)\n\n\tif !has_token || !has_original_url {\n\t\treturn errors.New(\"Post info errors\")\n\t}\n\n\tif !shortlib.IsAllowToken(token) {\n\t\treturn errors.New(\"Token is not allow\")\n\t}\n\n\tif !shortlib.IsNormalUrl(original_url) {\n\t\treturn errors.New(\"Url is not normal\")\n\t}\n\n\tshort_url, err := this.createUrl(original_url)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tresponse, err := this.createResponseJson(short_url)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t//add head information\n\theader := w.Header()\n\theader.Add(\"Content-Type\", \"application/json\")\n\theader.Add(\"charset\", \"UTF-8\")\n\tio.WriteString(w, response)\n\n\treturn nil\n}\n\n//\n//生成short url\n//\n//\nfunc (this *OriginalProcessor) createUrl(original_url string) (string, error) {\n\n\tshort, err := this.Lru.GetShortURL(original_url)\n\tif err == nil {\n\t\t//\t\tfmt.Printf(\"[INFO] Match the short url : %v ===> %v\\n\",original_url,short)\n\t\treturn short, nil\n\t}\n\t/*\n\t\tcount, err := this.RedisCli.NewShortUrlCount()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tcount_c := make(chan int64)\n\t\tch:=shortlib.CountChannl{0,count_c}\n\t\tthis.Count_Channl <- ch\n\t\tcount := <- count_c\n\t*/\n\n\tcount, err := this.CountFunction()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tshort_url, err := shortlib.TransNumToString(count)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\t//将对应关系添加到LRU缓存中\n\tthis.Lru.SetURL(original_url, short_url)\n\treturn short_url, nil\n\n}\n\nfunc (this *OriginalProcessor) createResponseJson(short_url string) (string, error) {\n\n\tjson_res := make(map[string]interface{})\n\tjson_res[SHORT_URL] = this.HostName + short_url\n\n\tres, err := json.Marshal(json_res)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn string(res), nil\n}\n"
  },
  {
    "path": "src/shortService/Server.go",
    "content": "/*************************************************************************\n\t> File Name: Server.go\n\t> Author: Wu Yinghao\n\t> Mail: wyh817@gmail.com\n\t> Created Time: 日  6/14 16:00:54 2015\n ************************************************************************/\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"shortlib\"\n)\n\nfunc main() {\n\tvar configFile string\n\tflag.StringVar(&configFile, \"conf\", \"config.ini\", \"configure file full path\")\n\tflag.Parse()\n\n\t//读取配置文件\n\tfmt.Printf(\"[INFO] Read configure file...\\n\")\n\tconfigure, err := shortlib.NewConfigure(configFile)\n\tif err != nil {\n\t\tfmt.Printf(\"[ERROR] Parse Configure File Error: %v\\n\", err)\n\t\treturn\n\t}\n\n\t//启动Redis客户端\n\tfmt.Printf(\"[INFO] Start Redis Client...\\n\")\n\tredis_cli, err := shortlib.NewRedisAdaptor(configure)\n\tif err != nil {\n\t\tfmt.Printf(\"[ERROR] Redis init fail..\\n\")\n\t\treturn\n\t}\n\t//是否初始化Redis计数器，如果为ture就初始化计数器\n\tif configure.GetRedisStatus() {\n\t\terr = redis_cli.InitCountService()\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"[ERROR] Init Redis key count fail...\\n\")\n\t\t}\n\t}\n\n\t//不使用redis的情况下，启动短链接计数器\n\tcount_channl := make(chan shortlib.CountChannl, 1000)\n\tgo CountThread(count_channl)\n\n\tcountfunction := shortlib.CreateCounter(configure.GetCounterType(), count_channl, redis_cli)\n\t//启动LRU缓存\n\tfmt.Printf(\"[INFO] Start LRU Cache System...\\n\")\n\tlru, err := shortlib.NewLRU(redis_cli)\n\tif err != nil {\n\t\tfmt.Printf(\"[ERROR]LRU init fail...\\n\")\n\t}\n\t//初始化两个短连接服务\n\tfmt.Printf(\"[INFO] Start Service...\\n\")\n\tbaseprocessor := &shortlib.BaseProcessor{redis_cli, configure, configure.GetHostInfo(), lru, countfunction}\n\n\toriginal := &OriginalProcessor{baseprocessor, count_channl}\n\tshort := &ShortProcessor{baseprocessor}\n\n\t//启动http handler\n\trouter := &shortlib.Router{configure, map[int]shortlib.Processor{\n\t\t0: short,\n\t\t1: original,\n\t}}\n\n\t//启动服务\n\n\tport, _ := configure.GetPort()\n\taddr := fmt.Sprintf(\":%d\", port)\n\tfmt.Printf(\"[INFO]Service Starting addr :%v,port :%v\\n\", addr, port)\n\terr = http.ListenAndServe(addr, router)\n\tif err != nil {\n\t\t//logger.Error(\"Server start fail: %v\", err)\n\t\tos.Exit(1)\n\t}\n\n}\n"
  },
  {
    "path": "src/shortService/ShortProcessor.go",
    "content": "/*************************************************************************\n\t> File Name: ShortProcessor.go\n\t> Author: Wu Yinghao\n\t> Mail: wyh817@gmail.com\n\t> Created Time: 日  6/14 16:00:54 2015\n ************************************************************************/\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"shortlib\"\n)\n\ntype ShortProcessor struct {\n\t*shortlib.BaseProcessor\n}\n\nfunc (this *ShortProcessor) ProcessRequest(method, request_url string, params map[string]string, body []byte, w http.ResponseWriter, r *http.Request) error {\n\n\terr := shortlib.IsShortUrl(request_url)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\toriginal_url, err := this.GetOriginalURL(request_url)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfmt.Printf(\"REQUEST_URL: %v --- ORIGINAL_URL : %v \\n\", request_url, original_url)\n\thttp.Redirect(w, r, original_url, http.StatusMovedPermanently)\n\treturn nil\n}\n\nfunc (this *ShortProcessor) GetOriginalURL(request_url string) (string, error) {\n\n\toriginal_url, err := this.Lru.GetOriginalURL(request_url)\n\t//没有从LRU获取到地址\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn original_url, nil\n\n}\n"
  },
  {
    "path": "src/shortlib/Configure.go",
    "content": "/*************************************************************************\n\t> File Name: Configure.go\n\t> Author: Wu Yinghao\n\t> Mail: wyh817@gmail.com\n\t> Created Time: 日  6/14 16:00:54 2015\n ************************************************************************/\n\npackage shortlib\n\nimport (\n\t\"errors\"\n\t\"github.com/ewangplay/config\"\n\t\"strconv\"\n)\n\ntype Configure struct {\n\tConfigureMap map[string]string\n}\n\nfunc NewConfigure(filename string) (*Configure, error) {\n\tconfig := &Configure{}\n\n\tconfig.ConfigureMap = make(map[string]string)\n\terr := config.ParseConfigure(filename)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn config, nil\n}\n\nfunc (this *Configure) loopConfigure(sectionName string, cfg *config.Config) error {\n\n\tif cfg.HasSection(sectionName) {\n\t\tsection, err := cfg.SectionOptions(sectionName)\n\t\tif err == nil {\n\t\t\tfor _, v := range section {\n\t\t\t\toptions, err := cfg.String(sectionName, v)\n\t\t\t\tif err == nil {\n\t\t\t\t\tthis.ConfigureMap[v] = options\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn nil\n\t\t}\n\t\treturn errors.New(\"Parse Error\")\n\t}\n\n\treturn errors.New(\"No Section\")\n}\n\nfunc (this *Configure) ParseConfigure(filename string) error {\n\tcfg, err := config.ReadDefault(filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tthis.loopConfigure(\"server\", cfg)\n\tthis.loopConfigure(\"service\", cfg)\n\tthis.loopConfigure(\"redis\", cfg)\n\n\treturn nil\n}\n\n//服务信息\nfunc (this *Configure) GetPort() (int, error) {\n\n\tportstr, ok := this.ConfigureMap[\"port\"]\n\tif ok == false {\n\t\treturn 9090, errors.New(\"No Port set, use default\")\n\t}\n\n\tport, err := strconv.Atoi(portstr)\n\tif err != nil {\n\t\treturn 9090, err\n\t}\n\n\treturn port, nil\n}\n\nfunc (this *Configure) GetRedisHost() (string, error) {\n\tredishost, ok := this.ConfigureMap[\"redishost\"]\n\n\tif ok == false {\n\t\treturn \"127.0.0.1\", errors.New(\"No redishost,use defualt\")\n\t}\n\n\treturn redishost, nil\n}\n\nfunc (this *Configure) GetRedisPort() (string, error) {\n\tredisport, ok := this.ConfigureMap[\"redisport\"]\n\n\tif ok == false {\n\t\treturn \"6379\", errors.New(\"No redisport,use defualt\")\n\t}\n\n\treturn redisport, nil\n}\n\nfunc (this *Configure) GetRedisStatus() bool {\n\n\tstatus, ok := this.ConfigureMap[\"status\"]\n\tif ok == false {\n\t\treturn true\n\t}\n\n\tif status == \"true\" {\n\t\treturn true\n\t}\n\treturn false\n\n}\n\nfunc (this *Configure) GetHostInfo() string {\n\n\thost_name, ok := this.ConfigureMap[\"hostname\"]\n\tif ok == false {\n\t\treturn \"http://wusay.org/\"\n\t}\n\n\treturn host_name\n\n}\n\nfunc (this *Configure) GetCounterType() string {\n\n\tcount_type, ok := this.ConfigureMap[\"counter\"]\n\tif ok == false {\n\t\treturn \"inner\"\n\t}\n\n\treturn count_type\n\n}\n"
  },
  {
    "path": "src/shortlib/LRU.go",
    "content": "/*************************************************************************\n\t> File Name: LRU.go\n\t> Author: Wu Yinghao\n\t> Mail: wyh817@gmail.com\n\t> Created Time: 一  6/15 17:07:37 2015\n ************************************************************************/\n\npackage shortlib\n\nimport (\n\t\"container/list\"\n\t\"errors\"\n\t//\t\"fmt\"\n)\n\ntype UrlElement struct {\n\tOriginal string\n\tShort    string\n}\n\ntype LRU struct {\n\tHashShortUrl  map[string]*list.Element\n\tHashOriginUrl map[string]*list.Element\n\tListUrl       *list.List\n\tRedisCli      *RedisAdaptor\n}\n\nfunc NewLRU(redis_cli *RedisAdaptor) (*LRU, error) {\n\n\tlru := &LRU{make(map[string]*list.Element), make(map[string]*list.Element), list.New(), redis_cli}\n\treturn lru, nil\n}\n\nfunc (this *LRU) GetOriginalURL(short_url string) (string, error) {\n\n\telement, ok := this.HashShortUrl[short_url]\n\t//没有找到key,从Redis获取\n\tif !ok {\n\t\toriginal_url, err := this.RedisCli.GetUrl(short_url)\n\t\t//Redis也没有相应的短连接，无法提供服务\n\t\tif err != nil {\n\t\t\treturn \"\", errors.New(\"No URL\")\n\t\t}\n\t\t//更新LRU缓存\n\t\tele := this.ListUrl.PushFront(UrlElement{original_url, short_url})\n\t\tthis.HashShortUrl[short_url] = ele\n\t\tthis.HashOriginUrl[original_url] = ele\n\t\treturn original_url, nil\n\t}\n\n\t//调整位置\n\tthis.ListUrl.MoveToFront(element)\n\tele, _ := element.Value.(UrlElement)\n\treturn ele.Original, nil\n}\n\nfunc (this *LRU) GetShortURL(original_url string) (string, error) {\n\n\telement, ok := this.HashOriginUrl[original_url]\n\t//没有找到key，返回错误，重新生成url\n\tif !ok {\n\t\treturn \"\", errors.New(\"No URL\")\n\t}\n\n\t//调整位置\n\tthis.ListUrl.MoveToFront(element)\n\tele, _ := element.Value.(UrlElement)\n\t/*\n\t\tfmt.Printf(\"Short_Url : %v \\n\",short_url)\n\n\t\tfor iter:=this.ListUrl.Front();iter!=nil;iter=iter.Next(){\n\t\t\tfmt.Printf(\"Element:%v ElementAddr:%v\\n\",iter.Value,iter)\n\t\t}\n\t\tfmt.Printf(\"\\n\\n\\n\")\n\t\tfor key,value := range this.HashUrl{\n\t\t\tfmt.Printf(\"Key:%v ==== Value:%v\\n\",key,value)\n\t\t}\n\t*/\n\treturn ele.Short, nil\n\n}\n\nfunc (this *LRU) SetURL(original_url, short_url string) error {\n\n\tele := this.ListUrl.PushFront(UrlElement{original_url, short_url})\n\tthis.HashShortUrl[short_url] = ele\n\tthis.HashOriginUrl[original_url] = ele\n\t//将数据存入Redis中\n\t//fmt.Printf(\"SET TO REDIS :: short : %v ====> original : %v \\n\",short_url,original_url)\n\terr := this.RedisCli.SetUrl(short_url, original_url)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n\n}\n\nfunc (this *LRU) checkList() error {\n\n\treturn nil\n}\n"
  },
  {
    "path": "src/shortlib/LRU_test.go",
    "content": "/*************************************************************************\n\t> File Name: LRU_test.go\n\t> Author: Wu Yinghao\n\t> Mail: wyh817@gmail.com\n\t> Created Time: 一  6/15 19:18:06 2015\n ************************************************************************/\n\npackage shortlib\n\nimport (\n\t\"testing\"\n)\n\nfunc Test_SetURL(t *testing.T) {\n\n\tlru, _ := NewLRU(nil)\n\terr := lru.SetURL(\"key6\", \"value6\")\n\terr = lru.SetURL(\"key5\", \"value5\")\n\terr = lru.SetURL(\"key4\", \"value4\")\n\terr = lru.SetURL(\"key3\", \"value3\")\n\terr = lru.SetURL(\"key2\", \"value2\")\n\terr = lru.SetURL(\"key1\", \"value1\")\n\tlru.GetShortURL(\"key3\")\n\tif err != nil {\n\t\tt.Error(\"Fail....\", err)\n\t} else {\n\t\tt.Log(\"OK...\\n\")\n\t}\n\n}\n"
  },
  {
    "path": "src/shortlib/Processor.go",
    "content": "/*************************************************************************\n\t> File Name: Processor.go\n\t> Author: Wu Yinghao\n\t> Mail: wyh817@gmail.com\n\t> Created Time: 日  6/14 16:00:54 2015\n ************************************************************************/\npackage shortlib\n\nimport (\n\t\"net/http\"\n)\n\ntype Processor interface {\n\t/*\n\t*\t基础接口\n\t*\t参数：方法，url参数，请求体\n\t*\t返回：需要返回的http response\n\t */\n\tProcessRequest(method, request_url string, params map[string]string, body []byte, w http.ResponseWriter, r *http.Request) error\n}\n\ntype BaseProcessor struct {\n\tRedisCli      *RedisAdaptor\n\tConfigure     *Configure\n\tHostName      string\n\tLru           *LRU\n\tCountFunction CreateCountFunc\n}\n"
  },
  {
    "path": "src/shortlib/RedisAdaptor.go",
    "content": "/*************************************************************************\n  > File Name: RedisAdaptor.go\n  > Author: Wu Yinghao\n  > Mail: wyh817@gmail.com\n  > Created Time: 二  6/ 9 15:29:05 2015\n ************************************************************************/\npackage shortlib\n\nimport (\n\t//\"errors\"\n\t\"fmt\"\n\t\"github.com/garyburd/redigo/redis\"\n)\n\ntype RedisAdaptor struct {\n\tconn   redis.Conn\n\tconfig *Configure\n}\n\nconst SHORT_URL_COUNT_KEY string = \"short_url_count\"\n\nfunc NewRedisAdaptor(config *Configure) (*RedisAdaptor, error) {\n\tredis_cli := &RedisAdaptor{}\n\tredis_cli.config = config\n\n\thost, _ := config.GetRedisHost()\n\tport, _ := config.GetRedisPort()\n\n\tconnStr := fmt.Sprintf(\"%v:%v\", host, port)\n\tfmt.Printf(connStr)\n\tconn, err := redis.Dial(\"tcp\", connStr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tredis_cli.conn = conn\n\n\treturn redis_cli, nil\n}\n\nfunc (this *RedisAdaptor) Release() {\n\tthis.conn.Close()\n}\n\nfunc (this *RedisAdaptor) InitCountService() error {\n\n\t_, err := this.conn.Do(\"SET\", SHORT_URL_COUNT_KEY, 0)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n\n}\n\nfunc (this *RedisAdaptor) NewShortUrlCount() (int64, error) {\n\n\tcount, err := redis.Int64(this.conn.Do(\"INCR\", SHORT_URL_COUNT_KEY))\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn count, nil\n\n}\n\nfunc (this *RedisAdaptor) SetUrl(short_url, original_url string) error {\n\n\tkey := fmt.Sprintf(\"short:%v\", short_url)\n\t_, err := this.conn.Do(\"SET\", key, original_url)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (this *RedisAdaptor) GetUrl(short_url string) (string, error) {\n\n\tkey := fmt.Sprintf(\"short:%v\", short_url)\n\toriginal_url, err := redis.String(this.conn.Do(\"GET\", key))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn original_url, nil\n}\n"
  },
  {
    "path": "src/shortlib/Router.go",
    "content": "/*************************************************************************\n\t> File Name: Router.go\n\t> Author: Wu Yinghao\n\t> Mail: wyh817@gmail.com\n\t> Created Time: 日  6/14 16:00:54 2015\n ************************************************************************/\n\npackage shortlib\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"regexp\"\n)\n\ntype Router struct {\n\tConfigure  *Configure\n\tProcessors map[int]Processor\n}\n\nconst (\n\tSHORT_URL    = 0\n\tORIGINAL_URL = 1\n\tUNKOWN_URL   = 2\n)\n\n//路由设置\n//数据分发\nfunc (this *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\n\tstart := TimeNow()\n\trequest_url := r.RequestURI[1:]\n\taction, err := this.ParseUrl(request_url)\n\tif err != nil {\n\t\tfmt.Printf(\"[ERROR]parse url fail : %v \\n\", err)\n\t}\n\terr = r.ParseForm()\n\tif err != nil {\n\t\treturn\n\t}\n\tparams := make(map[string]string)\n\tfor k, v := range r.Form {\n\t\tparams[k] = v[0]\n\t}\n\tbody, err := ioutil.ReadAll(r.Body)\n\tif err != nil && err != io.EOF {\n\t\treturn\n\t}\n\n\tif r.Method == \"GET\" {\n\t\taction = 0\n\t} else {\n\t\taction = 1\n\t}\n\n\tprocessor, _ := this.Processors[action]\n\terr = processor.ProcessRequest(r.Method, request_url, params, body, w, r)\n\tif err != nil {\n\t\tfmt.Printf(\"[ERROR] : %v\\n\", err)\n\t}\n\tif action == 0 {\n\t\tDuringTime(start, \"REDIRECT URL \")\n\t} else {\n\t\tDuringTime(start, \"CREATE SHORTURL \")\n\t}\n\treturn\n}\n\nfunc (this *Router) ParseUrl(url string) (action int, err error) {\n\n\tif this.isShortUrl(url) {\n\t\treturn SHORT_URL, nil\n\t} else {\n\t\treturn ORIGINAL_URL, nil\n\t}\n\n}\n\nfunc (this *Router) isShortUrl(url string) bool {\n\n\tshort_url_pattern := \"XXXX\"\n\turl_reg_exp, err := regexp.Compile(short_url_pattern)\n\tif err != nil {\n\t\treturn false\n\t}\n\tshort_match := url_reg_exp.FindStringSubmatch(url)\n\tif short_match == nil {\n\t\treturn false\n\t}\n\n\treturn true\n\n}\n"
  },
  {
    "path": "src/shortlib/Utils.go",
    "content": "/*************************************************************************\n\t> File Name: Utils.go\n\t> Author: Wu Yinghao\n\t> Mail: wyh817@gmail.com\n\t> Created Time: 日  6/14 18:05:47 2015\n ************************************************************************/\n\npackage shortlib\n\nimport (\n\t\"container/list\"\n\t\"fmt\"\n\t\"time\"\n)\n\ntype CountChannl struct {\n\tOk           int64\n\tCountOutChan chan int64\n}\n\ntype CreateCountFunc func() (int64, error)\n\nfunc IsAllowToken(token string) bool {\n\treturn true\n\n}\n\nfunc IsNormalUrl(url string) bool {\n\treturn true\n}\n\nfunc TransNumToString(num int64) (string, error) {\n\n\tstartTime := TimeNow()\n\tvar base int64\n\tbase = 62\n\tbaseHex := \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\"\n\toutput_list := list.New()\n\tfor num/base != 0 {\n\t\toutput_list.PushFront(num % base)\n\t\tnum = num / base\n\t}\n\toutput_list.PushFront(num % base)\n\tstr := \"\"\n\tfor iter := output_list.Front(); iter != nil; iter = iter.Next() {\n\t\tstr = str + string(baseHex[int(iter.Value.(int64))])\n\t}\n\tDuringTime(startTime, \"TransNumToString\")\n\treturn str, nil\n}\n\nfunc TransStringToNum(str string) (int64, error) {\n\n\treturn 0, nil\n}\n\nfunc TimeNow() time.Time {\n\treturn time.Now()\n}\n\nfunc DuringTime(start time.Time, taskname string) {\n\n\tendTime := time.Now()\n\tfmt.Printf(\"[INFO] [ %v ] COST Time %v \\n\", taskname, endTime.Sub(start))\n\n}\n\nfunc IsShortUrl(short_url string) error {\n\treturn nil\n}\n\nfunc CreateCounter(count_type string, count_chan chan CountChannl, rediscli *RedisAdaptor) CreateCountFunc {\n\n\tif count_type == \"inner\" {\n\t\treturn func() (int64, error) {\n\t\t\tcount_c := make(chan int64)\n\t\t\tch := CountChannl{0, count_c}\n\t\t\tcount_chan <- ch\n\t\t\tcount := <-count_c\n\t\t\treturn count, nil\n\t\t}\n\t} else {\n\t\treturn func() (int64, error) {\n\t\t\tcount, err := rediscli.NewShortUrlCount()\n\t\t\tif err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t\treturn count, nil\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "start.sh",
    "content": "#!/bin/sh\n\n# 在后台启动snsscheduler-server服务\nnohup ./bin/ProxyServer &\n"
  },
  {
    "path": "stop.sh",
    "content": "#!/bin/sh\n\nkillall ProxyServer\n"
  },
  {
    "path": "test.sh",
    "content": "#!/bin/sh\n\ngo test ProxyServer\n"
  }
]