github.com/rafael-santiago/cherry@v0.0.0-20161214151746-8ea42c6e9670/src/pkg/rawhttp/rawhttp.go (about)

     1  /*
     2  Package rawhttp implements conveniences related with HTTP request/reply operations.
     3   --
     4   *
     5   *                               Copyright (C) 2015 by Rafael Santiago
     6   *
     7   * This is a free software. You can redistribute it and/or modify under
     8   * the terms of the GNU General Public License version 2.
     9   *
    10  */
    11  package rawhttp
    12  
    13  import (
    14  	"fmt"
    15  	"io/ioutil"
    16  	"net/url"
    17  	"strings"
    18  )
    19  
    20  var charLT map[string]string
    21  var charLTInitialized bool
    22  
    23  func initCharLT() {
    24  	charLT = make(map[string]string)
    25  	charLT["%E2%82%AC"] = "%80"
    26  	charLT["%E2%80%9A"] = "%82"
    27  	charLT["%C6%92"] = "%83"
    28  	charLT["%E2%80%9E"] = "%84"
    29  	charLT["%E2%80%A6"] = "%85"
    30  	charLT["%E2%80%A0"] = "%86"
    31  	charLT["%E2%80%A1"] = "%87"
    32  	charLT["%CB%86"] = "%88"
    33  	charLT["%E2%80%B0"] = "%89"
    34  	charLT["%C5%A0"] = "%8A"
    35  	charLT["%E2%80%B9"] = "%8B"
    36  	charLT["%C5%92"] = "%8C"
    37  	charLT["%C5%8D"] = "%8D"
    38  	charLT["%C5%BD"] = "%8E"
    39  	charLT["%C2%90"] = "%90"
    40  	charLT["%E2%%80%98"] = "%91"
    41  	charLT["%E2%80%99"] = "%92"
    42  	charLT["%E2%80%9C"] = "%93"
    43  	charLT["%E2%80%9D"] = "%94"
    44  	charLT["%E2%80%A2"] = "%95"
    45  	charLT["%E2%80%93"] = "%96"
    46  	charLT["%E2%80%94"] = "%97"
    47  	charLT["%CB%9C"] = "%98"
    48  	charLT["%E2%84"] = "%99"
    49  	charLT["%C5%A1"] = "%9A"
    50  	charLT["%E2%80"] = "%9B"
    51  	charLT["%C5%93"] = "%9C"
    52  	charLT["%C5%BE"] = "%9E"
    53  	charLT["%C5%B8"] = "%9F"
    54  	charLT["%C2%A0"] = "%A0"
    55  	charLT["%C2%A1"] = "%A1"
    56  	charLT["%C2%A2"] = "%A2"
    57  	charLT["%C2%A3"] = "%A3"
    58  	charLT["%C2%A4"] = "%A4"
    59  	charLT["%C2%A5"] = "%A5"
    60  	charLT["%C2%A6"] = "%A6"
    61  	charLT["%C2%A7"] = "%A7"
    62  	charLT["%C2%A8"] = "%A8"
    63  	charLT["%C2%A9"] = "%A9"
    64  	charLT["%C2%AA"] = "%AA"
    65  	charLT["%C2%AB"] = "%AB"
    66  	charLT["%C2%AC"] = "%AC"
    67  	charLT["%C2%AD"] = "%AD"
    68  	charLT["%C2%AE"] = "%AE"
    69  	charLT["%C2%AF"] = "%AF"
    70  	charLT["%C2%B0"] = "%B0"
    71  	charLT["%C2%B1"] = "%B1"
    72  	charLT["%C2%B2"] = "%B2"
    73  	charLT["%C2%B3"] = "%B3"
    74  	charLT["%C2%B4"] = "%B4"
    75  	charLT["%C2%B5"] = "%B5"
    76  	charLT["%C2%B6"] = "%B6"
    77  	charLT["%C2%B7"] = "%B7"
    78  	charLT["%C2%B8"] = "%B8"
    79  	charLT["%C2%B9"] = "%B9"
    80  	charLT["%C2%BA"] = "%BA"
    81  	charLT["%C2%BB"] = "%BB"
    82  	charLT["%C2%BC"] = "%BC"
    83  	charLT["%C2%BD"] = "%BD"
    84  	charLT["%C2%BE"] = "%BE"
    85  	charLT["%C2%BF"] = "%BF"
    86  	charLT["%C3%80"] = "%C0"
    87  	charLT["%C3%81"] = "%C1"
    88  	charLT["%C3%82"] = "%C2"
    89  	charLT["%C3%83"] = "%C3"
    90  	charLT["%C3%84"] = "%C4"
    91  	charLT["%C3%85"] = "%C5"
    92  	charLT["%C3%86"] = "%C6"
    93  	charLT["%C3%87"] = "%C7"
    94  	charLT["%C3%88"] = "%C8"
    95  	charLT["%C3%89"] = "%C9"
    96  	charLT["%C3%8A"] = "%CA"
    97  	charLT["%C3%8B"] = "%CB"
    98  	charLT["%C3%8C"] = "%CC"
    99  	charLT["%C3%8D"] = "%CD"
   100  	charLT["%C3%8E"] = "%CE"
   101  	charLT["%C3%8F"] = "%CF"
   102  	charLT["%C3%90"] = "%D0"
   103  	charLT["%C3%91"] = "%D1"
   104  	charLT["%C3%92"] = "%D2"
   105  	charLT["%C3%93"] = "%D3"
   106  	charLT["%C3%94"] = "%D4"
   107  	charLT["%C3%95"] = "%D5"
   108  	charLT["%C3%96"] = "%D6"
   109  	charLT["%C3%97"] = "%D7"
   110  	charLT["%C3%98"] = "%D8"
   111  	charLT["%C3%99"] = "%D9"
   112  	charLT["%C3%9A"] = "%DA"
   113  	charLT["%C3%9B"] = "%DB"
   114  	charLT["%C3%9C"] = "%DC"
   115  	charLT["%C3%9D"] = "%DD"
   116  	charLT["%C3%9E"] = "%DE"
   117  	charLT["%C3%9F"] = "%DF"
   118  	charLT["%C3%A0"] = "%E0"
   119  	charLT["%C3%A1"] = "%E1"
   120  	charLT["%C3%A2"] = "%E2"
   121  	charLT["%C3%A3"] = "%E3"
   122  	charLT["%C3%A4"] = "%E4"
   123  	charLT["%C3%A5"] = "%E5"
   124  	charLT["%C3%A6"] = "%E6"
   125  	charLT["%C3%A7"] = "%E7"
   126  	charLT["%C3%A8"] = "%E8"
   127  	charLT["%C3%A9"] = "%E9"
   128  	charLT["%C3%AA"] = "%EA"
   129  	charLT["%C3%AB"] = "%EB"
   130  	charLT["%C3%AC"] = "%EC"
   131  	charLT["%C3%AD"] = "%ED"
   132  	charLT["%C3%AE"] = "%EE"
   133  	charLT["%C3%AF"] = "%EF"
   134  	charLT["%C3%B0"] = "%F0"
   135  	charLT["%C3%B1"] = "%F1"
   136  	charLT["%C3%B2"] = "%F2"
   137  	charLT["%C3%B3"] = "%F3"
   138  	charLT["%C3%B4"] = "%F4"
   139  	charLT["%C3%B5"] = "%F5"
   140  	charLT["%C3%B6"] = "%F6"
   141  	charLT["%C3%B7"] = "%F7"
   142  	charLT["%C3%B8"] = "%F8"
   143  	charLT["%C3%B9"] = "%F9"
   144  	charLT["%C3%BA"] = "%FA"
   145  	charLT["%C3%BB"] = "%FB"
   146  	charLT["%C3%BC"] = "%FC"
   147  	charLT["%C3%BD"] = "%FD"
   148  	charLT["%C3%BE"] = "%FE"
   149  	charLT["%C3%BF"] = "%FF"
   150  	charLTInitialized = true
   151  }
   152  
   153  func cherryDefaultHTTPReplyHeader(statusCode int, closeConnection bool) string {
   154  	var header = "HTTP/1.1 "
   155  	switch statusCode {
   156  	case 200:
   157  		header += "200 OK"
   158  		break
   159  
   160  	case 404:
   161  		header += "404 NOT FOUND"
   162  		break
   163  
   164  	case 403:
   165  		header += "403 FORBIDDEN"
   166  		break
   167  
   168  	default:
   169  		header += "501 NOT IMPLEMENTED"
   170  		break
   171  	}
   172  	if closeConnection {
   173  		header += "\n\rConnection: close\n" +
   174  			"Server: Cherry/0.1\n" +
   175  			"Accept-ranges: bytes\n" +
   176  			"Content-type: text/html\n" +
   177  			"Content-length: {{.content-length}}\n\n"
   178  	} else {
   179  		header += "200 Document follows\nContent-type: text/html\n\n"
   180  	}
   181  	return header
   182  }
   183  
   184  // MakeReplyBuffer assembles the reply buffer based on the statusCode.
   185  func MakeReplyBuffer(buffer string, statusCode int, closeConnection bool) []byte {
   186  	return []byte(strings.Replace(cherryDefaultHTTPReplyHeader(statusCode, closeConnection)+buffer,
   187  		"{{.content-length}}",
   188  		fmt.Sprintf("%d", len(buffer)),
   189  		-1))
   190  }
   191  
   192  // MakeReplyBufferByFilePath assembles the reply buffer base on the file data and the statusCode.
   193  func MakeReplyBufferByFilePath(filePath string, statusCode int, closeConnection bool) []byte {
   194  	buffer, err := ioutil.ReadFile(filePath)
   195  	if err != nil {
   196  		return []byte("")
   197  	}
   198  	tempReply := strings.Replace(cherryDefaultHTTPReplyHeader(statusCode, closeConnection)+string(buffer),
   199  		"{{.content-length}}",
   200  		fmt.Sprintf("%d", len(buffer)),
   201  		-1)
   202  	tempReply = strings.Replace(tempReply, "Content-type: text/html", getContentTypeFromFilePath(filePath), -1)
   203  	return []byte(tempReply)
   204  }
   205  
   206  func getContentTypeFromFilePath(filePath string) string {
   207  	if strings.HasSuffix(filePath, ".gif") {
   208  		return "Content-type: image/gif"
   209  	}
   210  	if strings.HasSuffix(filePath, ".jpeg") || strings.HasSuffix(filePath, ".jpg") {
   211  		return "Content-type: image/jpeg"
   212  	}
   213  	if strings.HasSuffix(filePath, ".png") {
   214  		return "Content-type: image/png"
   215  	}
   216  	if strings.HasSuffix(filePath, ".bmp") {
   217  		return "Content-type: image/bmp"
   218  	}
   219  	return "Content-type: text/plain"
   220  }
   221  
   222  // GetHTTPFieldFromBuffer returns data carried by some HTTP field inside a HTTP request.
   223  func GetHTTPFieldFromBuffer(field, buffer string) string {
   224  	index := strings.Index(buffer, field+":")
   225  	if index == -1 {
   226  		return ""
   227  	}
   228  	retval := ""
   229  	if len(buffer) > index+len(field) {
   230  		for _, b := range buffer[index+len(field+":"):] {
   231  			if b == '\n' || b == '\r' {
   232  				break
   233  			}
   234  			if len(retval) == 0 && (b == ' ' || b == '\t') {
   235  				continue
   236  			}
   237  			retval += string(b)
   238  		}
   239  	}
   240  	return retval
   241  }
   242  
   243  func utf8Unescape(data string) string {
   244  	if !charLTInitialized {
   245  		initCharLT()
   246  	}
   247  	var buffer = data
   248  	for k, v := range charLT {
   249  		buffer = strings.Replace(buffer, k, v, -1)
   250  	}
   251  	finalData, _ := url.QueryUnescape(buffer)
   252  	return finalData
   253  }
   254  
   255  func splitFields(buffer string) map[string]string {
   256  	dataValue := strings.Split(buffer, "&")
   257  	retval := make(map[string]string)
   258  	for _, dv := range dataValue {
   259  		set := strings.Split(dv, "=")
   260  		if len(set) == 2 {
   261  			retval[set[0]] = utf8Unescape(set[1])
   262  		}
   263  	}
   264  	return retval
   265  }
   266  
   267  // GetFieldsFromPost returns a map containing all fields in a HTTP post.
   268  func GetFieldsFromPost(buffer string) map[string]string {
   269  	if !strings.HasPrefix(buffer, "POST /") {
   270  		return make(map[string]string)
   271  	}
   272  	index := strings.Index(buffer, "\r\n\r\n")
   273  	if index == -1 || len(buffer) <= index+4 {
   274  		return make(map[string]string)
   275  	}
   276  	return splitFields(buffer[index+4:])
   277  }
   278  
   279  // GetFieldsFromGet returns a map containing all fields in a HTTP get.
   280  func GetFieldsFromGet(buffer string) map[string]string {
   281  	if !strings.HasPrefix(buffer, "GET /") {
   282  		return make(map[string]string)
   283  	}
   284  	var startIndex int
   285  	var endIndex int
   286  	for buffer[startIndex] != '&' {
   287  		startIndex++
   288  	}
   289  	for buffer[endIndex] != '\n' && buffer[endIndex] != '\r' && endIndex < len(buffer) {
   290  		endIndex++
   291  	}
   292  	return splitFields(buffer[startIndex+1 : endIndex])
   293  }