github.com/geniusesgroup/libgo@v0.0.0-20220713101832-828057a9d3d4/http/handler.go (about)

     1  /* For license and copyright information please see LEGAL file in repository */
     2  
     3  package http
     4  
     5  import (
     6  	"../convert"
     7  	"../log"
     8  	"../protocol"
     9  )
    10  
    11  type Handler struct{}
    12  
    13  // HandleIncomeRequest handle incoming HTTP request streams.
    14  // It can use for architectures like restful, ...
    15  // Protocol Standard - HTTP/1 : https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
    16  // Protocol Standard - HTTP/2 : https://httpwg.org/specs/rfc7540.html
    17  // Protocol Standard - HTTP/3 : https://quicwg.org/base-drafts/draft-ietf-quic-http.html
    18  func (handler *Handler) HandleIncomeRequest(stream protocol.Stream) (err protocol.Error) {
    19  	var httpReq Request
    20  	var httpRes Response
    21  	httpReq.Init()
    22  	httpRes.Init()
    23  	var streamSocket = stream.Socket()
    24  
    25  	var maybeBody []byte
    26  	maybeBody, err = httpReq.UnmarshalFrom(streamSocket.Marshal())
    27  	if err != nil {
    28  		httpRes.SetStatus(StatusBadRequestCode, StatusBadRequestPhrase)
    29  		handler.HandleOutcomeResponse(stream, &httpReq, &httpRes)
    30  		return
    31  	}
    32  	httpReq.body.checkAndSetCodecAsIncomeBody(maybeBody, streamSocket, &httpReq.H)
    33  
    34  	err = handler.ServeHTTP(stream, &httpReq, &httpRes)
    35  	return
    36  }
    37  
    38  // ServeHTTP handle incoming HTTP request.
    39  // Developers can develope new one with its desire logic to more respect protocols like restful, ...
    40  func (handler *Handler) ServeHTTP(stream protocol.Stream, httpReq *Request, httpRes *Response) (err protocol.Error) {
    41  	if !protocol.AppMode_Dev && !HostSupportedService.ServeHTTP(stream, httpReq, httpRes) {
    42  		handler.HandleOutcomeResponse(stream, httpReq, httpRes)
    43  		return
    44  	}
    45  
    46  	switch httpReq.uri.path {
    47  	case serviceMuxPath:
    48  		err = MuxService.ServeHTTP(stream, httpReq, httpRes)
    49  	case landingPath:
    50  		// TODO:::
    51  	default:
    52  		// serve by URL
    53  		var service protocol.Service
    54  		service, err = protocol.App.GetServiceByURI(httpReq.uri.path)
    55  		if service == nil {
    56  			// If project don't have any logic that support data on e.g. HTTP (restful, ...) we send platform GUI app for web
    57  			err = ServeWWWService.ServeHTTP(stream, httpReq, httpRes)
    58  		} else {
    59  			err = service.ServeHTTP(stream, httpReq, httpRes)
    60  		}
    61  	}
    62  	handler.HandleOutcomeResponse(stream, httpReq, httpRes)
    63  	return
    64  }
    65  
    66  // HandleOutcomeResponse use to handle outcoming HTTP response stream!
    67  func (handler *Handler) HandleOutcomeResponse(stream protocol.Stream, httpReq *Request, httpRes *Response) {
    68  	// Do some global assignment to response
    69  	httpRes.version = httpReq.version
    70  	if httpRes.Body() != nil {
    71  		var mediaType = httpRes.body.MediaType()
    72  		if mediaType != nil {
    73  			httpRes.H.Set(HeaderKeyContentType, mediaType.MediaType())
    74  		}
    75  		var compressType = httpRes.body.CompressType()
    76  		if compressType != nil {
    77  			httpRes.H.Set(HeaderKeyContentEncoding, compressType.ContentEncoding())
    78  		}
    79  
    80  		if httpRes.body.Len() > 0 {
    81  			httpRes.SetContentLength()
    82  		} else {
    83  			httpRes.H.SetTransferEncoding(HeaderValueChunked)
    84  		}
    85  	} else {
    86  		httpRes.H.SetZeroContentLength()
    87  	}
    88  
    89  	// httpRes.H.Set(HeaderKeyAccessControlAllowOrigin, "*")
    90  
    91  	stream.SendResponse(httpRes)
    92  
    93  	if protocol.LogMode_DeepDebug {
    94  		// TODO::: req||res not serialized yet to log it!! any idea to have better performance below??
    95  		protocol.App.Log(log.ConfEvent(domainEnglish, convert.UnsafeByteSliceToString(httpReq.Marshal())))
    96  		protocol.App.Log(log.ConfEvent(domainEnglish, convert.UnsafeByteSliceToString(httpRes.Marshal())))
    97  	}
    98  }
    99  
   100  // SendBidirectionalRequest use to handle outcoming HTTP request stream
   101  // It block caller until get response or error
   102  func SendBidirectionalRequest(conn protocol.Connection, service protocol.Service, httpReq *Request) (httpRes *Response, err protocol.Error) {
   103  	var stream protocol.Stream
   104  	stream, err = conn.OutcomeStream(service)
   105  	if err != nil {
   106  		return
   107  	}
   108  
   109  	err = stream.SendRequest(httpReq)
   110  	if err != nil {
   111  		return
   112  	}
   113  
   114  	for {
   115  		var status = <-stream.State()
   116  		switch status {
   117  		case protocol.ConnectionState_Timeout:
   118  			// err =
   119  		case protocol.ConnectionState_ReceivedCompletely:
   120  			var res Response
   121  			res.Init()
   122  			err = res.Unmarshal(stream.Socket().Marshal())
   123  			if err == nil {
   124  				err = res.GetError()
   125  			}
   126  			httpRes = &res
   127  		default:
   128  			continue
   129  		}
   130  		stream.Close()
   131  		break
   132  	}
   133  	return
   134  }
   135  
   136  // SendUnidirectionalRequest use to send outcoming HTTP request and don't expect any response.
   137  // It block caller until request send successfully or return error
   138  func SendUnidirectionalRequest(conn protocol.Connection, service protocol.Service, httpReq *Request) (err protocol.Error) {
   139  	var stream protocol.Stream
   140  	stream, err = conn.OutcomeStream(service)
   141  	if err != nil {
   142  		return
   143  	}
   144  
   145  	err = stream.SendRequest(httpReq)
   146  	if err != nil {
   147  		return
   148  	}
   149  
   150  	for {
   151  		var status = <-stream.State()
   152  		switch status {
   153  		case protocol.ConnectionState_Timeout:
   154  			// err =
   155  		case protocol.ConnectionState_SentCompletely:
   156  			// Nothing to do. Just let execution go to stream.Close() and break the loop
   157  		default:
   158  			continue
   159  		}
   160  		stream.Close()
   161  		break
   162  	}
   163  	return
   164  }