Golang HTTP Readrequest

Source: Internet
Author: User

How does the server parse into a specific request object after receiving the HTTP data sent by the client? Let's see how Golang is handled.

Let's start with a concrete example: just Method,url and Proto inside HTTP

package mainimport  ("Bufio" "FMT" _  "io" "net/http" "Net/textproto" "Strings") Func main ()   {request ()}//Of course This example is relatively simple, just through \ n to parse out a few fields func request ()  {paths :=  "/xxx" br :=  Bufio. Newreader (Strings. Newreader ("get "  + paths +  " http/1.1\r\nhost: test\r\n\r\n")) tp :=  textproto. Newreader (BR) var s stringvar err errorif s, err = tp. ReadLine ();  err != nil {fmt. Printf ("prase err: %v \n",  err)}fmt. Printf ("readline restult: %v \n",  s) req := new (http. Request) req. Method, req. Requesturi, req. Proto, _ = parserequestline (s) fmt. Printf ("prase result %v \n",  req)}func parserequestline (line string)   ( Method, requesturi, proto string, ok bool)  {s1 := strings. Index (line,  " ") s2 := strings. IndeX (line[s1+1:],  " ") if s1 < 0 | |  s2 < 0 {return}s2 += s1 + 1return line[:s1], line[s1+1  : s2], line[s2+1:], true}//--------------------------------output is://readline restult:  GET /xxx HTTP/1.1 //prase result &{GET <nil> HTTP/1.1  0 0 map[] <nil> 0 [] false  map[] map[] <nil>  map[]  /xxx <nil>}

take a closer look at Golang http source

Func readrequest (B *bufio. Reader)   (req *request, err error)  {tp := newtextprotoreader (b)  // Encapsulates a reader structure req = new (Request)// first line: get /index.html http/1.0   //the parsing var s stringif s, err = tp of the first line of information. ReadLine ();  err != nil {return nil, err}defer func ()  { Puttextprotoreader (TP) If err == io. Eof {err = io. Errunexpectedeof}} () var ok bool //parsing req of the first line of information. Method, req. Requesturi, req. Proto, ok = parserequestline (s) if !ok {return nil, &badstringerror{" Malformed http request ",  s}}rawurl := req. Requesturi //client the requested url//http corresponds to Protomajor and protominorif req. Protomajor, req. Protominor, ok = parsehttpversion (req. Proto);  !ok {return nil, &badstringerror{"Malformed&nbSp Http version ",  req. proto}}// connect requests are used two different ways, and  Neither uses a full url:// the standard use is to tunnel  HTTPS through an HTTP proxy.// It looks like  "connect  www.google.com:443 HTTP/1.1 ", and the parameter is// just the  authority section of a url. this information should go in  Req. Url. Host.//// the net/rpc package also uses connect, but there the  parameter is a path// that starts with a slash. it can  be parsed with the regular URL parser,// and the path  Will end up in req. Url. path, where it needs to be in&nbsporder for// rpc to work.//handle the method ==  "CONNECT"   details justauthority :=  req. method ==  "CONNECT"  && !strings. Hasprefix (rawurl,  "/") if justauthority {rawurl =  "/http"  + rawurl}                 //http url Parsing if  req. Url, err = url. Parserequesturi (Rawurl); err != nil {return nil, err}if justauthority  {// strip the bogus  "/http"  back off.req.URL.Scheme =  ""}//  SUBSEQUENT&NBSP;LINES:&NBSP;KEY:&NBSP;VALUE.MIMEHEADER,&NBSP;ERR&NBSP;:=&NBSP;TP. Readmimeheader () key-value data if err != nil {return nil,  in  //http header Err}req. Header = header (Mimeheader)// rfc2616: must treat//get /index.html http/1.1 Host: www.google.com// aNd//get http://www.google.com/index.html http/1.1//host: doesntmatter// the same.   in the second case, any host line is ignored.req.host  = req. Url. Hostif req. host ==  ""  {req. Host = req. Header.get ("Host")}delete (req. header,  "host")  //remove the host from the map in the header Fixpragmacachecontrol (req. Header)  //set Cache-control field Err = readtransfer (req, b) if err != nil { Return nil, err}req. Close = shouldclose (req. Protomajor, req. Protominor, req. Header, false)  //If you need to close Return req, nil}

parsehttpversion (resolves http protomajor and Protominor)

Func parsehttpversion (vers string)   (Major, minor int, ok bool)  {const  big = 1000000 // arbitrary upper bound switch vers {case   "http/1.1":return 1, 1, truecase  "http/1.0": return 1, 0, true}if  !strings. Hasprefix (vers,  "http/")  {return 0, 0, false}dot := strings. Index (vers,  ".") If dot < 0 {return 0, 0, false}major, err := strconv. Atoi (Vers[5:dot]) if err != nil | |  major < 0 | |  major > big {return 0, 0, false}minor, err = strconv. Atoi (vers[dot+1:]) if err != nil | |  minor < 0 | |  minor > Big {return 0, 0, false}return major, minor,  True

Setting the Cache-control field

Func Fixpragmacachecontrol (header header) {if HP, OK: = header["Pragma"]; OK && len (HP) > 0 && hp[0] = = "No-cache" {if _, PRESENTCC: = header["Cache-control"];!PRESENTCC {header["cache-control"] = []string{"No-cache"}}}}

Whether you need to close

Func Shouldclose (major, minor int, header header, removecloseheader bool) bool {if major < 1 {return true} else if Majo r = = 1 && Minor = = 0 {//This condition determines long connection if!strings. Contains (Strings. ToLower (Header.get ("Connection")), "Keep-alive") {return True}return false} else {//Todo:should split on commas, toss Su Rrounding White space,//and check each field. Judge the connection field if strings. ToLower (Header.get ("Connection")) = = "Close" {if Removecloseheader {header. Del ("Connection")}return True}}return false}

A long Readtransfer method for distinguishing between request and response

 msg is *request or *response.func readtransfer (Msg interface{}, r  *bufio. Reader)   (err error)  {t := &transferReader{RequestMethod:  "GET"}// unify  inputisresponse := falseswitch rr := msg. (type)  {case *response:t. Header = rr. Header t.statuscode = rr. Statuscodet.protomajor = rr. Protomajort.protominor = rr. Protominort.close = shouldclose (t.protomajor, t.protominor, t.header, true) isResponse  = trueif rr. Request != nil {t.requestmethod = rr. Request.method}case *request:t.header = rr. Headert.protomajor = rr. Protomajort.protominor = rr. Protominor// transfer semantics for requests are exactly like those  for// responses with status code 200, responding&nbSp;to a get methodt. Statuscode = 200default:panic ("Unexpected type")}// default to http/1.1if t . Protomajor == 0 && t.protominor == 0 {t.protomajor, t. Protominor = 1, 1}// transfer encoding, content lengtht. Transferencoding, err = fixtransferencoding (T.requestmethod, t.header) if err !=  nil {return err}reallength, err := fixlength (IsResponse, t.StatusCode,  t.requestmethod, t.header, t.transferencoding) if err != nil {return  err}if isresponse && t.requestmethod ==  "HEAD"  {if n, err  := parsecontentlength (T.header.get ("Content-length")); err != nil {return  err} else {t.contentlength = n}} else {t.contentlength = reallength}//  traileRt. Trailer, err = fixtrailer (t.header, t.transferencoding) if err != nil { return err}// if there is no content-length or chunked  TRANSFER-ENCODING&NBSP;ON&NBSP;A&NBSP;*RESPONSE//&NBSP;AND&NBSP;THE&NBSP;STATUS&NBSP;IS&NBSP;NOT&NBSP;1XX,  204 or 304, then the body is unbounded.// see rfc2616,  section 4.4.switch msg. (type)  {case *response:if reallength == -1  &&!chunked (t.transferencoding)  &&bodyallowedforstatus (T.StatusCode)  {//  Unbounded body.t.close = true}}// prepare body reader.  contentlength  < 0 means chunked encoding// or close connection when  finished, since multipart is not supported yet//Notice here contentlength <  0  represents  chunked encoding  or has finished receiving data, close connection switch {case chunked (t.transferencoding): if nobodyexpected (T. Requestmethod)  {t.Body = eofReader} else {t.Body = &body{src:  Internal. Newchunkedreader (R),  hdr: msg, r: r, closing: t.close}}case reallength  == 0:t.body = eofreadercase reallength > 0:t.body = &body {Src: io. Limitreader (r, reallength), closing: t.close}default:// reallength < 0,  i.e.  "Content-length"  not mentioned in headerif t.close {// close  semantics  (i.e. http/1.0) T.body = &body{src: r, closing: t.close} } else {// persistent connection  (i.e. http/1.1) t.Body = eofReader}}//  unify outputswitch rr := msg. (type)  {case *request:rr. Body = t.bodyrr.contenTlength = t.contentlengthrr.transferencoding = t.transferencodingrr.close = t. Closerr.trailer = t.trailercase *response:rr. Body = t.bodyrr.contentlength = t.contentlengthrr.transferencoding = t. Transferencodingrr.close = t.closerr.trailer = t.trailer}return nil}

Summary: If you want to write the HTTP server, these basic processing work can draw on

Golang HTTP Readrequest

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.