otarza / serv-cst

CST web III porject
6 stars 0 forks source link

HTTP თავსართების წაკითხვა #12

Closed ioseb closed 11 years ago

ioseb commented 11 years ago

იხ. თავსართის სტრუქტურისა და წაკითხვის წესების სპეციფიკაცია: http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-22#section-3.2.4

ioseb commented 11 years ago

@reflooding მოსაწვევს ელოდები?

otarza commented 11 years ago

@ioseb

წავიკითხე სპეციფიკაცია და როგორც მივხვდი უნდა დავწერო თითოეული Field-ის პარსესრი ხომ?

თუ ასეა მაინტერესებს უნდა გამოვიყენო თუ არა ვახოს მიერ შედგენილი კლასი რომელიც მოიცავს ველების დასახელებების კონსტანტებს. HttpHeader.java

თუ კი მაშინ უნდა გავარკვიო რაიმე მექანიზმი არსებობს თუ არა კლასების ველებში გავლის და იმის დადგენს თუ არსებობს ჩვენ ჩამონათვალში ჩემი პარსერისთვის გადმოცემულ სტრინგში შემავალი ველი თუ როგორ ვქნა?

ნუ სპეციფიკაციის ამ პუნქტში გავერკვიე და რას უნდა აკმაყოფილებდეს ვიცი.

ioseb commented 11 years ago

@reflooding

არა, @vaxop - ს კლასი არაფერში არ გჭირდება. ეგ მხოლოდ შიდა მოხმარებისთვის არის ანუ response ჰიდერების ასაწყობად ძირითადად და მაგალითად უკვე გაპარსული თავსართების მაპში შესამოწმებლად არის თუ არა ესა თუ ის თავსართი წაკითხული. მანდ მთავარია რომ key: value წყვილები გამოარჩიო და Map<String, String> მიიღო.

ყველა სხვა ისეთი თავსართი რომელიც არ არის სპეციფიკაციით განსაზღვრული ჩვეულებრივად უნდა წაიკითხო. HTTP არ ზღუდავს მოთხოვნისა და პასუხის გზავნილში არარეგისტრირებული თავსართების გამოყენებას.

ალგორითმს მე ასე ვხედავ:

გასაგებია?

ioseb commented 11 years ago

@reflooding

სპეციფიკაციის მიხედვით თავსართის სახელში შემდეგი ერთი ან ნებისმიერი რაოდენობის სიმბოლოა დასაშვები:

token = 1*tchar

სადაც token თავის მხრივ შედგება შემდეგი სიმბოლოთა ერთობლიობისგან:

ALPHA = A-Z / a-z
DIGIT = 0-9
tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /"^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
otarza commented 11 years ago

@ioseb დავაკომიტე ამ დროისთვის რა შედეგიც მაქვს, რეგულარული გამოსახულება და USASCII დამრჩა.

ioseb commented 11 years ago

@reflooding დასაწყისისთვის კარგია. მთავარია ინტერფეისი გვქონდეს კოდის სხვა ნაწილებმა რომ შეძლონ გამოყენება. თუმცა უნდა ვთქვათ რომ შორს არის იდეალურისგან და გაცილებით მეთი უნიტ ტესტებია დასამატებელი ეს ძალიან ზედაპირულია.

ioseb commented 11 years ago

@reflooding ასეთ რამეს გთავაზობ. რამდენადაც თავსართის სახელი შედგება სასრული რაოდენობის სიმბოლოებისგან და ეტევა ASCII კოდირებაში შესაძლებელია მარტივი ლუკაპის გაკეთება მასივიდან. შეხედე ამ კოდს: https://github.com/ioseb/uri-template/blob/master/uri_template_string.c

ესე იგი დასაშვები სიმბოლოები მაქვს განთავსებული 128 ელემენტეიან char[] მასივში. შესაბამისად როდესაც მაინტერესებს სიმბოლო დასაშვებია თუ არა ვნახულობ მაგ მასივში. ალგორითმი ასეთია:

რას ფიქრობ?

P.S. ნაჩვენებ ბმულზე რა სიმბოლოთა ცხრილებიც არის ეგ არ დააკოპირო, სხვა შემადგენლობაა მანდ.

otarza commented 11 years ago

@ioseb გასაგებია, საღამოს დავიწყებ იმპლემენტაციას.

აქ ვნახე ცხრილი და ამას გამოვიყენებ http://www.asciitable.pro/ascii_table.htm

ioseb commented 11 years ago

@reflooding

კომენტარში დაგიწერე და აქ დავამატებ. უნიტ ტესტი ასე უნდა დაწერო გამონაკლისი შემთხვევისთვის:

@Test(expected = HttpException.class)
public void testMalformedHttpHeaderFieldParsingThrowsHttpException() throws HttpException {
    HeaderParser.parse("აქ ცუდი ჰიდერი");
}

ანუ ამ შემთხვევაში რა ხდება:

შესაბამისად თუ შენ აიძულებ პარსერს რომ შეცდომა ისროლოს, უნიტ ტესტი დაიჭერს მაგ შეცდომას და თუ "გამწვანდა" ესე იგი: ა) შეცდომა მოხდა; ბ) შეცდომა დაიჭირა უნიტ ტესტის გარემომ;

წინააღმდეგ შემთხვევაში გაწითლდება.

ioseb commented 11 years ago

@reflooding რა ხდება?

ioseb commented 11 years ago

@reflooding დამატებითი შეხსენება. ცოტა არ იყოს ვფერხდებით.

otarza commented 11 years ago

ამაღამ განვაახლებ მუშაობას :8ball:

ioseb commented 11 years ago

მადლობა ღმერთს თორემ უკვე მეშინოდა გადაიფიქრათქო :+1:

ioseb commented 11 years ago

@reflooding იმედია საკმარისი ინფორმაცია მიიღე დღეს და დიდი ხანი აღარ გაწელავ.

otarza commented 11 years ago

@ioseb კი კი, ხაზზე ვარ და ვაკეთებ :)

ioseb commented 11 years ago

@reflooding ჯერ ონლაინ ვარ და თუ რამეა არ მოგერიდოს შემეხმიანე.

otarza commented 11 years ago

არ მომერიდება :) თუმცა მგონი რიგზეა ჯერ-ჯერობით ყველაფერი

otarza commented 11 years ago

word = token / quoted-string

 token          = 1*tchar

 tchar          = "!" / "#" / "$" / "%" / "&" / "'" / "*"
                / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
                / DIGIT / ALPHA
                ; any VCHAR, except special

 special        = "(" / ")" / "<" / ">" / "@" / ","
                / ";" / ":" / "\" / DQUOTE / "/" / "["

ესენი ხომ ყველა დასაშვებია? სპეციფიკაციაში ეგრე წერია და რავი

ioseb commented 11 years ago

value - ზე კი, დასაშვებია.

otarza commented 11 years ago

value-ზე დასაშვები რას ნიშნავს? ანუ ეს special ჯგუფის ელემენტები უნდა შევიტანო ჩემ მასივში თუ არა?

otarza commented 11 years ago

იმპლემენტაცია გავაკეთე უბრალოდ მასვიში ელემენტების სისწორეში არ ვარ დარწმუნებული ყველა სიმბოლო დასაშვებია თუ არა რაც მაქვს. სპეციფიკაციაში აღნიშნული Special სიმბოლოები უნდა მქონდეს თუ არა ვერ მივხვდი. ზედა კომენტარში დავაკოპირე ის სიმბოლოები რომლებიც სპეციფიკაციაში ვნახე და ეგენი უნდა მქონდეს ყველა თუ არა?

ამაში რომ დავრწმუნდები ტესტებს კიდევ მივამატებ.

ioseb commented 11 years ago

არ არის მიღებული. გადახედე კომენტარებს და დაწერე წესიერად.

otarza commented 11 years ago

@ioseb "Accept-Language:en-us,en;q=0.5" ასეთი სტრინგი ხომ შეიძლება მომივიდეს? ხოდა აქ წერტილმძიმე შედის და წერტილ მძიმე Special სიმბოლოების კატეგორიაში გადის და ვეღარ მივხვდი რა შეიძლება და რა არა.

ioseb commented 11 years ago

@reflooding მნიშვნელობა(ანუ value) ამოიღე(ანუ წაიკითხე) მთლიანად ჯერ სპეკით მერე დავამუშავებთ. თავსართი შედგება ორი ნაწილისგან:

  1. key: ანუ Accept-Language ამ შემთხვევაში;
  2. value: ანუ en-us,en;q=0.5 ამ შემთხვევაში.

key - ს თავის სპეციფიკაცია აქვს დასაშვები სიმბოლოების მხრივ და value - ს თავისი. ეს ორი ერთი და იგივე არ არის.

otarza commented 11 years ago

@ioseb აჰა და ჩემმა პარსერმა როგორც მივხვდი key-ც უნდა გაპარსოს და value-ც შესაბამისად key-თვის მექნება ცალკე lookup მასივი და value-თვის ცალკე.

სწორად გავიგე?

ioseb commented 11 years ago

@reflooding მესამედ მომყავს ეს მაგალითი: https://github.com/ioseb/uri-template/blob/master/uri_template_string.c თუ ნახავ რამდენიმე ცხრილი მაქვს სხვადასხვა დანიშნულებისთვის. შესაბამისად შეგიძლია თამამად მოიქცე ანალოგიურად. თუ ეჭვი გეპარება JDK - ს სორსებიდან შემიძლია მსგავსი მაგალითები მოგიყვანო :-)

გააკეთე ორი ცვლადი: allowedHeaderFieldKeyChars და allowedHeaderFieldValueChars

otarza commented 11 years ago

@ioseb ეჭვი არ მეპარება ვცდილობ სწორად გავიგო დავალება :)

ioseb commented 11 years ago

@reflooding შენი პოსტიდან:

 word           = token / quoted-string

     token          = 1*tchar

     tchar          = "!" / "#" / "$" / "%" / "&" / "'" / "*"
                    / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
                    / DIGIT / ALPHA
                    ; any VCHAR, except special

     special        = "(" / ")" / "<" / ">" / "@" / ","
                    / ";" / ":" / "\" / DQUOTE / "/" / "["

აქ საკვანძოა კარგად გაერკვე რომელი რა დანიშნულებას ატარებს. value გაცილებით რთული "გასაპარსია" და ამიტომ პირველ ეტაპზე მხოლოდ ის დაადგინე სიმბოლოები დასაშვებია თუ არა. დანარჩენს მერე "დავანავაროტებთ"

ioseb commented 11 years ago

@reflooding ძალიან ხომ არ აბუქსავებ? დრო მიდის...

otarza commented 11 years ago

ჰო ვიცი, ამ საღამოს ჯავას ლექციაზწ მაინც ტყუილა უნდა ვიჯდე და დავწერ. დროში ვარ მაგრად გაჭედილი.

ioseb commented 11 years ago

@reflooding რა ხდება აბა?

otarza commented 11 years ago

სადღაც დამიწერე რომ კლასს სახელი გადაარქვიო მაგრამ რა სახელი დავარქვა არ ვიცი :) რა ხდება და ვაგრძელებთ :)

ioseb commented 11 years ago

@reflooding HttpHeaderFieldParser

otarza commented 11 years ago

აუ რა მჭირს ეხლა იმ ტესტს ვეღარ ვპოულობ შენ რომ დამიწერე გადააკოპირეო....

ioseb commented 11 years ago

https://github.com/reflooding/serv-cst/issues/22

ioseb commented 11 years ago

@reflooding დაუშვებელი ფორმატის ტესტების იშჲუ დავხურე. ამას არ დავხურავ. ალგორითმი არ მომწონს და უხეშად გიწერია.

ioseb commented 11 years ago

@reflooding

ასეთ მოდიფიკაციაზე რას იტყვი?

package edu.cst.webserver.http;

import java.util.HashMap;
import java.util.Map;

/**
 * @author Otar
 */
public class HttpHeaderFieldParser {
    private static class HttpHeaderFieldParserException extends Exception {}

    //static US ASCII character lookup array
    private static char allowedHeaderFieldKeyChars[] = {
            0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,
            0,    '!',  '"',  '#',  '$',  '%',  '&',  '\'',
            0,    0,    '*',  '+',  0,    '-',  '.',  0,
            '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
            '8', '9',   0,    0,    0,    0,    0,    0,
            0,   'A',   'B',  'C',  'D',  'E',  'F',  'G',
            'H', 'I',   'J',  'K',  'L',  'M',  'N',  'O',
            'P', 'Q',   'R',  'S',  'T',  'U',  'V',  'W',
            'X', 'Y',   'Z',  0,     0,   0,    0,    '_',
            '`', 'a',   'b',  'c',   'd', 'e',  'f',  'g',
            'h', 'i',   'j',  'k',   'l', 'm',  'n',  'o',
            'p', 'q',   'r',  's',   't', 'u',  'v',  'w',
            'x', 'y',   'z',  0,     '|', 0,     '~'
    };

    /**
     * HTTP headerField parser
     * Parses headerField string and if its valid returns key value map, If not throws  HttpRequestException
     */
    public static Map<String, String> parse(String headerField) throws HttpRequestException {
        try {
            String tokens[] = getTokens(headerField);
            Map<String, String> headers = new HashMap<String, String>();

            String key = tokens[0];
            String value = tokens[1];
            headers.put(key, value);

            return headers;
        } catch (HttpHeaderFieldParserException e) {
            throw new HttpRequestException(HttpStatus.Code.BAD_REQUEST);
        }
    }

    private static boolean hasValidCharacters(String header) {
        for (int i = 0; i < header.length(); i++) {
            int code = header.charAt(i);

            if (code >= 0 && code < 128) {
                if (allowedHeaderFieldKeyChars[code] == 0) {
                    return false;
                }
            } else {
                return false;
            }
        }

        return true;
    }

    private static String[] getTokens(String headerField) throws HttpHeaderFieldParserException {
        throwIfEmpty(headerField);
        String tokens[] = headerField.trim().split(":\\s*", 2);
        throwIfInvalidTokens(tokens);
        return tokens;
    }

    private static void throwIfEmpty(String value) throws HttpHeaderFieldParserException {
        if (value == null || value.isEmpty()) {
            throw new HttpHeaderFieldParserException();
        }
    }

    private static void throwIfInvalidTokens(String[] tokens) throws HttpHeaderFieldParserException {
        if (tokens.length != 2) {
            throw new HttpHeaderFieldParserException();
        }

        throwIfEmpty(tokens[0]);
        throwIfEmpty(tokens[1]);
        throwIfInvalidHeaderFieldName(tokens[0]);
    }

    private static void throwIfInvalidHeaderFieldName(String headerFieldName) throws HttpHeaderFieldParserException {
        if (!hasValidCharacters(headerFieldName)) {
            throw new HttpHeaderFieldParserException();
        }
    }
}

თუ მოგეწონება პირდაპირ ეს გამოიყენე, დააკომიტე და დახურე თიქეთი.

otarza commented 11 years ago

@ioseb ჩემი იმპლემენტაციის ოპტიმიზაცია გავაკეთე ცალკე ფუნქციებში გატანა მემძიმასავით :) გადახედე მაინც და მერე დავხუროთ.

otarza commented 11 years ago

@ioseb ასეთ იდეა მაქვს, რო გავსპლიტო სანამ ვალიდატორს გადავცემ მანამდე და შემდეგ ვალიდატორ ფუნქციას გადავცე ცალ-ცალკე პარამეტრებად და ცალ-ცალკე გავატარო ვალიდაცია და თუ გაიარა ორივემ მხოლოდ მაგ შემთხვევაში დავაბრუნო true.

ივარგებს ეს მიდგომა?