xref: /unit/go/request.go (revision 1713:f5ba5973a0a3)
1/*
2 * Copyright (C) Max Romanov
3 * Copyright (C) NGINX, Inc.
4 */
5
6package unit
7
8/*
9#include "nxt_cgo_lib.h"
10*/
11import "C"
12
13import (
14	"io"
15	"net/http"
16	"net/url"
17	"crypto/tls"
18	"unsafe"
19)
20
21type request struct {
22	req    http.Request
23	resp   response
24	c_req  *C.nxt_unit_request_info_t
25}
26
27func (r *request) Read(p []byte) (n int, err error) {
28	res := C.nxt_cgo_request_read(r.c_req, buf_ref(p), C.uint32_t(len(p)))
29
30	if res == 0 && len(p) > 0 {
31		return 0, io.EOF
32	}
33
34	return int(res), nil
35}
36
37func (r *request) Close() error {
38	return nil
39}
40
41func new_request(c_req *C.nxt_unit_request_info_t) (r *request, err error) {
42	req := c_req.request
43
44	uri := GoStringN(&req.target, C.int(req.target_length))
45
46	URL, err := url.ParseRequestURI(uri)
47	if err != nil {
48		return nil, err
49	}
50
51	proto := GoStringN(&req.version, C.int(req.version_length))
52
53	r = &request{
54		req: http.Request {
55			URL: URL,
56			Header: http.Header{},
57			RequestURI: uri,
58			Method: GoStringN(&req.method, C.int(req.method_length)),
59			Proto: proto,
60			ProtoMajor: 1,
61			ProtoMinor: int(proto[7] - '0'),
62			ContentLength: int64(req.content_length),
63			Host: GoStringN(&req.server_name, C.int(req.server_name_length)),
64			RemoteAddr: GoStringN(&req.remote, C.int(req.remote_length)),
65		},
66		resp: response{header: http.Header{}, c_req: c_req},
67		c_req: c_req,
68	}
69
70	r.req.Body = r
71
72	if req.tls != 0 {
73		r.req.TLS = &tls.ConnectionState{ }
74		r.req.URL.Scheme = "https"
75
76	} else {
77		r.req.URL.Scheme = "http"
78	}
79
80	fields := get_fields(req)
81
82	for i := 0; i < len(fields); i++ {
83		f := &fields[i]
84
85		n := GoStringN(&f.name, C.int(f.name_length))
86		v := GoStringN(&f.value, C.int(f.value_length))
87
88		r.req.Header.Add(n, v)
89	}
90
91	return r, nil
92}
93
94func get_fields(req *C.nxt_unit_request_t) []C.nxt_unit_field_t {
95	f := uintptr(unsafe.Pointer(req)) + uintptr(C.NXT_FIELDS_OFFSET)
96
97	h := &slice_header{
98		Data: unsafe.Pointer(f),
99		Len: int(req.fields_count),
100		Cap: int(req.fields_count),
101	}
102
103	return *(*[]C.nxt_unit_field_t)(unsafe.Pointer(h))
104}
105
106//export nxt_go_request_handler
107func nxt_go_request_handler(c_req *C.nxt_unit_request_info_t) {
108
109	go func(c_req *C.nxt_unit_request_info_t, handler http.Handler) {
110
111		ctx := c_req.ctx
112
113		for {
114			r, err := new_request(c_req)
115
116			if err == nil {
117				handler.ServeHTTP(&r.resp, &r.req)
118
119				if !r.resp.header_sent {
120					r.resp.WriteHeader(http.StatusOK)
121				}
122
123				C.nxt_unit_request_done(c_req, C.NXT_UNIT_OK)
124
125			} else {
126				C.nxt_unit_request_done(c_req, C.NXT_UNIT_ERROR)
127			}
128
129			c_req = C.nxt_unit_dequeue_request(ctx)
130			if c_req == nil {
131				break
132			}
133		}
134
135	}(c_req, get_handler(uintptr(c_req.unit.data)))
136}
137